首页 > 编程语言 >55 | JAVA_HTTP编程

55 | JAVA_HTTP编程

时间:2022-09-05 20:00:26浏览次数:84  
标签:JAVA 请求 55 HTTP POST response conn HttpClient

HTTP编程

基础知识

什么是HTTP?HTTP就是目前使用最广泛的Web应用程序使用的基础协议,例如,浏览器访问网站,手机App访问后台服务器,都是通过HTTP协议实现的。

一个完整的HTTP请求-响应如下:

           GET / HTTP/1.1
            Host: www.sina.com.cn
            User-Agent: Mozilla/5 MSIE
            Accept: */*                ┌────────┐
┌─────────┐ Accept-Language: zh-CN,en  │░░░░░░░░│
│O ░░░░░░░│───────────────────────────>├────────┤
├─────────┤<───────────────────────────│░░░░░░░░│
│         │ HTTP/1.1 200 OK            ├────────┤
│         │ Content-Type: text/html    │░░░░░░░░│
└─────────┘ Content-Length: 133251     └────────┘
  Browser   <!DOCTYPE html>              Server
            <html><body>
            <h1>Hello</h1>
            ...
  • 如果是GET请求,那么该HTTP请求只有HTTP Header,没有HTTP Body。如果是POST请求,那么该HTTP请求带有Body,以一个空行分隔。一个典型的带Body的HTTP请求如下:
POST /login HTTP/1.1
Host: www.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30

username=hello&password=123456

POST请求通常要设置Content-Type表示Body的类型,Content-Length表示Body的长度,这样服务器就可以根据请求的Header和Body做出正确的响应。

  • GET请求的参数必须附加在URL上,并以URLEncode方式编码,例如:http://www.example.com/?a=1&b=K%26R,参数分别是a=1b=K&R。因为URL的长度限制,GET请求的参数不能太多,而POST请求的参数就没有长度限制,因为POST请求的参数必须放到Body中。并且,POST请求的参数不一定是URL编码,可以按任意格式编码,只需要在Content-Type中正确设置即可。常见的发送JSON的POST请求如下:
POST /login HTTP/1.1
Content-Type: application/json
Content-Length: 38

{"username":"bob","password":"123456"}
  • HTTP/1.0协议

对于最早期的HTTP/1.0协议,每次发送一个HTTP请求,客户端都需要先创建一个新的TCP连接,然后,收到服务器响应后,关闭这个TCP连接。由于建立TCP连接就比较耗时,因此,为了提高效率,HTTP/1.1协议允许在一个TCP连接中反复发送-响应,这样就能大大提高效率:

                       ┌─────────┐
┌─────────┐            │░░░░░░░░░│
│O ░░░░░░░│            ├─────────┤
├─────────┤            │░░░░░░░░░│
│         │            ├─────────┤
│         │            │░░░░░░░░░│
└─────────┘            └─────────┘
     │      request 1       │
     │─────────────────────>│
     │      response 1      │
     │<─────────────────────│
     │      request 2       │
     │─────────────────────>│
     │      response 2      │
     │<─────────────────────│
     │      request 3       │
     │─────────────────────>│
     │      response 3      │
     │<─────────────────────│
     ▼                      ▼
  • HTTP/2.0

HTTP/2.0允许客户端在没有收到响应的时候,发送多个HTTP请求,服务器返回响应的时候,不一定按顺序返回,只要双方能识别出哪个响应对应哪个请求,就可以做到并行发送和接收:

                       ┌─────────┐
┌─────────┐            │░░░░░░░░░│
│O ░░░░░░░│            ├─────────┤
├─────────┤            │░░░░░░░░░│
│         │            ├─────────┤
│         │            │░░░░░░░░░│
└─────────┘            └─────────┘
     │      request 1       │
     │─────────────────────>│
     │      request 2       │
     │─────────────────────>│
     │      response 1      │
     │<─────────────────────│
     │      request 3       │
     │─────────────────────>│
     │      response 3      │
     │<─────────────────────│
     │      response 2      │
     │<─────────────────────│
     ▼                      ▼

关于客户端的HTTP编程(和爬虫一个道理)

Java标准库提供了基于HTTP的包,但是要注意,早期的JDK版本是通过HttpURLConnection访问HTTP,典型代码如下:

URL url = new URL("http://www.example.com/path/to/target?a=1&b=2");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setUseCaches(false);
conn.setConnectTimeout(5000); // 请求超时5秒
// 设置HTTP头:
conn.setRequestProperty("Accept", "*/*");
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; MSIE 11; Windows NT 5.1)");
// 连接并发送HTTP请求:
conn.connect();
// 判断HTTP响应是否200:
if (conn.getResponseCode() != 200) {
    throw new RuntimeException("bad response");
}		
// 获取所有响应Header:
Map<String, List<String>> map = conn.getHeaderFields();
for (String key : map.keySet()) {
    System.out.println(key + ": " + map.get(key));
}
// 获取响应内容:
InputStream input = conn.getInputStream();
...

上述代码编写比较繁琐,并且需要手动处理InputStream,所以用起来很麻烦。

从Java 11开始,引入了新的HttpClient,它使用链式调用的API,能大大简化HTTP的处理。

我们来看一下如何使用新版的HttpClient。首先需要创建一个全局HttpClient实例,因为HttpClient内部使用线程池优化多个HTTP连接,可以复用:

static HttpClient httpClient = HttpClient.newBuilder().build();

使用GET请求获取文本内容代码如下:

import java.net.URI;
import java.net.http.*;
import java.net.http.HttpClient.Version;
import java.time.Duration;
import java.util.*;

public class Main {
    // 全局HttpClient:
    static HttpClient httpClient = HttpClient.newBuilder().build();

    public static void main(String[] args) throws Exception {
        String url = "https://www.sina.com.cn/";
        HttpRequest request = HttpRequest.newBuilder(new URI(url))
            // 设置Header:
            .header("User-Agent", "Java HttpClient").header("Accept", "*/*")
            // 设置超时:
            .timeout(Duration.ofSeconds(5))
            // 设置版本:
            .version(Version.HTTP_2).build();
        HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
        // HTTP允许重复的Header,因此一个Header可对应多个Value:
        Map<String, List<String>> headers = response.headers().map();
        for (String header : headers.keySet()) {
            System.out.println(header + ": " + headers.get(header).get(0));
        }
        System.out.println(response.body().substring(0, 1024) + "...");
    }
}

如果我们要获取图片这样的二进制内容,只需要把HttpResponse.BodyHandlers.ofString()换成HttpResponse.BodyHandlers.ofByteArray(),就可以获得一个HttpResponse<byte[]>对象。如果响应的内容很大,不希望一次性全部加载到内存,可以使用HttpResponse.BodyHandlers.ofInputStream()获取一个InputStream流。

要使用POST请求,我们要准备好发送的Body数据并正确设置Content-Type

String url = "http://www.example.com/login";
String body = "username=bob&password=123456";
HttpRequest request = HttpRequest.newBuilder(new URI(url))
    // 设置Header:
    .header("Accept", "*/*")
    .header("Content-Type", "application/x-www-form-urlencoded")
    // 设置超时:
    .timeout(Duration.ofSeconds(5))
    // 设置版本:
    .version(Version.HTTP_2)
    // 使用POST并设置Body:
    .POST(BodyPublishers.ofString(body, StandardCharsets.UTF_8)).build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
String s = response.body();

可见发送POST数据也十分简单。

标签:JAVA,请求,55,HTTP,POST,response,conn,HttpClient
From: https://www.cnblogs.com/mmxingye/p/16659381.html

相关文章

  • 56 | JAVA_编写HTTPserver
    编写HTTPServer我们来看一下如何编写HTTPServer。一个HTTPServer本质上是一个TCP服务器,我们先用TCP编程的多线程实现的服务器端框架:publicclassServer{public......
  • 44 | JAVA_IO_Writer(和Reader对应)
    Writer(和Reader对应)Reader是带编码转换器的InputStream,它把byte转换为char,而Writer就是带编码转换器的OutputStream,它把char转换为byte并输出。Writer和OutputStream的......
  • 43 | JAVA_IO_Reader(一种接口,字符流)
    Reader(一种接口,字符流)Reader是Java的IO库提供的另一个输入流接口。和InputStream的区别是,InputStream是一个字节流,即以byte为单位读取,而Reader是一个字符流,即以char为单......
  • 45 | JAVA_IO_ PrintStream 和 PrintWriter
    PrintStream和PrintWriterPrintStream(写到字节流)PrintStream是一种FilterOutputStream,它在OutputStream的接口上,额外提供了一些写入各种数据类型的方法:写入int:prin......
  • 46 | JAVA_IO_使用Files
    使用Files虽然Files是java.nio包里面的类,但他俩封装了很多读写文件的简单方法,例如,我们要把一个文件的全部内容读取为一个byte[],可以这么写:byte[]data=Files.readAllBy......
  • 47 | JAVA_数据库JDBC查询
    JDBC查询导入依赖因为我们选择了MySQL5.x作为数据库,所以我们首先得找一个MySQL的JDBC驱动。所谓JDBC驱动,其实就是一个第三方jar包,我们直接添加一个Maven依赖就可以了:<d......
  • 48 | JAVA_数据库JDBC更新
    JDBC更新数据库操作总结起来就四个字:增删改查,行话叫CRUD:Create,Retrieve,Update和Delete。查就是查询,我们已经讲过了,就是使用PreparedStatement进行各种SELECT,然后处理结果......
  • java获取目路下所有文件及子目录_Java编程获取文件列表及子文件目录的方法(非递归)...
    java获取目路下所有文件及子目录_Java编程获取文件列表及子文件目录的方法(非递归)...废话不谈,直接进入正题,理解见代码注释。//非递归publicListscanFiles(Stringpa......
  • 49 | JAVA_数据库JDBC事务
    JDBC事务数据库事务(Transaction)是由若干个SQL语句构成的一个操作序列,有点类似于Java的synchronized同步。数据库系统保证在一个事务中的所有SQL要么全部执行成功,要么全部......
  • 50 | JAVA_数据库JDBC_批量操作Batch
    JDBCBatch使用JDBC操作数据库的时候,经常会执行一些批量操作。例如,一次性给会员增加可用优惠券若干,我们可以执行以下SQL代码:INSERTINTOcoupons(user_id,type,expir......