AJAX(Asynchronous Javascript And Xml)

news2024/11/17 14:36:30

传统请求及缺点


  • 传统的请求都有哪些?

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

  • 点击超链接

  • 提交form表单

  • 使用JS代码发送请求

  • window.open(url)

  • document.location.href = url

  • window.location.href = url

  • ....

  • 传统请求存在的问题

  • 页面代码全部清空、刷新,导致了用户的体验较差。(如播放到一半的视频重新加载重新播放)

  • 传统的请求导致用户的体验有空白期。(用户的体验是不连贯的)

AJAX概述


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

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

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

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

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

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

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

  • AJAX 应用程序可能使用 XML 来传输数据,但将数据作为纯文本或 JSON 文本传输也同样常见。

  • AJAX可以更新网页的部分,而不需要重新加载整个页面。(页面局部刷新)

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

XMLHttpRequest对象


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

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

  • 创建XMLHttpRequest对象

  • varxhr=newXMLHttpRequest();

  • XMLHttpRequest对象的方法

方法

描述

abort()

取消当前请求

getAllResponseHeaders()

返回头部信息

getResponseHeader()

返回特定的头部信息

open(method, url, async, user, psw)

规定请求method:请求类型 GET 或 POSTurl:文件位置async:true(异步)或 false(同步)user:可选的用户名称psw:可选的密码

send()

将请求发送到服务器,用于 GET 请求

send(string)

将请求发送到服务器,用于 POST 请求

setRequestHeader()

向要发送的报头添加标签/值对

  • XMLHttpRequest对象的属性

属性

描述

onreadystatechange

定义当 readyState 属性发生变化时被调用的函数

readyState

保存 XMLHttpRequest 的状态。0:请求未初始化 1:服务器连接已建立 2:请求已收到 3:正在处理请求 4:请求已完成且响应已就绪

responseText

以字符串返回响应数据

responseXML

以 XML 数据返回响应数据

status

返回请求的状态号200: "OK"403: "Forbidden"404: "Not Found"

statusText

返回状态文本(比如 "OK" 或 "Not Found")

AJAX GET请求


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

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax get请求</title>
</head>
<body>

<script type="text/javascript">
    window.onload = function(){
        document.getElementById("helloBtn").onclick = function (){
            // 发送ajax get请求
            //console.log("发送ajax get请求")
            // 1.第一步:创建AJAX核心对象XMLHttpRequest
            var xhr = new XMLHttpRequest();
            // 2.第二步:注册回调函数
            // 这是一个回调函数,这个函数在XMLHttpRequest对象的readyState状态值发生改变的时候被调用。
            xhr.onreadystatechange = function (){
                // 这里的回调函数会被调用多次。
                // 0 -> 1 被调用一次
                // 1 -> 2 被调用一次
                // 2 -> 3 被调用一次
                // 3 -> 4 被调用一次
                //console.log(xhr.readyState)

                // 当XMLHttpRequest对象的readyState的状态是4的时候,表示响应结束了。
                /*if (xhr.readyState == 4) {

                }*/

                if (this.readyState == 4) {
                    // 响应结束了。
                    //console.log("响应结束了")
                    // 响应结束之后,一般会有一个HTTP的状态码。
                    // HTTP状态码常见的包括:200表示成功了,404表示资源找不到,500表示服务器内部错误。
                    // HTTP状态码是HTTP协议的一部分,HTTP协议中规定的。服务器响应之后都会有一个状态码。
                    // 获取HTTP状态码
                    //console.log("HTTP响应状态码:" + this.status)
                    if (this.status == 404) {
                        alert("对不起,您访问的资源不存在,请检查请求路径")
                    } else if(this.status == 500){
                        alert("对不起,服务器发生了严重的内部错误,请联系管理员")
                    } else if(this.status == 200){
                        //alert("响应成功,完美")
                        // 200表示完全响应完毕,成功结束了。
                        // 通过XMLHttpRequest对象获取响应的信息。
                        // 通过XMLHttpRequest对象的responseText属性来获取响应的信息。
                        //alert(this.responseText)

                        // 把响应信息放到div图层当中,渲染
                        document.getElementById("mydiv").innerHTML = this.responseText
                    }

                }
            }
            // 3.第三步:开启通道(open只是浏览器和服务器建立连接,通道打开,并不会发送请求)
            // XMLHttpRequest对象的open方法
            // open(method, url, async, user, psw)
            // method: 请求的方式,可以是GET,也可以是POST,也可以是其它请求方式。
            // url:请求的路径
            // async: 只能是true或者false,true表示此ajax请求是一个异步请求,false表示此ajax请求是一个同步请求。(大部分请求都是true,要求异步。极少数情况需要同步,以后再说。)
            // user:用户名 pwd: 密码,用户名和密码是进行身份认证的,说明要想访问这个服务器上的资源,可能需要提供一些口令才能访问。需不需要用户名和密码,主要看服务器的态度。
            xhr.open("GET", "/ajax/ajaxrequest1", true)
            // 4.第四步:发送请求
            xhr.send()
        }
    }
</script>

<!--给一个按钮,用户点击这个按钮的时候发送ajax请求-->
<input type="button" value="hello ajax" id="helloBtn">

<!--给一个div图层,ajax接收了响应的数据之后,在div中进行渲染。-->
<div id="mydiv"></div>

</body>
</html>
  • 发送AJAX get请求,后端代码:

​package com.bjpowernode.ajax.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 {
        /*String s = null;
        s.toString();*/

        // Servlet向浏览器响应一段数据
        PrintWriter out = response.getWriter();

        // out对象向浏览器输出信息
        // 服务器的代码实际上和以前的代码还是完全一样的。
        // 只不过这个out在响应的时候,浏览器客户端的XMLHttpRequest对象会接收到这个响应的信息。
        out.print("<font color='red'>welcome to study ajax!!!!</font>");
    }
}

有了AJAX后,response.getWriter().print()方法输出的字符串不再立即呈现到浏览器页面上,而是会被XMLHttpRequest对象的responseText属性接收,如果不在HTML页面中将其提出来,那么就不会呈现。

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

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

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

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>发送ajax get请求</title>
</head>
<body>
<script type="text/javascript">
    window.onload = function () {
        document.getElementById("btn").onclick = function () {
            //1. 创建AJAX核心对象
            var xhr = new XMLHttpRequest();
            //2. 注册回调函数
            xhr.onreadystatechange = function(){
                if (this.readyState == 4) {
                    if (this.status == 200) {
                        // 通过XMLHttpRequest对象的responseText属性可以获取到服务器响应回来的内容。
                        // 并且不管服务器响应回来的是什么,都以普通文本的形势获取。(服务器可能响应回来:普通文本、XML、JSON、HTML...)
                        // innerHTML属性是javascript中的语法,和ajax的XMLHttpRequest对象无关。
                        // innerHTML可以设置元素内部的HTML代码。(innerHTML可以将后面的内容当做一段HTML代码解释并执行)
                        //document.getElementById("myspan").innerHTML = this.responseText
                        document.getElementById("mydiv").innerHTML = this.responseText
                        // innerText也不是AJAX中的,是javascript中的元素属性,和XMLHttpRequest无关。
                        // innerText也是设置元素中的内容,但是即使后面是一段HTML代码,也是将其看做一个普通字符串设置进去。
                        //document.getElementById("myspan").innerText = this.responseText
                    }else{
                        alert(this.status)
                    }
                }
            }
            //3. 开启通道
            // 获取到用户填写的usercode和username
            var usercode = document.getElementById("usercode").value
            var username = document.getElementById("username").value
            
            xhr.open("GET", "/ajax/ajaxrequest2?usercode="+usercode+"&username="+username, true)
            //4. 发送请求
            xhr.send()
        }
    }
</script>
usercode<input type="text" id="usercode"><br>
username<input type="text" id="username"><br>
<button id="btn">发送ajax get请求</button>
<span id="myspan"></span>
<div id="mydiv"></div>
</body>
</html>

AJAX GET请求的缓存问题


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

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

  • 在HTTP协议中是这样规定get请求的: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()

  • 也可以随机数+时间戳....

//3. 开启通道
// 获取到用户填写的usercode和username
var usercode = document.getElementById("usercode").value
var username = document.getElementById("username").value
// 搞一个时间戳
//xhr.open("GET", "/ajax/ajaxrequest2?t="+new Date().getTime()+"&usercode="+usercode+"&username="+username, true)
// 搞一个随机数
xhr.open("GET", "/ajax/ajaxrequest2?t="+Math.random()+"&usercode="+usercode+"&username="+username, true)
//4. 发送请求
xhr.send()

AJAX POST请求


  • AJAX POST请求和GET请求的代码区别在哪里?就是前端代码有区别。后端代码没有区别。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>发送ajax post请求</title>
</head>
<body>

<script type="text/javascript">
    window.onload = function () {
        document.getElementById("mybtn").onclick = function () {
            // 发送AJAX POST请求
            // 1. 创建AJAX核心对象
            var xhr = new XMLHttpRequest();
            // 2. 注册回调函数
            xhr.onreadystatechange = function (){
                if (this.readyState == 4) {
                    if (this.status == 200) {
                        document.getElementById("mydiv").innerHTML = this.responseText
                    }else{
                        alert(this.status)
                    }
                }
            }
            // 3. 开启通道
            xhr.open("POST", "/ajax/ajaxrequest3", true)

            // 4. 发送请求
            // 怎么模拟AJAX提交form表单呢?设置请求头的内容类型(这行代码非常关键,是模拟form表单提交的关键代码。)
            // 设置请求头的内容类型时,必须在send之前,open之后。
            // 这一行代码的内容是固定的
            xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")

            // 放到send()这个函数的小括号当中的数据,会自动在请求体当中提交数据。
            // 使用JS代码获取用户填写的用户名和密码
            var username = document.getElementById("username").value;
            var password = document.getElementById("password").value;
            //xhr.send("注意格式:放在这里的数据就是在请求体当中提交的,格式不能随便来,还是需要遵循HTTP的协议:name=value&name=value&name=value")
            xhr.send("username="+username+"&password="+password)
        }
    }
</script>

用户名<input type="text" id="username"><br>
密码<input type="password" id="password"><br>

<button id="mybtn">发送AJAX POST请求</button>

<div id="mydiv"></div>

</body>
</html>

  • 实现一个案例:用户点击按钮之后,发送AJAX请求,显示学生列表。

  • 在后端java程序中拼接HTML代码,然后将HTML代码直接响应到浏览器客户端。这种方式不好,不应该在java代码中编写HTML代码,能否在java程序中直接向前端响应数据?可以,可以在后端拼接JSON格式的字符串,或者XML格式的字符串,将这个字符串发送给前端,前端解析即可。

基于JSON的数据交换


  • 在WEB前端中,如何将一个json格式的字符串转换成json对象

有两种方式:

第一种方式:使用eval函数。(这个以前在javascript语言中讲过)

        <script type="text/javascript">
            /*
                eval函数的作用是:
                    将字符串当做一段JS代码解释并执行。
            */
          
           // java连接数据库,查询数据之后,将数据在java程序中拼接成JSON格式的“字符串”,将json格式的字符串响应到浏览器
           // 也就是说java响应到浏览器上的仅仅是一个"JSON格式的字符串",还不是一个json对象.
           // 可以使用eval函数,将json格式的字符串转换成json对象.

           var fromJava = "{\"name\":\"zhangsan\",\"password\":\"123\"}"; //这是java程序给发过来的json格式的"字符串"

           // 将以上的json格式的字符串拼接成JS代码,调用函数将字符串执行,转换成json对象
           window.eval("var jsonObj = " + fromJava);

           // 访问json对象
           alert(jsonObj.name + "," + jsonObj.password); // 在前端取数据.

           // JS中访问json对象的属性
           alert(jsonObj.username);
           
           // JS中访问json对象的属性
           alert(jsonObj["username"]);
           
        </script>

第二种方式:调用javascript语言中的内置对象JSON的一个方法parse。(不用拼接字符串,方便)

varjsonStr="{\"username\" : \"zhangsan\", \"password\" : \"1233344\"}"
varjsonObj=JSON.parse(jsonStr)
console.log(jsonObj.username)
console.log(jsonObj.password)

  • 在前端循环拼接JSON数据进行表格展示

<table width="50%" border="1px">
    <thead>
    <tr>
        <th>序号</th>
        <th>姓名</th>
        <th>年龄</th>
        <th>住址</th>
    </tr>
    </thead>
    <tbody id="stutbody">
    <!--<tr>
        <td>1</td>
        <td>张三</td>
        <td>20</td>
        <td>北京大兴区</td>
    </tr>
    <tr>
        <td>2</td>
        <td>李四</td>
        <td>22</td>
        <td>北京海淀区</td>
    </tr>-->
    </tbody>
</table>

............................

// 2.注册回调函数
xhr.onreadystatechange = function () {
    if (this.readyState == 4) {
        if (this.status == 200) {
            //document.getElementById("stutbody").innerHTML = this.responseText

            // 将json格式的字符串转换成json对象
            var stuList = JSON.parse(this.responseText) // 是一个数组,并且数组中有多个学生数据
            var html = ""
            for (var i = 0; i < stuList.length; i++) {
                var stu = stuList[i]
                html += "<tr>"
                html += "<td>"+(i+1)+"</td>"
                html += "<td>"+stu.name+"</td>"
                html += "<td>"+stu.age+"</td>"
                html += "<td>"+stu.addr+"</td>"
                html += "</tr>"
            }
            document.getElementById("stutbody").innerHTML = html

        } else {
            alert(this.status)
        }
    }
}

  • 在后端拼接JSON格式的字符串,响应给前端的浏览器

StringBuilder json = new StringBuilder();
String jsonStr = "";
json.append("[");
while (rs.next()) {
    // 获取每个学生的信息
    Stringname=rs.getString("name");
    Stringage=rs.getString("age");
    Stringaddr=rs.getString("addr");
    // 拼接json格式的字符串
    // {"name":"   王五    ","age":    20      ,"addr":"      北京大兴区     "},
    json.append("{\"name\":\"");
    json.append(name);
    json.append("\",\"age\":");
    json.append(age);
    json.append(",\"addr\":\"");
    json.append(addr);
    json.append("\"},");
}
jsonStr=json.substring(0, json.length() -1) +"]";
out.print(jsonStr);

  • 拼接JSON格式的字符串太痛苦,可以使用阿里巴巴的fastjson组件,它可以将java对象转换成json格式的字符串

List<Student>studentList=newArrayList<>();
while (rs.next()) {
    // 取出数据
    Stringname=rs.getString("name");
    intage=rs.getInt("age");
    Stringaddr=rs.getString("addr");
    // 将以上数据封装成Student对象
    Students=newStudent(name, age, addr);
    // 将Student对象放到List集合
    studentList.add(s);
}
// 将List集合转换成json字符串
jsonStr=JSON.toJSONString(studentList);   

JSON.toJSONString的参数是Object类型,可以传集合,也可以传一个对象。

注意:使用fastjson需要引入fastjson-1.2.2.jar。

而mysql驱动(不是依赖)我们不需要导入,我们只需要放在lib目录下即可。

什么时候需要导入依赖?

当我们的类中需要用到依赖中的类,那么就需要导入依赖,否则放在lib目录下即可。

上述使用阿里巴巴的组件所需要导入的类:

package com.bjpowernode.ajax.servlet;

import com.alibaba.fastjson.JSON;    // 阿里巴巴JSON组件
import com.bjpowernode.ajax.beans.Student;    // 实体类

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

// Java内置类
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

基于XML的数据交换


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

response.setContentType("text/xml;charset=UTF-8");

以前是response.setContentType("text/html;charset=UTF-8");

  • xml和JSON都是常用的数据交换格式

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

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

  • 基于XML的数据交换,前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用XML完成数据交换</title>
</head>
<body>
<script type="text/javascript">
    window.onload = function(){
        document.getElementById("btn").onclick = function(){
            // 1.创建XMLHTTPRequest对象
            var xhr = new XMLHttpRequest();
            // 2.注册回调函数
            xhr.onreadystatechange = function () {
                if (this.readyState == 4) {
                    if (this.status == 200) {
                        // 服务器端响应了一个XML字符串,这里怎么接收呢?
                        // 使用XMLHTTPRequest对象的responseXML属性,接收返回之后,可以自动封装成document对象(文档对象)
                        var xmlDoc = this.responseXML
                        //console.log(xmlDoc)
                        // 获取所有的<student>元素,返回了多个对象,应该是数组。
                        var students = xmlDoc.getElementsByTagName("student")
                        //console.log(students[0].nodeName)
                        var html = "";
                        for (var i = 0; i < students.length; i++) {
                            var student = students[i]
                            // 获取<student>元素下的所有子元素
                            html += "<tr>"
                            html += "<td>"+(i+1)+"</td>"
                            var nameOrAge = student.childNodes
                            for (var j = 0; j < nameOrAge.length; j++) {
                                var node = nameOrAge[j]
                                if (node.nodeName == "name") {
                                    //console.log("name = " + node.textContent)
                                    html += "<td>"+node.textContent+"</td>"
                                }
                                if (node.nodeName == "age") {
                                    //console.log("age = " + node.textContent)
                                    html += "<td>"+node.textContent+"</td>"
                                }
                            }
                            html += "</tr>"
                        }
                        document.getElementById("stutbody").innerHTML = html
                    }else{
                        alert(this.status)
                    }
                }
            }
            // 3.开启通道
            xhr.open("GET", "/ajax/ajaxrequest6?t=" + new Date().getTime(), true)
            // 4.发送请求
            xhr.send()
        }
    }
</script>
<button id="btn">显示学生列表</button>
<table width="500px" border="1px">
    <thead>
    <tr>
        <th>序号</th>
        <th>姓名</th>
        <th>年龄</th>
    </tr>
    </thead>
    <tbody id="stutbody">
    <!--<tr>
        <td>1</td>
        <td>zhangsan</td>
        <td>20</td>
    </tr>
    <tr>
        <td>2</td>
        <td>lisi</td>
        <td>22</td>
    </tr>-->
    </tbody>
</table>
</body>
</html>

这里之所以使用responseXML来接受返回的数据而不是使用responseText,是因为responseText会把数据当成字符串来接收。对于JSON,我们有eval函数或者parse方法来将其作为JavaScript代码执行。但xml数据不同,xml代码不可以在HTML中使用,所有得使用responseXML来接收返回的数据,它会把xml代码自动封装成document对象(文档对象)。

相应内容改为 ("text/xml;charset=UTF-8") 也是因为返回的不是html代码,而是XML代码。(JS也算是HTML代码,可以在HTML中运行)。

  • 基于XML的数据交换,后端代码

package com.bjpowernode.ajax.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;

/**
 * @program: 代码
 * @ClassName: AjaxRequest6Servlet
 * @version: 1.0
 * @description: 服务器端返回XML字符串
 * @author: bjpowernode
 * @create: 2022-05-15 11:48
 **/
@WebServlet("/ajaxrequest6")
public class AjaxRequest6Servlet 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();

        /*
        <students>
            <student>
                <name>zhangsan</name>
                <age>20</age>
            </student>
            <student>
                <name>lisi</name>
                <age>22</age>
            </student>
        </students>
         */

        StringBuilder xml = new StringBuilder();
        xml.append("<students>");
        xml.append("<student>");
        xml.append("<name>zhangsan</name>");
        xml.append("<age>20</age>");
        xml.append("</student>");
        xml.append("<student>");
        xml.append("<name>lisi</name>");
        xml.append("<age>22</age>");
        xml.append("</student>");
        xml.append("</students>");

        out.print(xml);
    }
}

AJAX乱码问题


  • 测试内容:

  • 发送ajax get请求

  • 发送数据到服务器,服务器获取的数据是否乱码?

  • 服务器响应给前端的中文,会不会乱码?

  • 发送ajax post请求

  • 发送数据到服务器,服务器获取的数据是否乱码?

  • 服务器响应给前端的中文,会不会乱码?

  • 包括还要测试tomcat服务器的版本:

  • tomcat10和tomcat9都要进行测试。

  • 测试结果:

  • 对于tomcat10来说,关于字符集,我们程序员不需要干涉,不会出现乱码。

  • 对于tomcat9来说呢,post请求提交数据会乱码,get/post请求的响应也会乱码,仅get请求提交数据的时候不乱码。

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

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

request.setCharacterEncoding("UTF-8");
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    // 接收中文会乱码
    request.setCharacterEncoding("UTF-8");
    String username = request.getParameter("username");
    System.out.println(username);
    // 响应中文会乱码
    response.setContentType("text/html;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.print(username);
}

AJAX的异步与同步


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

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

  • 如果ajax请求1在发送的时候需要等待ajax请求2结束之后才能发送,那么这就是同步。(a等待b,或者b等待a,只要发生等待,就是同步。)

  • 异步和同步在代码上如何实现?

如果第三个参数是false:这个就表示“ajax请求1”不支持异步,也就是说ajax请求1发送之后,会影响其他ajax请求的发送,只有当我这个请求结束之后,你们其他的ajax请求才能发送。

false表示,不支持异步。我这个请求发了之后,你们其他的请求都要靠边站。都等着。你们别动呢,等我结束了你们再说。

xhr1.open("请求方式", "URL", false)
xhr1.send()

如果第三个参数是true:这个就表示“ajax请求2”支持异步请求,也就是说ajax请求2发送之后,不影响其他ajax请求的发送。

xhr2.open("请求方式", "URL", true) 
xhr2.send()

  • 什么情况下用同步?(大部分情况下我们都是使用ajax异步方式,同步很少用。)

  • 举一个例子

  • 用户注册

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

  • 邮箱地址也需要发送ajax请求校验

  • 其他的也可能需要发送ajax请求。。。

  • 并且最终注册按钮的时候,也是发送ajax请求进行注册。

  • 那么显然,注册的Ajax请求和校验的ajax请求不能异步,必须等待所有的校验ajax请求结束之后,注册的ajax请求才能发。否则如果校验卡住了,因为是异步,用户依然可以点击注册按钮进行注册,导致数据不合法。

AJAX代码封装


  • AJAX请求相关的代码都是类似的,有很多重复的代码,这些重复的代码能不能不写,能不能封装一个工具类。要发送ajax请求的话,就直接调用这个工具类中的相关函数即可。

  • 接下来,手动封装一个工具类,这个工具类我们可以把它看做是一个JS的库。我们把这个JS库起一个名字,叫做jQuery。(我这里封装的jQuery只是一个前端的库,和后端的java没有关系,只是为了方便web前端代码的编写,提高WEB前端的开发效率)

  • 手动开发jQuery,源代码

版本一:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>手动封装JS库jQuery</title>
</head>
<body>

<script type="text/javascript">

    /*封装一个函数,通过这个函数可以获取到html页面中的节点,这个函数我给他起一个名字,叫做:jQuery*/
    /*要封装的代码是:根据id来获取元素。document.getElementById("btn")*/
    /*设计思路来自于CSS的语法。 在CSS中,#id 可以获取到这个元素 */
    function jQuery(selector){ // selector可能是#id,也可以能是其他的选择器,例如类选择器:.class

        // 根据id获取元素
        if (typeof selector == "string") {
            if (selector.charAt(0) == "#") {
                // selector是一个id选择器
                //var domObj = document.getElementById(selector.substring(1))
                // 升级成全局变量
                domObj = document.getElementById(selector.substring(1))  // 将字符串从1开始截截到末位
                // 返回的dom对象
                return domObj
            }
        }
    }

    // 变量名可以有美元符号,将函数变量赋给一个新的(名字短的)变量
    $ = jQuery

    window.onload = function () {
        // document.getElementById("btn").onclick = function(){
        //     document.getElementById("div1").innerHTML = "<font color='red'>用户名不可用!!!!</font>"
        // }

        // jQuery("#btn").onclick = function(){
        //     jQuery("#div1").innerHTML = "<font color='red'>用户名不可用~~~~</font>"
        // }

        $("#btn").onclick = function(){
            $("#div1").innerHTML = "<font color='red'>~~~~用户名不可用~~~~</font>"
        }
    }

</script>

用户名:<input type="text" id="username">

<button id="btn">显示信息</button>

<div id="div1"></div>

</body>
</html>

版本二:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>手动封装JS库jQuery</title>
</head>
<body>

<script type="text/javascript">

    /*封装一个函数,通过这个函数可以获取到html页面中的节点,这个函数我给他起一个名字,叫做:jQuery*/
    /*要封装的代码是:根据id来获取元素。document.getElementById("btn")*/
    /*设计思路来自于CSS的语法。 在CSS中,#id 可以获取到这个元素 */
    function jQuery(selector){ // selector可能是#id,也可以能是其他的选择器,例如类选择器:.class

        // 根据id获取元素
        if (typeof selector == "string") {
            if (selector.charAt(0) == "#") {
                // selector是一个id选择器
                //var domObj = document.getElementById(selector.substring(1))
                // 升级成全局变量
                domObj = document.getElementById(selector.substring(1))
                // 返回的dom对象
                //return domObj
                // 返回的jQuery对象
                return new jQuery()
            }
        }

        // 页面加载完毕之后,注册回调函数。
        if (typeof selector == "function") {
            window.onload = selector
        }

    }

    $ = jQuery


    // $(function(){}) 作用是什么?
    // 只要你写上以上的代码,就表示在页面加载完毕之后,执行里面的回调函数。
    $(function () {
        $("#btn").onclick = function(){
            $("#div1").innerHTML = "<font color='red'>%%%%%%%%用户名不可用~~~~</font>"
        }
    })

</script>

用户名:<input type="text" id="username">

<button id="btn">显示信息</button>

<div id="div1"></div>

</body>
</html>

版本三:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>手动封装JS库jQuery</title>
</head>
<body>

<script type="text/javascript">

    /*封装一个函数,通过这个函数可以获取到html页面中的节点,这个函数我给他起一个名字,叫做:jQuery*/
    /*要封装的代码是:根据id来获取元素。document.getElementById("btn")*/
    /*设计思路来自于CSS的语法。 在CSS中,#id 可以获取到这个元素 */
    function jQuery(selector){ // selector可能是#id,也可以能是其他的选择器,例如类选择器:.class

        // 根据id获取元素
        if (typeof selector == "string") {
            if (selector.charAt(0) == "#") {
                // selector是一个id选择器
                //var domObj = document.getElementById(selector.substring(1))
                // 升级成全局变量,不然下面的html等方法不能用
                domObj = document.getElementById(selector.substring(1))
                // 返回的dom对象
                //return domObj
                // 返回的jQuery对象,因为 domObj 对象(标签)没有html、click等方法
                return new jQuery()
            }
        }

        // 页面加载完毕之后,注册回调函数。
        if (typeof selector == "function") {
            window.onload = selector
        }

        // 定义一个html()函数,代替:domObj.innerHTML = ""
        this.html = function(htmlStr){
            domObj.innerHTML = htmlStr
        }

        // 定义一个click()函数,代替:domObj.onclick = function(){}
        this.click = function(fun){
            domObj.onclick = fun
        }

        this.focus = function (fun){
            domObj.onfocus = fun
        }

        this.blur = function(fun) {
            domObj.onblur = fun
        }

        this.change = function (fun){
            domObj.onchange = fun
        }

        // ....

        this.val = function(v){
            if (v == undefined) {
                return domObj.value     // 获得value,返回文本框的值
            }else{
                domObj.value = v        // 修改value
            }
        }
    }

    $ = jQuery

    // 这里把jQuery当成是方法进行传参调用
    $(function () {
        // $(标签名)不再返回标签对象,而是将类的成员变量设置为标签对象,然后通过返回的类对象对方法进行调用
        // 这里把jQuery当成是类调用其方法
        $("#btn").click(function(){
            $("#div1").html("<font color='red'>%%%%%%%%用户名不可用%%%%%%%%%</font>")

            // 获取到文本框中的用户名
            /*var username = document.getElementById("username").value
            alert(username)*/

            var username = $("#username").val()
            alert(username)

            // 修改文本框的value
            //document.getElementById("username").value = "呵呵"
            $("#username").val("呵呵了!!!")

        })
    })

</script>

用户名:<input type="text" id="username">

<button id="btn">显示信息</button>

<div id="div1"></div>

</body>
</html>

版本四(开始封装AJAX):

讲解:将AJAX请求封装到jQuery库当中_哔哩哔哩_bilibili

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.focus = function (fun){
        domObj.onfocus = fun
    }
    this.blur = function(fun) {
        domObj.onblur = fun
    }
    this.change = function (fun){
        domObj.onchange = fun
    }
    this.val = function(v){
        if (v == undefined) {
            return domObj.value
        }else{
            domObj.value = v
        }
    }

    // 静态的方法,发送ajax请求
    /**
     * 分析:使用ajax函数发送ajax请求的时候,需要程序员给我们传过来什么?
     *      请求的方式(type):GET/POST
     *      请求的URL(url):url
     *      请求时提交的数据(data):data
     *      请求时发送异步请求还是同步请求(async):true表示异步,false表示同步。
     */
    jQuery.ajax = function(jsonArgs){
        // 1.
        var xhr = new XMLHttpRequest();
        // 2.
        xhr.onreadystatechange = function(){
            if (this.readyState == 4) {
                if (this.status == 200) {
                    // 我们这个工具类在封装的时候,先不考虑那么多,假设服务器返回的都是json格式的字符串。
                    var jsonObj = JSON.parse(this.responseText)
                    // 调用函数
                    jsonArgs.success(jsonObj)
                }
            }
        }

        // 转大写,避免传的是小写
        if (jsonArgs.type.toUpperCase() == "POST") {
            // 3.
            xhr.open("POST", jsonArgs.url, jsonArgs.async)
            // 4.
            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生效。(JS的静态方法)
// JS的类的静态方法要先new才能调用,很奇怪
new jQuery()
  • 使用以上库,怎么用?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试我们自己手动开发的jQuery库</title>
</head>
<body>

<!--引入自己写的jQuery库-->
<script type="text/javascript" src="/ajax/js/jQuery-1.0.0.js"></script>

<script type="text/javascript">
    $(function(){
        $("#btn1").click(function(){
            // 发送ajax请求
            $.ajax({    // 传一个JSON过去
                //type : "GET",
                type : "POST",
                url : "/ajax/ajaxrequest11",
                data : "username=" + $("#username").val(),
                async : true,
                success : function(json){
                    $("#div1").html(json.uname)
                }
            })
        })
    })
</script>

<button id="btn1">发送ajax请求</button><br>
用户名:<input type="text" id="username"><br>
<div id="div1"></div>

</body>
</html>
package com.bjpowernode.ajax.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.util.Locale;

/**
 * @program: 代码
 * @ClassName: AjaxRequest11Servlet
 * @version: 1.0
 * @description: 测试自己写的jQuery库
 * @author: bjpowernode
 * @create: 2022-05-15 19:38
 **/
@WebServlet("/ajaxrequest11")
public class AjaxRequest11Servlet 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":"zhangsan"}
        response.getWriter().print("{\"uname\":\""+username+"\"}");
    }
}

AJAX跨域问题


跨域

  • 跨域是指从一个域名的网页去请求另一个域名的资源(网页、Java程序等)。比如从百度(https://baidu.com)页面去请求京东(https://www.jd.com)的资源。

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

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

  • 出现这个错误的根本原因是:跨域的时候,不允许共享同一个XMLHttpRequest对象。因为共享同一个XMLHttpRequest对象是不安全的,别人可以通过这个对象获取你的在另一个服务器上提交的信息,存在安全问题。

  • 同源策略有什么用?如果你刚刚在网银输入账号密码,查看了自己还有1万块钱,紧接着访问一些不规矩的网站,这个网站可以访问刚刚的网银站点,并且获取账号密码,那后果可想而知。所以,从安全的角度来讲,同源策略是有利于保护网站信息的。

  • 有一些情况下,我们是需要使用ajax进行跨域访问的。比如某公司的A页面(a.bjpowernode.com)有可能需要获取B页面(b.bjpowernode.com)。

同源还是不同源

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

  • 协议

  • 域名

  • 端口

  • 只要上面的任一元素不一致,就是不同源。同源:XMLHttpRequest对象可以共享。不同源:XMLHttpRequest对象不可以共享。

  • 协议一致,域名一致,端口号一致,三个要素都一致,才是同源,其它一律都是不同源

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

http://www.myweb2.com/b.js

不同源

域名不同

http://www.myweb.com/a.js

http://crm.myweb.com/b.js

不同源

子域名不同

AJAX跨域解决方案

方案1:设置响应头

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

  • 实现:

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

方案2:jsonp

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

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

  • jsonp不是ajax请求,但是可以完成局部刷新的效果,并且可以解决跨域问题。

  • 注意:jsonp解决跨域的时候,只支持GET请求。不支持post请求。

  • <script>标签的src属性可以跨域访问,jsonp就是通过这种投机取巧的方式实施跨域访问。

jsonp代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jsonp实现跨域</title>
</head>
<body>

<script type="text/javascript">
    /**
     * 这是我自定义的函数
     */
    function sayHello(data){
        //alert("hello world!")
        alert("hello," + data.name)
    }

    function sum(){
        alert("求和。。。")
    }
</script>

<!--超链接也可以跨域呀?为什么不用呢?因为超链接点击之后会跳转页面,无法做到页面局部刷新效果。-->
<!--script标签是可以跨域的。src属性可以是xxx.js文件,那这个路径可以是一个servlet路径吗?可以-->
<script type="text/javascript" src="http://localhost:8081/b/jsonp1?fun=sum"></script>

</body>
</html>
package com.bjpowernode.b.web.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("/jsonp1")
public class JSONPServlet1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 在后台输出
        //System.out.println("jsonp方式完成跨域访问");

        // 向前端响应一段js代码呢?
        PrintWriter out = response.getWriter();
        //out.print("alert(123)"); // 这是响应一段js代码,只不过这个alert函数是JS内置的函数,可以直接用。
        // 注意:不要误以为是后端java代码调用了sayHello()函数,实际上后端只负责响应一个字符串回去。
        // 真正的调用者,还是浏览器,浏览器接收到这个字符串之后,会自动将这个字符串当做一段js代码解释执行。
        //out.print("sayHello()"); // 这也是响应一段JS代码。只不过这个sayHello函数是程序员自定义的。

        //响应一段js代码,然后传一个json数据给前端
        //out.print("sayHello({\"name\" : \"jackson\"})");

        // 动态获取函数名
        String fun = request.getParameter("fun");
        //out.print(fun + "({\"name\" : \"jackson\"})");
        out.print(fun + "()");

    }
}

jsonp解决跨域问题,达到ajax局部刷新的效果:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jsonp跨域</title>
</head>
<body>

<!--<script type="text/javascript" src="http://localhost:8081/b/jsonp2?fun=sayHello"></script>-->

<script type="text/javascript">
  // 自定义的函数
  function sayHello(data){ // data是一个json:{"username" : "lucy"}
    document.getElementById("mydiv").innerHTML = data.username
  }

  // 因为script标签会在页面加载的时候执行,如果我们想点击按钮后再执行跨域访问的操作,可以手动创建script元素并给相关的属性赋值
  window.onload = () => {
    document.getElementById("btn").onclick = () => {
      // 加载script元素
      // 创建script元素对象
      const htmlScriptElement = document.createElement("script");
      // 设置script的type属性
      htmlScriptElement.type = "text/javascript"
      // 设置script的src属性
      htmlScriptElement.src = "http://localhost:8081/b/jsonp2?fun=sayHello"
      // 将script对象添加到body标签中(这一步就是加载script)
      // Elements,返回一个对象数组
      document.getElementsByTagName("body")[0].appendChild(htmlScriptElement)
    }
  }
</script>

<button id="btn">jsonp解决跨域问题,达到ajax局部刷新的效果</button>

<div id="mydiv"></div>

</body>
</html>
package com.bjpowernode.b.web.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("/jsonp2")
public class JSONPServlet2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 获取函数名
        String fun = request.getParameter("fun");
        // 响应一段js代码
        response.getWriter().print(fun + "({\"username\" : \"lucy\"})");
    }
}

方案3:jQuery封装的jsonp

  • 牛人们写的jQuery库,已经对jsonp进行了封装。大家可以直接拿来用。

  • 用之前需要引入jQuery库的js文件。(这里的jQuery库咱们就不再封装了,咱们直接用jQuery写好的jsonp方式。)

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

  • jQuery包:jquery-3.6.0.min.js-Java文档类资源-CSDN文库

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jQuery的jsonp封装解决ajax跨域问题</title>
</head>
<body>

<!--引入jQuery库:这个jQuery库是官网的,不是咱们手写封装的山寨版。-->
<script type="text/javascript" src="/a/js/jquery-3.6.0.min.js"></script>

<script type="text/javascript">

    // 这个函数不需要你写,jQuery可以自动帮助你生成
    // function jQuery3600508253314856699_1655528968612(json){} 
    // 系统自动生成的这个函数默认情况,会自动调用success的回调函数。

    $(function(){
        $("#btn").click(function(){
            // 发送所谓的ajax请求(其实本质上并不是一个ajax请求。只是披着ajax的皮。乔装打扮的ajax。)
            $.ajax({
                type : "GET", // jsonp请求只支持get请求。
                /* 虽然这里的url是这样写的,但实际上发送的请求是:
                   http://localhost:8081/b/jsonp3?callback=jQuery3600508253314856699_1655528968612&_=1655528968613
                */
                /* callback=jQuery3600508253314856699_1655528968612
                   相对于http://localhost:8081/b/jsonp2?fun=sayHello
                */
                // callback就是我们之前的fun
                // jQuery3600508253314856699_1655528968612就是我们之前的sayHello,而这个名字是jQuery自动为我们生成的。
                url : "http://localhost:8081/b/jsonp3",
                dataType : "jsonp", // 指定数据类型是jsonp形式。【最关键的是它】
                success : function(data){ // data变量用来接收服务器端的响应(data是一个json:{"username":"lisi"})
                    $("#mydiv").html("欢迎你:" + data.username)
                }
            })
        })
    })
</script>

<button id="btn">jQuery库封装的jsonp</button>

<div id="mydiv"></div>

</body>
</html>
package com.bjpowernode.b.web.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("/jsonp3")
public class JSONPServlet3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 获取函数名
        // 相对于String callback = request.getParameter("fun");
        String callback = request.getParameter("callback");

        // 响应一段js代码,调用函数
        response.getWriter().print(callback + "({\"username\":\"lisi\"})");
    }
}

我们也可以指定jQuery发起请求时传的参数名以及回调参数:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jQuery的jsonp封装解决ajax跨域问题</title>
</head>
<body>

<!--引入jQuery库:这个jQuery库是官网的,不是咱们手写封装的山寨版。-->
<script type="text/javascript" src="/a/js/jquery-3.6.0.min.js"></script>

<script type="text/javascript">

    // 这个函数不需要你写,jQuery可以自动帮助你生成
    // function jQuery3600508253314856699_1655528968612(json){}
    // 系统自动生成的这个函数默认情况,会自动调用success的回调函数。

    // 自定义的函数
    function sayHello(data){
        $("#mydiv").html("欢迎你:" + data.username)
    }

    $(function(){
        $("#btn").click(function(){
            // 发送所谓的ajax请求(其实本质上并不是一个ajax请求。只是披着ajax的皮。乔装打扮的ajax。)
            $.ajax({
                type : "GET", // jsonp请求只支持get请求。
                /* 虽然这里的url是这样写的,但实际上发送的请求是:
                   http://localhost:8081/b/jsonp3?callback=jQuery3600508253314856699_1655528968612&_=1655528968613
                */
                /* callback=jQuery3600508253314856699_1655528968612
                   相对于http://localhost:8081/b/jsonp2?fun=sayHello
                */
                // callback就是我们之前的fun
                // jQuery3600508253314856699_1655528968612就是我们之前的sayHello,而这个名字是jQuery自动为我们生成的。
                url : "http://localhost:8081/b/jsonp3",
                dataType : "jsonp", // 指定数据类型是jsonp形式。【最关键的是它】
                jsonp : "fun", // 不采用默认的参数名callback,用这个属性来指定具体的参数名。
                jsonpCallback : "sayHello" // 不采用默认的回调函数,用这个属性来指定具体的回调函数。
                /*success : function(data){ // data变量用来接收服务器端的响应(data是一个json:{"username":"lisi"})
                    $("#mydiv").html("欢迎你:" + data.username)
                }*/

// 不设置jsonpCallback的时候,jQuery会自动生成一个随机的回调函数,并且这个回调函数还会自动调用success的回调函数。
            })
        })
    })
</script>

<button id="btn">jQuery库封装的jsonp</button>

<div id="mydiv"></div>

</body>
</html>

方案4:代理机制(httpclient)

  • 使用Java程序怎么去发送get/post请求呢?【GET和POST请求就是HTTP请求。】

  • 第一种方案:使用JDK内置的API(java.net.URL.....),这些API是可以发送HTTP请求的。(很麻烦,一般不用)

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

  • 在java程序中,使用httpclient组件可以发送http请求。

  • 对于httpclient组件的代码,大家目前可以不进行深入的研究,可以从网上直接搜。然后粘贴过来,改一改直接用来发送get和post请求。

  • 使用httpclient组件,需要先将这个组件相关的jar包引入到项目当中。

  • commons-httpclient-3.0.1-Java文档类资源-CSDN文库

代理方法:

package com.bjpowernode.httpclient;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class HttpClientSendGet {
    public static void main(String[] args) throws Exception {
        // 使用java代码去发送HTTP get请求
        // 目标地址
        //String url = "https://www.baidu.com";
        String url = "http://localhost:8081/b/hello";
        HttpGet httpGet = new HttpGet(url);
        // 发送post请求则 HttpPost httpPost = new HttpPost(url);

        // 设置类型 "application/x-www-form-urlencoded" "application/json"
        httpGet.setHeader("Content-Type", "application/x-www-form-urlencoded");
        System.out.println("调用URL: " + httpGet.getURI());

        // httpClient实例化
        CloseableHttpClient httpClient = HttpClients.createDefault();
        // 执行请求并获取返回
        HttpResponse response = httpClient.execute(httpGet);
        HttpEntity entity = response.getEntity();
        System.out.println("返回状态码:" + response.getStatusLine());

        // 显示结果
        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);
        }
        System.out.println("服务器响应的数据:" + responseSB);
        reader.close();

        httpClient.close();
    }
}

目标方法:

package com.bjpowernode.b.web.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("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.getWriter().print("{\"username\":\"zhangsan\"}");
    }
}

方案5:nginx反向代理

  • nginx反向代理中也是使用了这种代理机制来完成AJAX的跨域,实现起来非常简单,只要修改一个nginx的配置即可。以后大家学习nginx之后再说吧。!!!!

AJAX实现搜索联想 自动补全


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

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

  • 搜索联想和自动补全:实际上是为了方便用户的使用。让用户的体验更好。

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

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

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

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

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

  • 发送ajax请求,请求中提交用户输入的搜索内容,例如:北京(发送ajax请求,携带“北京”两个字)

  • 后端接收到ajax请求,接收到“北京”两个字,执行select语句进行模糊查询。返回查询结果。

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

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

数据库的构建:

drop table if exists t_ajax;
create table t_ajax(
    id int primary key auto_increment,
    content varchar(255)
);
insert into t_ajax(content) values('javascript');
insert into t_ajax(content) values('javaweb');
insert into t_ajax(content) values('java');
insert into t_ajax(content) values('java123');
insert into t_ajax(content) values('mysql');
insert into t_ajax(content) values('myweb');
insert into t_ajax(content) values('myapp');
insert into t_ajax(content) values('jdk');
commit;
select * from t_ajax;

前端页面实现:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax实现搜索联想和自动补全功能</title>
    <style>
        .userInput {
            width: 300px;
            height: 25px;
            font-size: 20px;
            padding-left: 5px;
        }

        .showDataDiv {
            width: 310px;
            border: 1px solid lightgray;
            background-color: antiquewhite;
            display: none;
        }

        .showDataDiv p {
            padding-left: 5px;
            margin-top: 2px;
            margin-bottom: 2px;
        }

        .showDataDiv p:hover{
            cursor: pointer;
            border: 1px blue solid;
            background-color: aliceblue;
        }
    </style>
</head>
<body>

<script type="text/javascript">
    /*不使用jQuery,也不使用我们自己写的jQuery库。使用原生的ajax实现搜索联想和自动补全。*/
    window.onload = () => {
        document.getElementById("keywords").onkeyup = function(){
            if (this.value == "") {    // 没有输入则隐藏div
                document.getElementById("datadiv").style.display = "none"
            }else{
                // 发送ajax请求
                // 1. 创建AJAX核心对象
                const xmlHttpRequest = new XMLHttpRequest();
                // 2. 注册回调函数
                xmlHttpRequest.onreadystatechange = () => {
                    if (xmlHttpRequest.readyState == 4) {
                        if (xmlHttpRequest.status >= 200 && xmlHttpRequest.status < 300) {
                            // [{"content":"javascript"},{"content":"javaweb"},{"content":"java..."}]
                            const json = JSON.parse(xmlHttpRequest.responseText);
                            // 遍历数组
                            let html = ""
                            for (let i = 0; i < json.length; i++) {
                                html += "<p onclick='setInput(\""+json[i].content+"\")'>"+json[i].content+"</p>"
                            }
                            document.getElementById("datadiv").innerHTML = html
                            // 显示出来
                            document.getElementById("datadiv").style.display = "block"
                        }
                    }
                }
                // 3. 开启通道
                xmlHttpRequest.open("GET", "/ajax-autocomplete/query?_="+new Date().getTime()+"&keywords=" + this.value, true)
                // 4. 发送请求
                xmlHttpRequest.send()
            }

        }
    }

    function setInput(content){
        document.getElementById("keywords").value = content
        document.getElementById("datadiv").style.display = "none"
    }
</script>

<input type="text" class="userInput" id="keywords">

<div id="datadiv" class="showDataDiv">
    <!--<p>北京疫情最新情况</p>
    <p>北京天气</p>
    <p>北京时间</p>
    <p>北京人</p>-->
</div>
</body>
</html>

后端代码实现:

package com.bjpowernode.javaweb.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.*;
import java.util.ArrayList;
import java.util.List;

@WebServlet("/query")
public class QueryServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 获取用户输入的关键字
        String keywords = request.getParameter("keywords");
        // jdbc代码连接数据库,根据关键字查询数据库,返回数据,拼接json格式的字符串
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        try {
            // 注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            // 获取连接
            String url = "jdbc:mysql://localhost:3306/bjpowernode?useUnicode=true&characterEncoding=UTF-8";
            String user = "root";
            String password = "root";
            conn = DriverManager.getConnection(url, user, password);

            String sql = "select content from t_ajax where content like ?"; // 模糊查询的时候,条件不建议使用%开始,因为会让字段上的索引失效,全表查询,查询效率降低。
            ps = conn.prepareStatement(sql);
            ps.setString(1, keywords + "%");
            rs = ps.executeQuery();
            // [{"content":"javascript"},{"content":"javaweb"},{"content":"java..."}]
            while (rs.next()) {
                String content = rs.getString("content");
                sb.append("{\"content\":\""+content+"\"},");
            }

        }catch(Exception e){
            e.printStackTrace();
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().print(sb.subSequence(0, sb.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协议版本。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/195486.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言——读写TXT文件中的(多行多列矩阵型)浮点型数据的两种方式

C语言——读写TXT文件中的&#xff08;多行多列矩阵型&#xff09;浮点型数据将要提取的数据方式一&#xff1a;将数据按行读取并写入到结构体数组中读取一个文件中的数据读取两个文件中的数据报错解决&#xff1a;0x00007FF6C90AB2C7 处有未经处理的异常(在 sfann_sins.exe 中…

LabVIEW更高的吞吐量与更少的延迟2

LabVIEW更高的吞吐量与更少的延迟2上一篇《LabVIEW更高的吞吐量与更少的延迟1》介绍了吞吐量的内容&#xff0c;本次介绍延迟的相关内容。改善延迟有一些技术可以解决改善延迟的问题。围绕这一目标的两个主要工具是实时操作系统和FPGA。实时操作系统任何操作系统都会遇到抖动或…

应用可靠性与性能不给力?HarmonyOS HiViewDFX了解一下

作为基础软件服务子系统的HarmonyOS HiViewDFX&#xff08;以下简称HiViewDFX&#xff09;框架&#xff0c;是HarmonyOS的公共基础设施。包括日志、事件、跟踪、故障管理及观测剖析五大部分&#xff0c;同时也提供了故障检测、定位和性能观测剖析的开发套件&#xff0c;以及将端…

操作系统权限提升(十一)之系统错误配置-启动项提权

系列文章 操作系统权限提升(一)之操作系统权限介绍 操作系统权限提升(二)之常见提权的环境介绍 操作系统权限提升(三)之Windows系统内核溢出漏洞提权 操作系统权限提升(四)之系统错误配置-Tusted Service Paths提权 操作系统权限提升(五)之系统错误配置-PATH环境变量提权 操作…

【前端】Vue项目:旅游App-(19)loading:网络请求时显示loading效果

文章目录目标过程与代码loading效果mainStore控制loading显示点击蒙板取消网络请求在网络请求处添加对loading的控制效果总代码修改或添加的文件loading.vueservice/request/indexstore/modules/mainApp.vue参考本项目博客总结&#xff1a;【前端】Vue项目&#xff1a;旅游App-…

自动化运维|云原生架构下的产品自动化发布、快速部署和持续交付实战之路

自动化运维|云原生架构下的产品自动化发布、快速部署和持续交付实战之路。 1.背景介绍 CI/CD是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法。CI/CD 的核心概念是持续集成、持续交付和持续部署。作为一种面向开发和运维团队的解决方案&#xff0c;CI/CD 主要针…

只做笔记有必要买apple pencil吗?平价电容笔排行榜

如今国内的电容笔&#xff0c;牌子也越来越多了&#xff0c;苹果原装电容笔虽然性能不错&#xff0c;但价格也非常昂贵&#xff0c;一般人是买不起的。现在市面上有很多可以取代原来的苹果电容笔的平替电容笔。下面&#xff0c;我给大家推荐几款电容笔&#xff0c;好用而且价格…

Java 文件IO操作基础,File,FileInputStream,FileOutputStream

什么是文件 有个时候我们程序中的数据不会直接保存&#xff0c;一旦程序断电&#xff0c;数据将不会存在&#xff0c;如果我们想要我们程序中的数据永久的保存起来&#xff0c;所以&#xff0c;java的文件io显得非常重要。 什么是文件流 创建文件 如下相关的文件io方法 new F…

【教程】Wireshark抓取抖音直播的OBS推流地址和密钥

真不用花钱去买提取推流码的软件。。。自己提取很简单的。 简单记录一下&#xff0c;以备自己需要时候忘了怎么搞。 1、电脑开热点&#xff0c;手机连接热点&#xff1b; 2、电脑安装wireshark软件&#xff0c;并选择WLAN或者以太网接口&#xff1b; 3、wireshark筛选rtmpt&am…

Kubernetes持久化Events到sentry

背景 Kubernetes中的事件最终还是存储在etcd中&#xff0c;默认情况下只保存1个小时&#xff0c;由于etcd并不支持一些复杂的分析操作&#xff0c;默认Kubernetes只提供了非常简单的过滤方式&#xff0c;比如通过Reason、时间、类型等。同时这些事件只是被动的存在etcd中&…

CAPL(vTESTStudio) - DOIP - UDP发送_03

继UDP接收的介绍完成后,今天我们介绍下UDP发送的函数,这里我们将我自主开发的函数整体都会介绍个大家,一般能够完成大家日常脚本开发中90%以上使用发送UDP的数据函数,绝对干货满满。 UDP发送 一、参数定义 无论DoIP发送报文的UDP还是TCP函数,亦或是CAN、CANFDLIN的发送函…

Redux了解及应用(三)

React - redux 使用&#xff08;由浅入深&#xff09;&#xff1a;https://blog.csdn.net/Jie_1997/article/details/128078971 这篇文章总结的很棒&#xff01;&#xff01;&#xff01;了解redux及应用直接看这篇文章即可 备注&#xff1a;第五节的第三小节&#xff0c;容器…

学习驱动的复杂软件符号执行

原文来自微信公众号“编程语言Lab”&#xff1a;学习驱动的复杂软件符号执行搜索关注“编程语言Lab”公众号&#xff08;HW-PLLab&#xff09;获取编程语言更多技术内容&#xff01;欢迎加入编程语言社区 SIG-编程语言测试&#xff0c;了解更多编程语言测试相关的技术内容。加入…

元宇宙:有人追捧,就会有人抵触

或许&#xff0c;直到现在&#xff0c;我们依然无法否认元宇宙即将对我们的生产和生活产生的深刻影响。即使是在它遭遇巨大的不确定性的大背景下&#xff0c;依然如此。 有人追捧&#xff0c;便有人抵触。元宇宙商用的止步不前&#xff0c;元宇宙技术的难以突破……几乎都是这…

为什么不进行穷举测试?

本章主要介绍不对所有可能性进行测试的原因&#xff0c;对于经理和测试人员&#xff0c;都应该了解测试是一种采样过程&#xff0c;需要了解采样给测试所带来的风险。 1、可进行测试的数目是无限的 如果不能查看代码内部逻辑&#xff0c;可输入的测试用例是无限的。当然还有在不…

第30章 分布式缓存强制删除触发器的触发调试

1 Services.Users.Caching.RoleCacheEventConsumer using Core.Caching; using Core.Domain.Users; using Services.Caching; namespace Services.Users.Caching { /// <summary> /// 摘要&#xff1a; /// 通过该类中的方法成员&#xff0c;在角色实体的1个实例…

Linux —— 文件系统概述、软硬链接与动静态库

目录 1.文件系统概述 1.1磁盘的基本存储结构 1.2磁盘的基本逻辑结构 1.3操作系统中的文件系统 1.4文件系统如何对磁盘进行管理 2.软链接、硬链接 2.1软链接 2.2硬链接 2.3目录的硬链接数 3.静态库和动态库 3.1静态库的制作 3.2静态库的使用 3.3动态库的制作 3.4动态…

年薪50k大佬带你五分钟学会接口自动化测试框架

今天&#xff0c;我们来聊聊接口自动化测试是什么&#xff1f;如何开始&#xff1f;接口自动化测试框架怎么做&#xff1f;自动化测试自动化测试&#xff0c;这几年行业内的热词&#xff0c;也是测试人员进阶的必备技能&#xff0c;更是软件测试未来发展的趋势。特别是在敏捷模…

分布式请求链路跟踪-SpringCloud Sleuth

文章目录1.概述1.1.为什么会出现这个技术&#xff1f; 需要解决哪些问题?1.2.是什么?1.3.如何解决问题?2.搭建链路监控步骤2.1.zipkin2.2.服务提供者2.3.服务消费者&#xff08;调用方&#xff09;2.4.测试1.概述 1.1.为什么会出现这个技术&#xff1f; 需要解决哪些问题?…

力扣刷题记录——1108. IP 地址无效化、1281. 整数的各位积和之差 次数 、1295. 统计位数为偶数的数字、1394. 找出数组中的幸运数

本专栏主要记录力扣的刷题记录&#xff0c;备战蓝桥杯&#xff0c;供复盘和优化算法使用&#xff0c;也希望给大家带来帮助&#xff0c;博主是算法小白&#xff0c;希望各位大佬不要见笑&#xff0c;今天要分享的是——《力扣刷题记录——1108. IP 地址无效化、1281. 整数的各位…