AJAX详解

目录

AJAX(Asynchronous Javascript And Xml)

传统请求及缺点

AJAX概述

绑定事件

XMLHttpRequest对象

AJAX GET请求

AJAX GET请求的缓存问题

AJAX POST请求

JSON对象

基于JSON的数据交互

基于XML的数据交换

AJAX乱码问题

AJAX的异步与同步

AJAX代码封装

AJAX实现省市联动

AJAX跨域问题

跨域

同源策略

AJAX跨域解决方案

AJAX实现搜索联想 自动补全

附录:HTTP状态信息

1xx: 信息

2xx: 成功

3xx: 重定向

4xx: 客户端错误

5xx: 服务器错误


源码放评论区了,需要的自提。

AJAX(Asynchronous Javascript And Xml)

传统请求及缺点

  • 传统请求有哪些?

    • 直接在浏览器地址栏输入URL。

    • 点击超链接

    • 提交form表单

    • 使用JavaScript代码发送请求

      • window.open(url)

      • document.location.href = url

      • window.location.href = url

      • ....




    
    演示传统请求,以及传统请求的缺点



传统请求
   
  • 传统请求存在的问题

    • 页面全部刷新导致了用户的体验差

    • 传统的请求导致用户的体验有空白期。

AJAX详解_第1张图片

 

AJAX概述

  • AJAX不能称为一种技术,它是多种技术的综合产物

  • AJAX可以让浏览器发送一种特殊的请求,这种请求时异步的

  • 什么是同步,什么是异步?

    • 假设有t1和t2线程,t1和t2线程并发,就是异步

    • 假设有t1和t2线程,t2在执行的时候,必须等待t1线程执行到某个位置之后才能执行。t2在等t1,显然他们是排队的,排队的就是同步

    • AJAX是可以发送异步请求的,也就是说,在同一个浏览器页面中,可以发送多个AJAX请求,这些请求之间不需要等待,是并发的

  • AJAX代码属于web前端的JS代码,和后端的JAVA没有关系,后端也可以是php语言,也可以是C语言

  • AJAX应用程序可以使用XML来传输数据,也可以使用纯文本或JSON来传输数据

  • AJAX可以做到在同一个网页中同时启动多个请求,类似于在同一个网页中启动“多线程”,一个“线程”一个“请求”

  • AJAX详解_第2张图片

AJAX详解_第3张图片 

绑定事件

学习AJAX的前提。



​

    
    

​

    
​
    
​

​

XMLHttpRequest对象

  • XMLHttpRequest对象是AJAX的核心对象,它用来发送请求以及接收服务器数据的返回。

  • XMLHttpRequest对象,现代浏览器都是支持的,都内置了该对象,直接用即可。

  • 创建XMLHttpRequest对象:var xhr=new XMLHttpRequest();

  • XMLHttpRequest对象的方法

    方法 描述
    open(method,url,async,user,pwd) 规定请求method:请求类型GET或POST 。url:文件位置。 async:true(异步),false(同步)。 user:可选的用户名。 pwd:可选的密码。
    send() 将请求发送到服务器,用于GET请求
    abort() 取消当前请求
    getAllResponseHeaders() 返回头部信息
    getResponseHeader() 返回特定的头部信息
    send(string) 将请求发送到服务器,用于POST请求
    setRequestHeader() 向要发送的报头添加标签/值对
  • XMLHttpRequest对象的属性

属性 描述
readyState 保存XMLHttpRequest的状态。0:请求未初始化 1:服务器连接已建立 2:请求已收到 3:正在处理请求 4:请求已完成且响应已就绪
onreadystatechange 定义当readyState属性发生改变的时候调用函数
responseText 以字符串返回响应数据
responseXML 以XML数据返回响应数据
status 返回请求的状态码(200、404、500)
statusText 返回状态文本(OK、Not Found)

AJAX GET请求

  • 发送AJAX get请求,前端代码

    
    
    
        
        ajax get请求
    
    
    
    
    
    
    

  • 发送AJAX get请求的后端代码:

package servlet;
​
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
​
import java.io.IOException;
import java.io.PrintWriter;
​
@WebServlet("/ajaxrequest1")
public class AjaxRequest1Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        PrintWriter out = response.getWriter();
​
        //out对象向浏览器输出信息
        //out在响应的时候,浏览器客户端的XMLHttpRequest对象会接收到这个响应的信息
        out.print("welcome!!!");
    }
}
​
  • 发送AJAX get请求的前端代码的另一种写法




    
    Title




  • AJAX get请求如何提交数据呢?

    • get请求提交数据是在”请求行“上提交,格式是:url?name=value&name=value&name=value

    • 其实这个get请求提交数据的格式是HTTP协议中规定的,遵循协议即可。

AJAX GET请求的缓存问题

  • 对于低版本的IE浏览器来说,AJAX的get请求可能会走缓存,存在缓存问题。对于现代的浏览器来说,大部分浏览器已经不存在AJAX的get缓存问题了。

  • 什么是AJAX get请求缓存问题?

    • 在HTTP协议中,规定get请求会被缓存起来。

    • 发送AJAX get请求时,在同一个浏览器上,前后发送的AJAX请求路径一样的话,对于低版本的IE来说,第二次的AJAX get请求会走缓存,不走服务器。

  • POST请求在HTTP协议中的规定:POST请求不会被浏览器缓存。

  • GET请求的优缺点:

    • 优点:直接从浏览器缓存中获取资源,不需要从服务器上重新加载资源,速度较快,用户体验好。

    • 缺点:无法实时获取最新的服务器资源。

  • 浏览器什么时候会走缓存?

    • GET请求

    • 请求路径已经被浏览器缓存过,第二次发送请求的时候,这个路径没有变化,会走缓存。

  • 如果是低版本的IE浏览器,怎么解决AJAX get请求的缓存问题?

    • 可以在请求路径url后面添加时间戳,这个时间戳是随时变化的。所以每一次发送请求的路径都是不一样的,就不会走浏览器的缓存。

    • 时间戳:"url?t="+new Date().getTime()

    • 随机数:"url?t="+Math.random()

    • 随机数+事件戳

AJAX POST请求

  • AJAX POST请求和GET请求的区别。

    //4.发送请求
                //设置请求头的内容类型(模拟form表单提交的关键代码),必须在open之后
                xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
                //放到send()这个函数的小括号当中的数据,会自动在请求体当中提交数据
                var username = document.getElementById("username").value;
                var password = document.getElementById("password").value;
                //send中的数据就是在请求体当中提交的数据,需要遵循HTTP协议:name=value&name=value...
                xhr.send("username=" + username + "&password=" + password);
  • 实现一个案例:使用AJAX POST请求实现用户注册时用户名是否可用。

    • 前端:输入用户名的时候,失去焦点事件blur发生,然后发送AJAX POST请求,提交用户名

    • 后端:接收用户名,连接数据库,根据用户名去表中搜索

    • 如果用户名已存在,后端响应消息:用户名已存在

    • 如果用户名不存在,后端响应消息:用户名可以使用

    • 前端代码:

      
      
      
          
          AJAX POST请求验证用户名是否可用
      ​
      用户名:

    • 后端代码:

      package servlet;
      ​
      import jakarta.servlet.ServletException;
      import jakarta.servlet.annotation.WebServlet;
      import jakarta.servlet.http.HttpServlet;
      import jakarta.servlet.http.HttpServletRequest;
      import jakarta.servlet.http.HttpServletResponse;
      ​
      import java.io.IOException;
      import java.io.PrintWriter;
      import java.sql.*;
      ​
      ​
      @WebServlet("/ur")
      public class userRegisterServlet extends HttpServlet {
          @Override
          protected void doPost(HttpServletRequest request, HttpServletResponse response)
                  throws ServletException, IOException {
              // 获取用户名
              String username = request.getParameter("username");
              // 打布尔标记(一种编程模型)
              boolean flag = false; // 默认是用户名不存在。
              // 连接数据库验证用户名是否存在
              Connection conn = null;
              PreparedStatement ps = null;
              ResultSet rs = null;
              try {
                  // 1.注册驱动
                  Class.forName("com.mysql.cj.jdbc.Driver");
                  // 2.获取连接
                  conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ajax?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC", "root", "030522");
                  // 3.获取预编译的数据库操作对象
                  String sql = "select id,name from t_user where name = ?";
                  ps = conn.prepareStatement(sql);
                  ps.setString(1, username);
                  // 4.执行SQL语句
                  rs = ps.executeQuery();
                  // 5.处理结果集
                  if (rs.next()) {
                      // 用户名已存在。
                      flag = true;
                  }
      ​
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  // 6.释放资源
                  if (rs != null) {
                      try {
                          rs.close();
                      } catch (SQLException e) {
                          e.printStackTrace();
                      }
                  }
                  if (ps != null) {
                      try {
                          ps.close();
                      } catch (SQLException e) {
                          e.printStackTrace();
                      }
                  }
                  if (conn != null) {
                      try {
                          conn.close();
                      } catch (SQLException e) {
                          e.printStackTrace();
                      }
                  }
              }
      ​
              // 响应结果到浏览器
              response.setContentType("text/html;charset=UTF-8");
              PrintWriter out = response.getWriter();
              if (flag) {
                  // 用户名已存在,不可用
                  out.print("对不起,用户名已存在");
              }else{
                  // 用户名不存在,可以使用
                  out.print("用户名可以使用");
              }
          }
      }

JSON对象

在JavaScript语言中怎样创建一个JSON对象?语法是什么?

var jsonobj = {
"属性名1" : 属性值,
"属性名2" : 属性值,
"属性名3" : 属性值,
"属性名4" : 属性值,
}

注意:属性值可以是数字、布尔类型、字符串、数组、json对象......




    
    Title




AJAX详解_第4张图片

 

基于JSON的数据交互

  • 从后端java程序种响应回来的是json格式的字符串,如何将json格式的字符串转换成json对象?

    //从服务器端返回来的不是一个json对象,是一个json格式的字符串
        var fromJavaServerJsonStr = "{\"usercode\":111,\"age\":20}";
        //将json格式的字符串转换为json对象
        //第一种方式:使用eval函数
        //第二种方式:调用js语言种的内置对象JSON的一个方法parse
        var jsonobj = JSON.parse(fromJavaServerJsonStr);
        alert(jsonobj.usercode + "," + jsonobj.age);
  • 动态展示学员列表前端代码

    
    
    
        
        Title
    
    
    
    
    序号 姓名

  • 动态展示学员列表后端代码

    package servlet;
    
    import bean.Student;
    import com.alibaba.fastjson.JSON;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.annotation.WebServlet;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.sql.*;
    import java.util.ArrayList;
    
    @WebServlet("/showList")
    public class showListServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            response.setContentType("text/html;charset=utf-8");
            PrintWriter out = response.getWriter();
    
            /*//拼接HTML代码
            StringBuilder html = new StringBuilder();
            //目前存在的缺点,后端的java代码中拼接HTML代码,难以维护
            //如何将数据直接返回给web前端,将页面展示的功能交给前端处理,后端的java代码只返回数据。
            //返回数据采用JSON的格式,也可以采用XML格式
            html.append("");
            html.append("1");
            html.append("张三");
            html.append("");
            html.append("");
            html.append("2");
            html.append("李四");
            html.append("");
    
            out.print(html);*/
    
            //拼接JSON格式的字符串
            //String jsonStr="[{\"name\":\"张三\"},{\"name\":\"李四\"}]";
    
            //准备StringBuilder对象,拼接JSON
            StringBuilder json = new StringBuilder();
            String jsonStr = "";
    
            //连接数据库,查询所有的学生,拼接JSON字符串
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            String driver = "com.mysql.cj.jdbc.Driver";
            String url = "jdbc:mysql://localhost:3306/ajax";
            String user = "root";
            String password = "030522";
            try {
                Class.forName(driver);
                conn = DriverManager.getConnection(url, user, password);
                String sql = "select name from t_student";
                ps = conn.prepareStatement(sql);
                rs = ps.executeQuery();
                /*//开始拼接
                json.append("[");
                while (rs.next()) {
                    String name = rs.getString("name");
                    //拼接{"name":"   张三   "},
                    json.append("{\"name\":\"");
                    json.append(name);
                    json.append("\"},");
                }
                jsonStr = json.substring(0, json.length() - 1) + "]";*/
    
                ArrayList list = new ArrayList<>();
                while (rs.next()) {
                    String name = rs.getString("name");
                    //将以上数据封装成Student对象
                    Student student = new Student(name);
                    //将Student对象放到list集合
                    list.add(student);
                }
                //将list集合转换成json字符串
                jsonStr = JSON.toJSONString(list);
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    rs.close();
                    ps.close();
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
    
            }
    
            //响应JSON格式的字符串给前端
            out.print(jsonStr);
    
        }
    }
  • 注:使用fastjson组件可以将java对象转换成json对象,需要引入jar包。

基于XML的数据交换

  • 如果需要服务器端响应XML,响应的内容类型需要写成:

    response.setContentType("text/xml;charset=UTF-8");
  • XML和JSON都是常用的数据交换格式

    • XML体积大,解析麻烦,较少用。

    • JSON体积小,解析简单,较常用。

  • 展示学员列表,前端代码

    
    
    
        
        Title
    
    
    
    
    
    序号 姓名 年龄

  • 展示学员列表,后端代码

    package servlet;
    
    import jakarta.servlet.ServletException;
    import jakarta.servlet.annotation.WebServlet;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    @WebServlet("/showList2")
    public class xmlShowListServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            //注意:响应的内容类型是XML
            response.setContentType("text/xml;charset=utf-8");
            PrintWriter out = response.getWriter();
    
            StringBuilder xml = new StringBuilder();
            xml.append("");
            xml.append("    ");
            xml.append("        zhangsan");
            xml.append("        20");
            xml.append("    ");
            xml.append("    ");
            xml.append("        lisi");
            xml.append("        22");
            xml.append("    ");
            xml.append("");
    
            out.print(xml);
        }
    }

AJAX乱码问题

  • 对于tomcat10,不会出现乱码

  • 对于tomcat9

    • 响应中文的时候,会出现乱码,如何解决?

      response.setContentType("text/html;charset=utf-8");
    • 发送post请求时,发送给服务器的数据,服务器接收之后会乱码,如何解决?

      request.setCharacterEncoding("utf-8");

AJAX的异步与同步

  • 什么是异步?什么是同步?

    • ajax请求1和ajax请求2,同时并发,谁也不用等谁,这就是异步。

    • 如果ajax请求1在发送的时候需要等待ajax请求2结束之后才能发送,这就是同步。

  • 代码实现

    //假设这个是ajax请求1
    //如果第三个参数是false,表示不支持异步。会影响其他ajax请求的发送,只有当我这个请求结束之后,其他的ajax请求才能发送。
    xhr1.open("请求方式","URL",false);
    xhr1.send();
    
    //假设这个是ajax请求2
    //如果第三个参数是true,表示支持异步请求。ajax请求2发送之后,不影响其他ajax请求的发送。
    xhr2.open("请求方式","URL",true);
    xhr2.send();

  • 什么情况下使用同步?(大部分情况下使用ajax异步请求,同步很少用)

    • 用户注册

      • 用户名需要发送ajax请求进行校验

      • 密码需要发送ajax请求进行校验

      • ......

      • 最终点击注册按钮的时候,也需要发送ajax请求

      • 注册的ajax请求和校验的ajax请求不能异步,必须等待所有的校验ajax请求结束之后,注册的ajax请求才能发送。

AJAX代码封装

  • AJAX请求相关的代码都是类似的,有很多重复的代码,这些重复的代码可以封装成一个工具类。如果发送AJAX请求,就直接调用这个工具类的相关函数即可。

  • 手动开发jQuery,源代码

    function jQuery(selector) {
        if (typeof selector == "string") {
            if (selector.charAt(0) == "#") {
                domObj = document.getElementById(selector.substring(1));
                return new jQuery();
            }
        }
        if (typeof selector == "function") {
            window.onload = selector;
        }
        this.html = function (htmlStr) {
            domObj.innerHTML = htmlStr;
        }
        this.click = function (fun) {
            domObj.onclick = fun;
        }
        this.val = function (v) {
            if (v == undefined) {
                return domObj.value;
            } else {
                domObj.value = v;
            }
        }
    
        jQuery.ajax = function (jsonArgs) {
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function () {
                if (xhr.readyState == 4) {
                    if (xhr.status == 200) {
                        //假设服务器都是json格式
                        var jsonObj = JSON.parse(this.responseText);
                        //调用函数
                        jsonArgs.success(jsonObj);
                    }
                }
            }
            if (jsonArgs.type.toUpperCase() == "POST") {
                xhr.open("POST", jsonArgs.url, jsonArgs.async);
                xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
                xhr.send(jsonArgs.data);
            }
            if (jsonArgs.type.toUpperCase() == "GET") {
                xhr.open("GET", jsonArgs.url + "?" + jsonArgs.data, jsonArgs.async);
                xhr.send();
            }
        }
    }
    
    $ = jQuery;
    //执行这个的目的是让静态方法ajax生效
    new jQuery();

  • 前端测试代码

    
    
    
        
        Title
    
    
    
    
    
    
    
    用户名:
  • 后端测试代码

    package servlet;
    
    import jakarta.servlet.ServletException;
    import jakarta.servlet.annotation.WebServlet;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    @WebServlet("/ajaxPackageTestServlet")
    public class AjaxPackageTestServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            String username = request.getParameter("username");
            response.setContentType("text/html;charset=utf-8");
            // {"uname":"tom"}
            response.getWriter().print("{\"uname\":\""+username.toUpperCase()+"\"}");
        }
    
        protected void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            String username = request.getParameter("username");
            response.setContentType("text/html;charset=utf-8");
            // {"uname":"tom"}
            response.getWriter().print("{\"uname\":\""+username.toUpperCase()+"\"}");
        }
    }

AJAX实现省市联动

  • 什么是省市联动?

    • 在网页上,选择对应的省份之后,动态的关联出该省份对应的市。选择对应的市之后,动态的关联出该市对应的区。

  • 进行数据库表的设计

    t_area (区域表)
    id(PK-自增)     code        name        pcode
    -----------------------------------------------
    1               001         河北省      null
    2				002			河南省		 null	
    3				003			石家庄		 001
    4				004			邯郸		  001
    5				005			郑州		  002
    6				006			洛阳		  002
    7				007			涧西区		 006
    
    将全国所有的省、市、区、县等信息存储在一张表上
    采用的存储方式是 code pcode形式

  • 建表t_area,模拟好数据。

  • 实现功能

    • 页面加载完毕之后,将所有的区域信息全部展示

    • 选择省份之后,下拉出现该省对应的市

    • 选择市之后,下拉出现该市对应的区

    • ......

  • 省市联动前端代码

    
    
    
        
        Title
    
    
    
    
    
    
    
    
    

  • 省市联动后端代码

    package servlet;
    
    import bean.Area;
    import com.alibaba.fastjson.JSON;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.annotation.WebServlet;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    import java.sql.*;
    import java.util.ArrayList;
    import java.util.List;
    
    
    @WebServlet("/listAreaServlet")
    public class listAreaServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            String pcode = request.getParameter("pcode");
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            String driver = "com.mysql.cj.jdbc.Driver";
            String url = "jdbc:mysql://localhost:3306/ajax?useUnicode=true&characterEncoding=UTF-8";
            String user = "root";
            String password = "030522";
            List areaList = new ArrayList<>();
            try {
                Class.forName(driver);
                conn = DriverManager.getConnection(url, user, password);
                String sql = "";
                if (pcode == null) {
                    sql = "select code,name from t_area where pcode is null";
                    ps = conn.prepareStatement(sql);
                } else {
                    sql = "select code,name from t_area where pcode=?";
                    ps = conn.prepareStatement(sql);
                    ps.setString(1, pcode);
                }
                rs = ps.executeQuery();
                while (rs.next()) {
                    String code = rs.getString("code");
                    String name = rs.getString("name");
                    Area area = new Area(code, name);
                    areaList.add(area);
                }
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    rs.close();
                    ps.close();
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            response.setContentType("text/html;charset=utf-8");
            //使用fastjson将java对象转换成json字符串
            String jsonStr = JSON.toJSONString(areaList);
            //响应JSON
            response.getWriter().print(jsonStr);
        }
    }

AJAX跨域问题

跨域

  • 跨域指的是从一个域名的网页区请求另一个域名的资源。比如从百度页面去请求京东的资源。

  • 通过超链接或form表单或window.location.href的方式进行跨域是不存在问题的。但在一个域名的网页中的一段js代码发送ajax请求去访问另一个域名中的资源,由于同源策略的存在导致无法跨域访问,那么ajax就存在这种跨域问题。

AJAX详解_第5张图片

 

同源策略

  • 同源策略指的是一段脚本只能读取来自统一来源的窗口和文档的属性,同源就是协议、域名和端口号都相同。

  • 同源策略作用:如果你刚刚再网银输入账号密码,查看到了自己的存款信息,紧接着访问一些不安全的网站,这个网站可以访问刚刚的网银站点,并获取账号密码...... 因此,从安全的角度来讲,同源策略是有利于保护网站信息的。

  • 区分同源和不同源的三要素:

    • 协议

    • 域名

    • 端口号

  • 只有协议、域名、端口号均一致,才是同源,其他情况都是不同源

URL1 URL2 是否同源 描述
http://localhost:8080/a/index.html http://localhost:8080/a/first 同源 协议 域名 端口一致
http://localhost:8080/a/index.html http://localhost:8080/b/first 同源 协议 域名 端口一致
http://www.myweb.com:8080/a.js https://www.myweb.com:8080/b.js 不同源 协议不同
http://www.myweb.com:8080/a.js http://www.myweb.com:8081/b.js 不同源 端口不同
http://www.myweb.com/a.js MyWeb2.com is for sale | HugeDomains 不同源 域名不同
http://www.myweb.com/a.js http://crm.myweb.com/b.js 不同源 子域名不同
  • 在某种情况下,需要使用ajax进行跨域访问。比如某公司的A页面有可能需要获取B页面。

AJAX跨域解决方案

方案一:设置响应头

  • 核心原理:跨域访问的资源允许你跨域访问。

  • 实现:

response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080"); // 允许某个
response.setHeader("Access-Control-Allow-Origin", "*"); // 允许所有

方案二:jsonp

  • jsonp:json with padding(带填充的json)

  • jsonp不是一个真正的ajax请求,只不过是可以完成ajax的局部刷新效果,解决跨域问题。可以说jsonp是一种类ajax请求的机制。

  • jsonp解决跨域的时候,只支持GET请求,不支持POST请求。

  • 应用A前端代码

    
    
    
        
        Title
    
    
    
    
    
    

  • 应用B后端代码

package com;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet("/jsonp")
public class jsonpServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //获取函数名
        String fun = request.getParameter("fun");
        //响应一段js代码
        response.getWriter().print(fun + "({\"username\":\"tom\"})");
    }
}

方案三:jQuery封装的jsonp

  • jQuery库中已经对jsonp进行了封装,可以直接拿来用。

  • 用之前需要引入jQuery库的js文件。

  • jQuery中的jsonp其实就是方案二的高度封装,底层原理完全相同。

  • 应用A前端代码




    
    Title







  • 应用B后端代码

package com;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet("/jQueryJsonp")
public class jQueryJsonpServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //String callback = request.getParameter("callback");
        //response.getWriter().print(callback + "({\"username\":\"tom\"})");

        String fun = request.getParameter("fun");
        response.getWriter().print(fun + "({\"username\":\"tom\"})");
    }
}

方案四:代理机制(httpclient)

  • 使用java程序怎么去发送get/post请求呢?

    • 第一种方式:使用JDK内置的API,这些API是可以发送HTTP请求的。

    • 第二种方式:使用第三方的开源组件,比如apache的httpclient组件。(httpclient组件时开源免费的,可以直接用)

  • 在java程序中,使用httpclient组件可以发送http请求。需要先将这个组件相关的jar包引入到项目中。

AJAX详解_第6张图片

 

  • 应用A前端代码

    
    
    
        
        Title
    
    
    
    
    
    

  • 应用A后端代码

    package com;
    
    import jakarta.servlet.ServletException;
    import jakarta.servlet.annotation.WebServlet;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    @WebServlet("/proxy")
    public class ProxyServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            // 通过httpclient组件,发送HTTP GET请求,访问 TargetServlet
            HttpGet httpGet = new HttpGet("http://localhost:8081/b/target");
            httpGet.setHeader("Content-Type", "application/x-www-form-urlencoded");
            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpResponse resp = httpClient.execute(httpGet);
            HttpEntity entity = resp.getEntity();
            BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));
            String line = null;
            StringBuffer responseSB = new StringBuffer();
            while ((line = reader.readLine()) != null) {
                responseSB.append(line);
            }
            reader.close();
            httpClient.close();
            // b站点响应回来的数据
            response.getWriter().print(responseSB);
        }
    }

  • 应用B后端代码

    package com;
    
    import jakarta.servlet.ServletException;
    import jakarta.servlet.annotation.WebServlet;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    
    @WebServlet("/target")
    public class TargetServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            response.getWriter().print("{\"username\":\"tom\"}");
        }
    }

方案五:nginx反向代理

  • nginx反向代理中也是使用了这种代理机制来完成AJAX的跨域,实现起来非常简单,只要修改nginx的配置即可。

AJAX实现搜索联想 自动补全

  • 什么是搜索联想,自动补全?

    • 百度是一个很典型的代表,在百度的搜索框中输入相关信息的时候,会有搜索联想和自动补全。

    • 搜索联想:当用户输入一些单词之后,自动联想出用户要搜索的信息,给一个提示。

    • 自动补全:当联想出一些内容之后,用户点击某个联想的单词,然后将这个单词自动补全到搜索框当中。

    • 搜索联想和自动补全功能,因为是页面局部刷新效果,所有需要使用ajax请求来实现。

  • 搜索联想和自动补全的核心实现原理?

    • 当键盘事件发生之后,比如keyup:键弹起事件。

    • 发送ajax请求,请求中提交用户输入的搜索内容。

    • 后端接收ajax请求,执行select语句进行模糊查询(like),返回查询结果。

    • 将查询结果封装成json格式的字符串,将字符串响应到前端。

    • 前端接收到json格式的字符串之后,解析这个字符串,动态展示页面。

  • 前端页面

    
    
    
        
        ajax实现搜索联想和自动补全功能
        
    
    
    

  • 后端页面

    package servlet;
    ​
    import jakarta.servlet.ServletException;
    import jakarta.servlet.annotation.WebServlet;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    ​
    import java.io.IOException;
    import java.sql.*;
    ​
    @WebServlet("/query")
    public class queryServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            //获取用户输入的关键字
            String keywords = request.getParameter("keywords");
            String driver = "com.mysql.cj.jdbc.Driver";
            String url = "jdbc:mysql://localhost:3306/ajax";
            String user = "root";
            String password = "030522";
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            StringBuilder builder = new StringBuilder();
            try {
                Class.forName(driver);
                conn = DriverManager.getConnection(url, user, password);
                String sql = "select content from t_query where content like ?";
                ps = conn.prepareStatement(sql);
                ps.setString(1, keywords + "%");
                rs = ps.executeQuery();
                builder.append("[");
                while (rs.next()) {
                    String content = rs.getString("content");
                    builder.append("{\"content\":\"" + content + "\"},");
                }
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    rs.close();
                    ps.close();
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            response.getWriter().print(builder.subSequence(0, builder.length() - 1) + "]");
        }
    }
     

附录:HTTP状态信息

1xx: 信息

消息: 描述:
100 Continue 服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求。
101 Switching Protocols 服务器转换协议:服务器将遵从客户的请求转换到另外一种协议。

2xx: 成功

消息: 描述:
200 OK 请求成功(其后是对GET和POST请求的应答文档。)
201 Created 请求被创建完成,同时新的资源被创建。
202 Accepted 供处理的请求已被接受,但是处理未完成。
203 Non-authoritative Information 文档已经正常地返回,但一些应答头可能不正确,因为使用的是文档的拷贝。
204 No Content 没有新文档。浏览器应该继续显示原来的文档。如果用户定期地刷新页面,而Servlet可以确定用户文档足够新,这个状态代码是很有用的。
205 Reset Content 没有新文档。但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容。
206 Partial Content 客户发送了一个带有Range头的GET请求,服务器完成了它。

3xx: 重定向

消息: 描述:
300 Multiple Choices 多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址。
301 Moved Permanently 所请求的页面已经转移至新的url。
302 Found 所请求的页面已经临时转移至新的url。
303 See Other 所请求的页面可在别的url下被找到。
304 Not Modified 未按预期修改文档。客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。
305 Use Proxy 客户请求的文档应该通过Location头所指明的代理服务器提取。
306 Unused 此代码被用于前一版本。目前已不再使用,但是代码依然被保留。
307 Temporary Redirect 被请求的页面已经临时移至新的url。

4xx: 客户端错误

消息: 描述:
400 Bad Request 服务器未能理解请求。
401 Unauthorized 被请求的页面需要用户名和密码。
402 Payment Required 此代码尚无法使用。
403 Forbidden 对被请求页面的访问被禁止。
404 Not Found 服务器无法找到被请求的页面。
405 Method Not Allowed 请求中指定的方法不被允许。
406 Not Acceptable 服务器生成的响应无法被客户端所接受。
407 Proxy Authentication Required 用户必须首先使用代理服务器进行验证,这样请求才会被处理。
408 Request Timeout 请求超出了服务器的等待时间。
409 Conflict 由于冲突,请求无法被完成。
410 Gone 被请求的页面不可用。
411 Length Required "Content-Length" 未被定义。如果无此内容,服务器不会接受请求。
412 Precondition Failed 请求中的前提条件被服务器评估为失败。
413 Request Entity Too Large 由于所请求的实体的太大,服务器不会接受请求。
414 Request-url Too Long 由于url太长,服务器不会接受请求。当post请求被转换为带有很长的查询信息的get请求时,就会发生这种情况。
415 Unsupported Media Type 由于媒介类型不被支持,服务器不会接受请求。
416 服务器不能满足客户在请求中指定的Range头。
417 Expectation Failed

5xx: 服务器错误

消息: 描述:
500 Internal Server Error 请求未完成。服务器遇到不可预知的情况。
501 Not Implemented 请求未完成。服务器不支持所请求的功能。
502 Bad Gateway 请求未完成。服务器从上游服务器收到一个无效的响应。
503 Service Unavailable 请求未完成。服务器临时过载或当机。
504 Gateway Timeout 网关超时。
505 HTTP Version Not Supported 服务器不支持请求中指明的HTTP协议版本。

你可能感兴趣的:(ajax,javascript,ecmascript,java,web)