HTTP协议:四.构造 HTTP 请求

前言:本篇文章主要参考自《图解HTTP》,在学习期间也查询过其他资料。仅作为个人的学习记录。

四.构造 HTTP 请求

先描述一些通过浏览器请求访问某个网站时候,发送的 HTTP 请求和得到的响应.

当浏览器希望访问某个网站时,会与服务器建立 TCP 连接.(如果是 HTTPS,则要先经过加密等一系列步骤再建立 TCP 连接).服务器总是使用80端口和加密端口443,然后,浏览器向服务器发送一个HTTP请求,服务器收到后,返回一个HTTP响应,并且在响应中包含了HTML的网页内容,这样,浏览器解析HTML后就可以给用户显示网页了。

这里介绍在前端这一块构造 HTTP 请求的三种方式:

  • 在浏览器上面地址栏输入 URL(构造 GET 请求)
  • 使用 form 表单(可构造 GET 和 POST 请求,还可以用于提交文件)
  • 使用 ajax (可构造各种请求)
  • 通过 Java 代码,基于 Socket 套接字来构造一个 HTTP 请求

接下来,会通过代码以及 fiddler 抓包工具来说明这四种构造 HTTP 请求的方式.

1.在浏览器地址栏处输入 URL

只可以构造 GET 请求

https://www.baidu.com

抓包得到

GET https://mbd.baidu.com/newspage/api/getpcvoicelist?callback=JSONP_0& HTTP/1.1
Host: mbd.baidu.com
Connection: keep-alive
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"
....

可得知发送了一个 GET 请求

2.使用 form 表单构造 HTTP 请求

form 表单只可以构造 GET 和 POST 两种请求

其中有个 action 属性,描述了表单数据提交的 URL 地址

1)构造 GET 请求
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title> form 表单构造 HTTP 请求</title>
</head>
<body>
    <form action="https://www.baidu.com">
        <input type="text" name="userId"><br>
        <input type="text" name="userName"><br>
        <input type="submit" value="提交按钮">
    </form>
</body>
</html>

在这里插入图片描述

如果不指定方法,则默认为 GET

GET https://www.baidu.com/?userId=123&userName=cy HTTP/1.1
Host: www.baidu.com

?后面为键值对,和表单上的数据是一一对应的关系.

2)构造 POST 请求

只需要给 method 属性指定一下方法即可

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title> form 表单构造 HTTP 请求</title>
</head>
<body>
    <form action="https://www.gitee.com" method="POST">
        <input type="text" name="userId"><br>
        <input type="text" name="userName"><br>
        <input type="submit" value="提交按钮">
    </form>
</body>
</html>
POST https://www.gitee.com/ HTTP/1.1
Host: www.gitee.com
Connection: keep-alive
Content-Length: 22
Content-Type: application/x-www-form-urlencoded
....

userId=123&userName=cy

如果给百度提交 POST 数据,则会返回一个错误页面.具体如何得看人家怎么处理.

POST https://www.baidu.com/ HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Content-Length: 22
Content-Type: application/x-www-form-urlencoded
....

userId=123&userName=cy

在这里插入图片描述

GET 和 POST 的区别也就在于数据在哪.

一个是在 query string中,一个则是在 body 中.

3.通过 ajax 构造 HTTP 请求

从前端角度, 除了浏览器地址栏能构造 GET 请求, form 表单能构造 GET 和 POST 之外, 还可以通过 ajax的方式来构造 HTTP 请求. 并且功能更强大

ajax 全称 Asynchronous Javascript And XML, 是 2005 年提出的一种 JavaScript 给服务器发送 HTTP 请求的方式.
特点是可以不需要 刷新页面/页面跳转 就能进行数据传输

在以前,ajax 和服务器之间主要是传输 XML 格式的数据,所以名字里才带 XML.(现在主要传输 JSON)

Asynchronous 指的是"同步",对应的是 Synchronous “异步”

Java 多线程有个关键字 synchronized 也叫同步,但和这里的不是一个意思.写作同步,但意思是互斥.

同步和异步的概念:

同步异步通常用来形容一次方法调用

  • 同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为,是一个等待模式

  • 异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作.

    异步方法通常会在另外一个线程中,“真实”地执行着。整个过程,不会阻碍调用者的工作。

在这里插入图片描述

在网络编程中,数据通过网络传输,如果没有异步机制,则会有很多时间是在进行等待,这无疑效率低下.我们在一边进行等待的时候,可以在另一边进行获取资源.这是异步带来的好处.

1)关于 ajax

ajax 可以在浏览器渲染页面的同时,发起网络请求.

如果没有异步机制,那么浏览器发起网络请求,就会阻塞渲染页面的线程,就会导致页面被卡死

浏览器和服务器的交互过程(引入 ajax 后):

在这里插入图片描述

当浏览器在发送请求的过程中,同时也在进行其他工作(异步),等待服务器返回响应后,浏览器才继续处理响应,而不是一直在干等.

2)引入 ajax
  • ajax 原生的用法,但比较麻烦
  • 第二就是使用第三方库,例如 jQuery,其库已经封装好 ajax 方法供我们使用

jQuery 是 javaScript 中最知名最广泛使用的第三方库之一,对 DOM API 进行了很好的封装,包括其中的 ajax

ajax 原生的用法

TODO

使用第三方库

Java 中可以使用 maven 从中央仓库下载

而在 JS 中,由于 JS 代码都是从服务器被浏览器下载到本地后才执行的,所以就得在网页代码中引入 jQuery 所在的网络地址,就能直接使用

输入图片说明

一般使用 min.js ,即被压缩过的 js代码

输入图片说明

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

直接这样引入,在 js 代码前

或者自己创建个 js 文件,将内容复制到 js 文件中也可以.

注意事项:

ajax 中的"跨域"问题

同源限制: 要求协议名,主机号,端口号相同,否则就是不同域

https://www.baidu.com:80

ajax 为了保证安全性,要求发起 ajax 请求的页面,要和接收 ajax 请求的服务器,在同一个域名/地址下.

如果发起请求的页面,对应的域名和接收 ajax 请求的服务器的域名不相同,则认为是一个跨域请求

解决不可跨域限制

默认情况是不允许"跨域"的

但代理服务器(云服务器)端做了特殊的处理,解开了 ajax 不能跨域的限制(服务器端解开)

服务器 Servlet 代码

@Override
protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 对于 PUT / DELETE 这样的请求 浏览器 会先发一个 OPTIONS 请求进行预检.
// 如果拿到的结果没有允许跨域的头部信息, 则后续的发送就失败了.
    setAccess(resp);
    resp.setStatus(200);
    resp.setContentType("text/plain; charset=utf-8");
    resp.getWriter().write("ajax option 方法");
}

// 配置跨域
private void setAccess(HttpServletResponse resp) {
    resp.setHeader("Access-Control-Allow-Origin", "*");
    resp.setHeader("Access-Control-Allow-Headers", "*");
    resp.setHeader("Access-Control-Allow-Methods", "*");
}
3)构造请求
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>使用 ajax 构造 HTTP 请求</title>
</head>
<body>
    <img src="伊蕾娜.jpg">
    <p>简介:创下日本平成年代收视奇迹的电视剧《半泽直树》续作。本作的故事将承接前作,从半泽被调往东京中央证券任营业企划部长开始。某天,东京中央证券接到了一桩巨额交易。一家大型IT企业企图动用1500亿日元进行恶意收购。这笔交易的规模创造了东京中央证券交易史上的新纪录。此时的半泽还不知道一场新的同银行之间的斗争即将拉开大幕……</p>
    <div id="test">
        这本是一个 div 标签
    </div>
    <style>
        p{
            color: red;
            font-size: 30px;
        }
    </style>
   
    <input type="submit" value="点击构造 GET 请求,获取资源" id="GETfunc">

    <!-- 以上为渲染页面的一些代码 -->
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <!-- 构造 GET 请求 -->
    <script>
        //dom api,获取页面元素,根据 id 选择器获取
        let submit = document.querySelector("#GETfunc");
        submit.onclick = function(){
            $.ajax({
            type:'GET',//请求的方法
            url:'http://42.192.83.143:8089/AjaxMockServer/info',//指定 url ,这里写成绝对路径,也可以写相对路径 /info,根据资源所在的情况而定
            success: function(data,status){
                //data 是响应里的 body,status 是响应里的状态码
                //console.log 将制定内容打印到控制台里
                console.log(data);
                console.log(status);
                //load() 方法从服务器加载数据,并把返回的数据放入被选元素中。
                $('#test').load('http://42.192.83.143:8089/AjaxMockServer/info');
            }
         });
        }
         
    </script>
</body>
</html>

以上是一个 GET 请求,想要提交 POST 请求,直接将 type 改成 POST 即可.

这里是跟点击按钮相关联了,触发点击按钮就触发点击事件.

若直接将 $ajax({}) 放到 <script> 标签里,则是在页面加载的过程中就发送了请求.也

$.ajax({....});

$ 就是 jQuery 中定义好的一个对象(变量),jQuery 中所有的 api 都是 $ 这个对象的方法.

在浏览器加载过程中,是可以同时发起多个 ajax 请求的,此时这多个 ajax 请求相当于是一个并发执行的关系.

4.通过 Java socket 构造 HTTP 请求

发送 HTTP 请求,本质上就是按照 HTTP 的格式往 TCP Socket 中写入一个字符串.

接收 HTTP 响应,本质上就是从 TCP Socket 中读取一个字符串,再按照 HTTP 的格式来进行解析

构造一个简单的 HTTP 客户端,用于发送 HTTP 请求

package may.twenty;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class HttpClient {
    private Socket socket = null;
    private String serverIP;
    private int serverPort;

    public HttpClient(String serverIP, int serverPort) throws IOException {
        this.serverIP = serverIP;
        this.serverPort = serverPort;
        //Socket 与对应 IP 主机上对应的端口进行连接
        this.socket = new Socket(serverIP,serverPort);
    }
    public String get(String url) throws IOException {
        StringBuilder request = new StringBuilder();
        //构造首行
        request.append("GET "+url+" HTTP/1.1\n");
        //构造 header
        request.append("Host: "+serverIP+":"+serverPort+"\n");
        //构造空行
        request.append("\n");
        System.out.println(request.toString());
        InputStream inputStream = socket.getInputStream();
        OutputStream outputStream = socket.getOutputStream();
        //发送 get 请求
        outputStream.write(request.toString().getBytes());
        outputStream.flush();
        //读取服务器返回的响应数据
        byte[] buffer = new byte[4096*4096];
        int n = inputStream.read(buffer);
        return new String(buffer,0,n,"utf-8");
    }
    public String post(String url,String body) throws IOException {
        StringBuilder request = new StringBuilder();
        //构造首行
        request.append("POST "+url+" HTTP/1.1\n");
        //构造header
        request.append("Host: "+serverIP+":"+serverPort+"\n");
        request.append("Content-type: text/plain\n");
        request.append("Content-Length: "+body.getBytes().length+"\n");
        //构造空行
        request.append("\n");
        //构造 body
        request.append(body);
        System.out.println(request);
        OutputStream outputStream = socket.getOutputStream();
        InputStream inputStream = socket.getInputStream();
        //发送请求
        outputStream.write(request.toString().getBytes());
        outputStream.flush();

        //接收响应数据(body)
        byte[] buffer = new byte[4096*4096];
        int n = inputStream.read(buffer);
        return new String(buffer,0,n);
    }

    /**
     * 使用 Java 构造的 HTTP 客户端不再有 "跨域" 限制了, 此时也可以用来获取其他服务器的数据了.
     * 跨域只是浏览器的行为, 对于 ajax 有效. 对于其他语言来说一般都和跨域无关
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        //获取搜狗主页
//        HttpClient client = new HttpClient("www.sogou.com",80);
//        String response = client.get("/index.html");
//        System.out.println(response);

        //重定向到搜狗主页.. 状态码为302的响应
        HttpClient client = new HttpClient("www.sogou.com",80);
        String response = client.get("https://www.sogou.com/");
        System.out.println(response);

        //POST
        HttpClient client1 = new HttpClient("42.192.83.143",8089);
        String getResp = client1.get("/AjaxMockServer/info");
        System.out.println(getResp);
        String postResp = client1.post("/AjaxMockServer/info","这是 body");
        System.out.println(postResp);
    }
}

运行结果:

GET https://www.sogou.com/ HTTP/1.1
Host: www.sogou.com:80


HTTP/1.1 302 Moved Temporarily
Server: nginx
Date: Fri, 20 May 2022 02:38:47 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: ABTEST=0|1653014327|v17; expires=Sun, 19-Jun-22 02:38:47 GMT; path=/
P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"
Location: https://www.sogou.com/
UUID: a4b8ef9c-9030-4742-b0f1-5381b2207d6a

8a
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>nginx</center>
</body>
</html>

0


GET /AjaxMockServer/info HTTP/1.1
Host: 42.192.83.143:8089


HTTP/1.1 200 
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: *
Content-Type: text/plain;charset=utf-8
Content-Length: 15
Date: Fri, 20 May 2022 02:38:47 GMT

ajax get 方法
POST /AjaxMockServer/info HTTP/1.1
Host: 42.192.83.143:8089
Content-type: text/plain
Content-Length: 11

这是 body
HTTP/1.1 200 
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: *
Content-Type: text/plain;charset=utf-8
Content-Length: 16
Date: Fri, 20 May 2022 02:38:47 GMT

ajax post 方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值