API前置系统开发流程:3.Json报文的接收和处理

之前做项目的时候,处理json报文,是通过JSONObject实例一个obj对象,通过fromObject方法来获取请求的json字符串,然后再通过obj.getString()方法来获取请求报文中的变量值,这种方法没什么好说的,比较无脑,然后项目过程中就产生了一些问题,让我事后想了一下要如何改进,结果如下。

先来看一下产生的问题:
1.我们做项目时,各对象之间的数据传递通常是使用model对象,所以在获取到json中的数据后,我们要把数据添加到model中,如果按照我之前用的方法,那么就要根据model的定义,每个field一个一个的get set 进去,model小还好,如果很大的话,代码量也是很可观。不想偷懒的程序猿不是好工程狮。
2.在项目联调阶段,client部门发来json报文,按理说我按照报文规范,一个字段一个字段的get set 是不会出现问题的。但是,一上来就obj.getString()就报了空指针,原因是client端的规范是非必输项如果是空值,则字段可以缺省。这一下把我干傻了,这还怎么解析,最后通过沟通,让client端修改的报文规范,所有字段必须传送,即使为空。
那么这件事就一直在我心里,不解决不快啊。

好了话不多说,看一下目前的解决方法。

承接上一篇文章,首先先建立报文接受的server端:

	StringBuffer str = new StringBuffer();
	try {
		BufferedInputStream in = new BufferedInputStream(req.getInputStream());
		int i;
		char c;
		while((i=in.read()) != -1) {
			c = (char)i;
			str.append(c);
		}
	} catch (Exception ex) {
		// TODO: handle exception
		ex.printStackTrace();
	}
	JSONObject obj = JSONObject.fromObject(str.toString());

这样我们就得到了一个json格式的object,顺便说一下JSONObject的maven以来

<dependency>
		<groupId>net.sf.json-lib</groupId>
		<artifactId>json-lib</artifactId>
		<version>2.4</version>
		<classifier>jdk15</classifier>
</dependency>

接下来我们建立一个名字叫做Person的Model

public class Person {

private String name = "";

private String age = "";

private String sex = "";

public String getName() {
	return name;
}

public void setName(String name) {
	this.name = name;
}

public String getAge() {
	return age;
}

public void setAge(String age) {
	this.age = age;
}

public String getSex() {
	return sex;
}

public void setSex(String sex) {
	this.sex = sex;
}

}

下面一段就是我最新的json解析的关键了

在方法中加入如下处理代码

Person person = new Person();
	Field[] fields = person.getClass().getDeclaredFields();
	for(Field field:fields) {
		int mod = field.getModifiers();
		if(Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
			continue;
		}
		field.setAccessible(true);
		try {
			field.set(person,obj.get(field.getName()));
		} catch (IllegalArgumentException | IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	JSONObject resp = JSONObject.fromObject(person);
	String respJson = resp.toString();

整个方法的代码如下:

@RequestMapping(value="/json",produces= {"application/json;charset=UTF-8"})
@ResponseBody
public String dealJson(HttpServletRequest req) {
	
	StringBuffer str = new StringBuffer();
	
	try {
		BufferedInputStream in = new BufferedInputStream(req.getInputStream());
		int i;
		char c;
		while((i=in.read()) != -1) {
			c = (char)i;
			str.append(c);
		}
	} catch (Exception ex) {
		// TODO: handle exception
		ex.printStackTrace();
	}
	JSONObject obj = JSONObject.fromObject(str.toString());
	Person person = new Person();
	Field[] fields = person.getClass().getDeclaredFields();
	for(Field field:fields) {
		int mod = field.getModifiers();
		if(Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
			continue;
		}
		field.setAccessible(true);
		try {
			field.set(person,obj.get(field.getName()));
		} catch (IllegalArgumentException | IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	logger.info(person.getName());
	JSONObject resp = JSONObject.fromObject(person);
	String respJson = resp.toString();
	return respJson;
}

我们来通过postman调用一下,看一下处理结果


得到如下返回结果



可以看到我并没有传送age字段,但是并不影响报文的处理,而且person实力可以通过get方法来直接使用请求报文中的数据了。问题解决了!!

<think>好的,我现在需要详细解释HTTP协议中客户端向服务器请求资源的整个流程,包括服务器内部的处理步骤。首先,我需要回顾自己对HTTP协议的理解,确保流程的每个步骤都是正确的。 首先,客户端发起请求的过程应该包括DNS解析、建立TCP连接、发送HTTP请求、服务器处理请求、返回响应,最后关闭连接。不过用户要求详细说明,特别是服务器内部流程,所以我需要更深入地分解每个部分。 关于DNS解析,客户端如何获取服务器的IP地址?可能需要解释本地缓存、hosts文件、递归查询DNS服务器等步骤。这部分虽然属于前置步骤,但也是整个流程的一部分,是否需要包含呢?用户可能希望了解整个过程,所以应该包括。 接下来是TCP连接的建立,三次握手的过程。需要确认三次握手的步骤是否正确:SYN,SYN-ACK,ACK。这部分需要简明扼要地描述,但用户可能已经有一定基础,所以不需要太详细,但必须准确。 然后是发送HTTP请求,这里可能有不同的HTTP版本,比如HTTP/1.1的持久连接,或者HTTP/2的多路复用。但用户可能希望了解基本流程,所以先以HTTP/1.1为例。请求的结构包括方法、URI、头部、正文(如果有的话)。服务器接收请求后,如何解析处理? 服务器内部处理部分需要详细说明。例如,Web服务器(如Apache、Nginx)如何接收请求,如何根据配置找到对应的处理程序(如静态文件、动态脚本),处理请求时可能涉及的应用服务器(如PHP-FPM、Node.js),数据库查询,生成响应内容等。这里可能需要分步骤说明,比如解析请求头、路由到处理程序、处理业务逻辑、访问数据库、生成响应等。 生成响应后,服务器发送HTTP响应,包括状态码、头部、正文。然后根据是否保持连接,决定是否关闭TCP连接。如果是HTTP/1.1,默认保持连接,但可能会有超时关闭的情况。关闭连接时的四次挥手过程是否需要说明?用户可能想知道整个流程,所以应该简要提及。 此外,是否需要考虑HTTPS的情况?用户的问题是关于HTTP的,所以可能不需要涉及TLS握手,但如果有提到安全连接,可能需要说明,但原问题没有提到,所以暂时忽略。 在思考过程中,还需要确认是否有遗漏的步骤。例如,负载均衡器、缓存服务器等是否属于服务器内部流程的一部分?如果是大型架构的一部分,可能会有,但基础流程中可能不涉及,所以暂时不讨论,除非用户明确要求。 现在需要组织这些步骤,确保逻辑连贯,每个部分都详细且准确。可能的结构如下: 1. DNS解析:客户端将域名转换为IP地址。 2. 建立TCP连接:三次握手。 3. 发送HTTP请求:请求行、头部、正文。 4. 服务器处理请求: a. 接收请求并解析。 b. 路由到对应的处理程序。 c. 处理业务逻辑(如访问数据库)。 d. 生成响应内容。 5. 返回HTTP响应:状态码、头部、正文。 6. 关闭TCP连接:四次挥手(或保持连接)。 在服务器内部流程部分,需要更详细。例如,Web服务器如何根据虚拟主机配置确定使用哪个服务器块,如何处理静态文件请求(直接读取文件),动态请求如何转发给应用服务器,应用服务器执行脚本,查询数据库,生成HTML,返回给Web服务器,再由Web服务器构造HTTP响应。 可能还需要提到中间件,比如日志记录、身份验证、缓存处理等,这些是否属于服务器内部流程的一部分?应该简要提及,以展示服务器处理的多步骤。 需要确保技术术语正确,例如Nginx的server块,反向代理,FastCGI处理PHP请求等,但可能需要用更通用的术语描述,避免特定技术细节,除非必要。 最后,是否需要示例或流程图辅助说明?虽然用户没有要求,但文字描述需要清晰,分步骤说明。 总结思考,整个流程需要覆盖从客户端发起请求到服务器处理并返回响应的每个环节,特别是服务器内部如何解析请求、路由、处理、生成响应。需要确保准确性详细度,同时保持易懂。</think>以下是HTTP协议中客户端向服务器请求资源的详细流程,包括服务器内部处理步骤: -- ### **一、整体流程概览** 1. **DNS解析** → 2. **建立TCP连接** → 3. **发送HTTP请求** → 4. **服务器处理请求** → 5. **返回HTTP响应** → 6. **关闭连接**(或保持复用) --- ### **二、详细步骤解析** #### **1. DNS解析:将域名转换为IP地址** - **客户端本地查询** - 检查浏览器缓存 → 操作系统缓存(如hosts文件) → 本地DNS缓存。 - **递归查询** - 若本地无记录,客户端向配置的DNS服务器(如ISP提供的DNS)发起请求,逐级查询直至根DNS服务器。 - **获取IP地址** - 最终解析出服务器IP地址(例如 `203.0.113.1`)。 #### **2. 建立TCP连接:三次握手** - **第一步(SYN)** 客户端发送SYN报文(`SYN=1, Seq=客户端初始序列号`)。 - **第二步(SYN-ACK)** 服务器返回SYN-ACK报文(`SYN=1, ACK=1, Seq=服务器初始序列号, Ack=客户端序列号+1`)。 - **第三步(ACK)** 客户端发送ACK报文(`ACK=1, Ack=服务器序列号+1`),连接建立完成。 #### **3. 发送HTTP请求** 客户端通过TCP连接发送HTTP报文,格式如下: - **请求行** `方法 URI 协议版本`(例如 `GET /index.html HTTP/1.1`)。 - **请求头** 包含元数据(如 `Host: example.com`, `User-Agent`, `Accept-Language`)。 - **请求体(可选)** 仅某些方法(如POST)包含数据(如表单输入、文件上传)。 #### **4. 服务器内部处理流程** ##### **(1)接收请求并解析** - **监听端口** Web服务器(如Nginx、Apache)监听80端口(HTTP)或443端口(HTTPS)。 - **读取请求** 从TCP连接中读取原始字节流,按HTTP协议解析为结构化请求。 - **解析内容** - 分离请求行、头部、正文。 - 解码URL、处理字符编码(如UTF-8)。 ##### **(2)路由与虚拟主机匹配** - **基于域名路由** 根据请求头中的 `Host` 字段选择对应的虚拟主机配置(如Nginx的 `server` 块)。 - **路径匹配规则** 根据请求URI匹配配置中的路由规则(如 `/static/` 目录指向静态文件,`/api/` 指向后端应用)。 ##### **(3处理请求** - **静态资源请求** - 直接读取文件(如HTML、CSS、图片)并返回。 - 检查缓存头(如 `If-Modified-Since`),若未修改则返回 `304 Not Modified`。 - **动态请求处理** - **转发到应用服务器** 通过FastCGI(PHP)、WSGI(Python)、或反向代理(Node.js)将请求交给后端程序。 - **执行业务逻辑** 应用代码可能查询数据库(如MySQL)、调用外部API、生成动态内容(如用户登录验证)。 - **生成响应内容** 最终生成HTML、JSON等数据。 ##### **(4)中间件与附加处理** - **日志记录** 记录访问日志(如请求时间、状态码、客户端IP)。 - **安全过滤** 检查恶意请求(如SQL注入、XSS攻击)、限流(防止DDoS)。 - **压缩与缓存** - 启用GZIP压缩响应体。 - 添加缓存头(如 `Cache-Control: max-age=3600`)。 #### **5. 返回HTTP响应** 服务器构造响应报文并发送给客户端: - **状态行** `协议版本 状态码 状态描述`(如 `HTTP/1.1 200 OK`)。 - **响应头** 包含元数据(如 `Content-Type: text/html`, `Content-Length`, `Set-Cookie`)。 - **响应体** 实际内容(如HTML页面、JSON数据)。 #### **6. 关闭连接** - **HTTP/1.1默认持久连接** 若未指定 `Connection: close`,TCP连接保持打开以供复用。 - **主动关闭连接** - 服务器或客户端发送 `FIN` 报文,触发四次挥手: 1. 发起方发送FIN。 2. 接收方返回ACK。 3. 接收方发送FIN。 4. 发起方返回ACK。 --- ### **三、流程示意图** ``` 客户端 服务器 |--->| |<--- IP地址 ------| |--->| |<--- SYN-ACK -----| |---- ACK -------->| |---- HTTP请求 --->| | |→ 解析请求 | |→ 路由匹配 | |→ 处理逻辑 | |→ 生成响应 |<-- HTTP响应 ----| |---- FIN -------->| (或保持连接) |<--| | ... | ``` -- ### **四、关键技术点** 1. **无状态协议** HTTP本身不保留会话状态,依赖Cookies或Token维护用户会话。 2. **性能优化** - 连接复用(HTTP/1.1 Keep-Alive、HTTP/2多路复用)。 - CDN加速静态资源分发。 3. **安全性扩展** - HTTPS在TCP握手后增加TLS/SSL加密层(交换密钥、验证证书)。 - 强制使用HSTS头防止降级攻击。 -- ### **五、示例场景** **访问 `http://example.com/about.html`** 1. DNS解析 `example.com` → 得到IP `203.0.113.1`。 2. 客户端与服务器三次握手建立TCP连接。 3. 客户端发送: ``` GET /about.html HTTP/1.1 Host: example.com User-Agent: Chrome/120.0 ``` 4. 服务器: - 匹配虚拟主机配置。 - 读取 `/var/www/html/about.html` 文件。 - 返回: ``` HTTP/1.1 200 OK Content-Type: text/html Content-Length: 1024 <html>...</html> ``` 5. 客户端渲染HTML页面,保持TCP连接以供后续请求。 --- ### **总结** HTTP请求的核心流程围绕 **“建立连接→传输数据→关闭连接”** 展开,服务器内部通过 **解析、路由、业务处理、生成响应** 完成资源交付。理解这一流程是优化Web性能、排查故障的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值