HTTP协议简介
HTTP(Hypertext Transfer Protocol)是一种用于传输超文本的应用层协议。它是现代互联网的基础之一,用于在客户端和服务器之间传输数据。在本文中,我们将探讨如何使用Java解析HTTP协议字符串,并将其封装成一个HttpRequest类的过程。
HTTP协议是一种无状态的、请求-响应式的协议,基于文本的,客户端通过发送请求到服务器来获取资源,服务器通过发送响应来回应请求。HTTP请求通常包含请求行、请求头和请求体,而HTTP响应包含响应行、响应头和响应体。

解析HTTP请求的过程
首先,我们需要将接收到的HTTP协议字符串分解为请求行、请求头和请求体等部分。接下来,我们将逐步解析这些部分并提取出有用的信息。
解析请求行
请求行包含了HTTP方法、请求的URL和协议版本。我们可以通过找到第一个换行符来划分请求行,并然后再通过空格来划分方法、URL和协议版本。
ini
import java.util.HashMap;
import java.util.Map;
public class HttpStringParsingDemo {
public static void main(String[] args) {
// 带路径参数和查询字符串参数的HTTP请求字符串
String httpRequestString = "GET /api/user/1/blogs?page=1&size=10 HTTP/1.1\r\n" +
"Host: www.juejin.cn\r\n" +
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)\r\n" +
"Accept: text/html,application/xhtml+xml\r\n\r\n";
// 将请求字符串分割成行
String[] requestLines = httpRequestString.split("\r?\n");
// 将第一行(请求行)再次分割成各部分
String[] requestLineParts = requestLines[0].split(" ");
String method = requestLineParts[0];
String fullUrl = requestLineParts[1];
String protocolVersion = requestLineParts[2];
// 解析URL,提取路径和查询字符串
String[] urlParts = fullUrl.split("\?");
String path = urlParts[0];
String queryString = urlParts.length > 1 ? urlParts[1] : "";
// 提取路径参数
Map<String, String> pathParams = new HashMap<>();
String[] pathSegments = path.split("/");
if (pathSegments.length > 3) { // Assuming at least 3 segments (/api/user/{user_id}/blogs)
pathParams.put("user_id", pathSegments[3]);
}
// 提取查询字符串参数
Map<String, String> queryParams = new HashMap<>();
String[] querySegments = queryString.split("&");
for (String querySegment : querySegments) {
String[] keyValue = querySegment.split("=");
if (keyValue.length == 2) {
queryParams.put(keyValue[0], keyValue[1]);
}
}
// 打印提取的信息
System.out.println("HTTP方法: " + method);
System.out.println("路径: " + path);
System.out.println("路径参数: " + pathParams);
System.out.println("查询字符串: " + queryParams);
System.out.println("协议版本: " + protocolVersion);
}
}
>>> out
HTTP方法: GET
路径: /api/user/1/blogs
路径参数: {user_id=1}
查询字符串: {size=10, page=1}
协议版本: HTTP/1.1
我这里的路径参数 user_id 是硬写上去的,web框架处理路径参数一般通过正则来匹配,例如java的一些注解 @PathVariable("user_id") Integer user_id
提取出来
解析请求头:
请求头包含了多个键值对,每个键值对表示一个请求头字段和对应的值。我们可以循环遍历请求行之后的每一行,通过找到冒号来划分字段名和字段值。
ini
import java.util.HashMap;
import java.util.Map;
public class HttpHeaderParsingDemo {
public static void main(String[] args) {
// 带请求头的HTTP请求字符串
String httpRequestString = "GET /headers HTTP/1.1\r\n" +
"Host: www.juejin.cn\r\n" +
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)\r\n" +
"Accept: text/html,application/xhtml+xml\r\n\r\n";
// 将请求字符串分割成行
String[] requestLines = httpRequestString.split("\r?\n");
// 解析请求头字段
Map<String, String> headers = new HashMap<>();
for (int i = 1; i < requestLines.length; i++) {
String[] headerParts = requestLines[i].split(": ");
if (headerParts.length == 2) {
String headerName = headerParts[0];
String headerValue = headerParts[1];
headers.put(headerName, headerValue);
}
}
// 打印提取的请求头信息
for (Map.Entry<String, String> entry : headers.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
>>> out
Accept: text/html,application/xhtml+xml
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Host: www.juejin.cn
请求头信息也是通过 HashMap 来存储
解析请求体:
如果是POST请求等包含请求体的请求,我们可以从请求体部分提取出数据。这部分通常需要根据请求头中的Content-Length来确定请求体的长度,然后读取相应长度的数据。
swift
import com.alibaba.fastjson.JSON;
import java.util.Map;
import java.util.HashMap;
public class HttpReqBodyDemo {
public static void main(String[] args) {
// 带JSON请求体的HTTP POST请求字符串
String httpRequestString = "POST /user/login HTTP/1.1\r\n" +
"Host: www.juejin.cn\r\n" +
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)\r\n" +
"Content-Length: 22\r\n" +
"Content-Type: application/json\r\n\r\n" +
"{"username":"xiao","password":"123"}";
// 将请求字符串分割成行
String[] requestLines = httpRequestString.split("\r?\n");
// 解析请求头字段
// 这里可以参考之前示例中的方法解析请求头
// 解析请求体
String requestBody = "";
if (headers.containsKey("Content-Length")) {
int contentLength = Integer.parseInt(headers.get("Content-Length"));
requestBody = requestLines[requestLines.length - 1];
}
// 解析JSON数据并封装成Map
Map<String,String> map = (Map<String, String>) JSON.parse(requestBody);
// 打印封装的Map数据
for (Map.Entry<String, String> entry : jsonMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
当然要根据不同的Content-Type分别处理,这里就简单处理个Json数据,毕竟也比较常用。
封装成HttpRequest类
现在我们已经成功地从HTTP协议字符串中解析出了请求行、请求头和请求体等信息。接下来,我们可以将这些信息封装成一个HttpRequest类,以便更方便地使用和处理。
typescript
import com.alibaba.fastjson.JSON;
import java.util.Map;
public class HttpRequest {
private String method;
private String url;
private String protocolVersion;
private Map<String, String> headers;
private Map<String, String> queryParameters;
private Map<String, String> requestBody;
public HttpRequest(String method, String url, String protocolVersion,
Map<String, String> headers, Map<String, String> queryParameters,
Map<String, String> requestBody) {
this.method = method;
this.url = url;
this.protocolVersion = protocolVersion;
this.headers = headers;
this.queryParameters = queryParameters;
this.requestBody = requestBody;
}
public String getMethod() {
return method;
}
public String getUrl() {
return url;
}
public String getProtocolVersion() {
return protocolVersion;
}
public Map<String, String> getHeaders() {
return headers;
}
public Map<String, String> getQueryParameters() {
return queryParameters;
}
public Map<String, String> getRequestBody() {
return requestBody;
}
}
ini
import com.alibaba.fastjson.JSON;
import java.util.Map;
public class HttpRequestParsingDemo {
public static void main(String[] args) {
// HTTP请求字符串
String httpRequestString = "POST /submit?test=xiao&code=hui HTTP/1.1\r\n" +
"Host: www.juejin.cn\r\n" +
"User-Agent: Mozilla/5.0\r\n" +
"Content-Length: 27\r\n" +
"Content-Type: application/json\r\n" +
"\r\n" +
"{"username":"xiao","age":20}";
// 将请求字符串分割成行
String[] requestLines = httpRequestString.split("\r?\n");
// 解析请求行
String[] requestLineParts = requestLines[0].split(" ");
String method = requestLineParts[0];
String url = requestLineParts[1];
String protocolVersion = requestLineParts[2];
// 解析请求头部分
Map<String, String> headers = new HashMap<>();
for (int i = 1; i < requestLines.length; i++) {
String[] headerParts = requestLines[i].split(": ");
if (headerParts.length == 2) {
String headerName = headerParts[0];
String headerValue = headerParts[1];
headers.put(headerName, headerValue);
}
}
// 解析查询字符串参数
Map<String, String> queryParams = new HashMap<>();
String[] querySegments = queryString.split("&");
for (String querySegment : querySegments) {
String[] keyValue = querySegment.split("=");
if (keyValue.length == 2) {
queryParams.put(keyValue[0], keyValue[1]);
}
}
// 解析JSON请求体
String requestBody = "";
if (headers.containsKey("Content-Length")) {
int contentLength = Integer.parseInt(headers.get("Content-Length"));
requestBody = requestLines[requestLines.length - 1];
}
// 解析JSON数据并封装成Map
Map<String,String> requestBody = (Map<String, String>) JSON.parse(requestBody);
// 创建HttpRequest对象
HttpRequest httpRequest = new HttpRequest(
method,
url,
protocolVersion,
headers,
queryParams,
requestBody)
);
// 打印HttpRequest对象中的信息
System.out.println("HTTP方法: " + httpRequest.getMethod());
System.out.println("URL: " + httpRequest.getUrl());
System.out.println("协议版本: " + httpRequest.getProtocolVersion());
System.out.println("请求头部分: " + httpRequest.getHeaders());
System.out.println("查询字符串参数: " + httpRequest.getQueryParameters());
System.out.println("JSON请求体: " + httpRequest.getRequestBody());
}
}
总结
在本文中,我们探讨了如何使用Java解析HTTP协议字符串,并将其封装成了一个HttpRequest类。通过逐步解析请求行、请求头和请求体,我们能够从协议字符串中提取出有用的信息,并将其封装成一个类对象,以便更方便地使用和处理。这种方法可以帮助开发人员在处理HTTP请求时更加灵活和高效,为构建Web应用程序和服务器端处理提供了基础。虽然是一个简单的字符串解析封装,但通过深入理解HTTP协议的解析过程,我们能够更好地理解互联网信息交互原理。