1. 请求与响应基础概念解析
在JavaWeb开发中,HTTP请求与响应构成了客户端与服务器交互的基础通信模型。一个完整的请求-响应周期始于客户端(通常是浏览器)向服务器发送HTTP请求,服务器处理请求后返回HTTP响应。这个看似简单的模型背后,隐藏着许多值得深入理解的机制。
HTTP请求主要由三部分组成:
- 请求行:包含请求方法(GET/POST等)、URI和HTTP协议版本
- 请求头:包含客户端环境信息、缓存策略、内容类型等元数据
- 请求体:POST/PUT等方法时携带的实体数据
对应的HTTP响应也包含三个核心部分:
- 状态行:HTTP版本、状态码和状态描述
- 响应头:服务器信息、内容类型、缓存控制等
- 响应体:实际的返回内容(HTML/JSON等)
关键理解:HTTP协议是无状态的,这意味着服务器不会记住之前的请求。会话管理需要通过Cookie/Session等机制实现。
2. Servlet请求处理机制详解
2.1 HttpServletRequest对象解析
HttpServletRequest对象封装了客户端的所有请求信息。开发中最常用的方法包括:
java复制// 获取请求参数
String username = request.getParameter("username");
// 获取请求头
String userAgent = request.getHeader("User-Agent");
// 获取请求URI
String requestURI = request.getRequestURI();
// 获取会话对象
HttpSession session = request.getSession();
参数获取时需要注意字符编码问题。当表单提交包含中文时,需要显式设置编码:
java复制request.setCharacterEncoding("UTF-8");
常见坑点:getParameter()方法只能在请求体尚未被读取时调用一次。多次读取需要自行缓存参数。
2.2 请求转发与包含
请求转发(Forward)是服务器内部的行为,客户端感知不到:
java复制request.getRequestDispatcher("/target.jsp").forward(request, response);
与重定向(Redirect)的关键区别:
- 转发:服务器内部跳转,URL不变,可共享request对象
- 重定向:客户端重新请求,URL变化,request对象不共享
请求包含(Include)用于组合多个Servlet的输出:
java复制request.getRequestDispatcher("/header.jsp").include(request, response);
3. 响应生成与优化技巧
3.1 HttpServletResponse核心方法
设置响应状态和头信息:
java复制response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("text/html;charset=UTF-8");
response.setHeader("Cache-Control", "no-cache");
输出响应体的几种方式:
java复制// 字符流输出
PrintWriter out = response.getWriter();
out.println("<h1>Hello World</h1>");
// 字节流输出(适合文件下载)
OutputStream os = response.getOutputStream();
os.write(fileBytes);
3.2 响应缓存优化
合理设置缓存策略能显著提升性能:
java复制// 静态资源缓存1小时
response.setHeader("Cache-Control", "public, max-age=3600");
// 动态内容禁用缓存
response.setHeader("Cache-Control", "no-store");
ETag验证缓存机制:
java复制String eTag = generateETag(content);
response.setHeader("ETag", eTag);
4. 中文乱码问题全解决方案
4.1 请求乱码处理
POST请求乱码解决方案:
java复制request.setCharacterEncoding("UTF-8");
GET请求乱码需要额外处理(Tomcat 8+已默认UTF-8):
java复制String param = new String(request.getParameter("name").getBytes("ISO-8859-1"), "UTF-8");
或者在server.xml中配置URI编码:
xml复制<Connector URIEncoding="UTF-8" ... />
4.2 响应乱码处理
确保同时设置内容类型和字符编码:
java复制response.setContentType("text/html;charset=UTF-8");
或者分别设置:
java复制response.setCharacterEncoding("UTF-8");
response.setContentType("text/html");
5. 文件上传下载实现
5.1 文件上传处理
使用Apache Commons FileUpload:
java复制DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> items = upload.parseRequest(request);
for (FileItem item : items) {
if (!item.isFormField()) {
String fileName = item.getName();
item.write(new File("/uploads/" + fileName));
}
}
5.2 文件下载实现
设置正确的MIME类型和下载头:
java复制response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
使用缓冲流提高下载性能:
java复制try (InputStream in = new FileInputStream(file);
OutputStream out = response.getOutputStream()) {
byte[] buffer = new byte[4096];
int length;
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
}
6. RESTful风格接口设计
6.1 请求方法语义化
合理使用HTTP方法:
- GET:获取资源
- POST:创建资源
- PUT:更新资源
- DELETE:删除资源
6.2 响应状态码规范
常用状态码:
- 200 OK:成功
- 201 Created:创建成功
- 400 Bad Request:客户端错误
- 401 Unauthorized:未认证
- 404 Not Found:资源不存在
- 500 Internal Server Error:服务器错误
6.3 JSON响应处理
使用Jackson库生成JSON响应:
java复制response.setContentType("application/json");
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.getWriter(), dataObject);
7. 性能优化实战技巧
7.1 响应压缩配置
启用Gzip压缩减少传输量:
java复制if (acceptsGzip(request)) {
response.setHeader("Content-Encoding", "gzip");
OutputStream gzipOut = new GZIPOutputStream(response.getOutputStream());
// 使用gzipOut输出内容
}
7.2 异步处理实现
Servlet 3.0+异步支持:
java复制AsyncContext asyncContext = request.startAsync();
executor.submit(() -> {
// 耗时处理
asyncContext.complete();
});
7.3 连接池优化
数据库连接池配置示例(HikariCP):
java复制HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/db");
config.setUsername("user");
config.setPassword("pass");
config.setMaximumPoolSize(20);
HikariDataSource ds = new HikariDataSource(config);
8. 安全防护最佳实践
8.1 XSS防护
输出编码防御XSS:
java复制String safeOutput = ESAPI.encoder().encodeForHTML(userInput);
或者使用JSTL:
jsp复制<c:out value="${userInput}" />
8.2 CSRF防护
添加CSRF Token:
java复制String token = UUID.randomUUID().toString();
session.setAttribute("csrfToken", token);
request.setAttribute("csrfToken", token);
验证Token:
java复制if (!session.getAttribute("csrfToken").equals(request.getParameter("csrfToken"))) {
throw new SecurityException("CSRF验证失败");
}
8.3 SQL注入防护
使用PreparedStatement:
java复制String sql = "SELECT * FROM users WHERE username = ?";
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setString(1, username);
9. 调试与问题排查
9.1 请求日志记录
打印完整请求信息:
java复制Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
System.out.println(name + ": " + request.getHeader(name));
}
9.2 响应调试技巧
临时关闭缓存方便调试:
java复制response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
9.3 常见问题速查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 中文乱码 | 编码不一致 | 统一使用UTF-8编码 |
| 获取不到POST参数 | 提前读取了输入流 | 确保先setCharacterEncoding |
| 下载文件损坏 | 未正确设置MIME类型 | 检查Content-Type和Content-Disposition |
| 会话丢失 | Cookie未正确设置 | 检查domain/path/secure属性 |
10. 现代框架中的请求响应处理
10.1 Spring MVC处理流程
典型控制器方法:
java复制@PostMapping("/users")
public ResponseEntity<User> createUser(
@RequestBody User user,
@RequestHeader("User-Agent") String userAgent) {
User saved = userService.save(user);
return ResponseEntity.created(URI.create("/users/" + saved.getId())).body(saved);
}
10.2 过滤器与拦截器
日志过滤器示例:
java复制public class LoggingFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
long start = System.currentTimeMillis();
chain.doFilter(req, res);
long duration = System.currentTimeMillis() - start;
log.info("Request took {} ms", duration);
}
}
10.3 响应统一封装
通用响应体设计:
java复制public class ApiResponse<T> {
private int code;
private String message;
private T data;
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(200, "success", data);
}
}
在实际项目中,我发现合理设计请求响应处理层能显著提高系统的可维护性。特别是在微服务架构中,建议为所有API响应设计统一的包装格式,并确保错误处理的一致性。对于性能敏感的场景,要特别注意避免在请求/响应对象中保存大量数据,这会显著增加序列化开销和内存占用。