拦截器中优雅接收JSON对象的完整指南
在Java Web开发中,拦截器(Interceptor)是处理请求前后逻辑的重要组件,当需要拦截并处理JSON格式的请求体时,如何正确接收和解析JSON对象成为关键问题,本文将详细介绍在拦截器中接收JSON对象的多种方法及最佳实践。
核心原理:获取请求流并解析
拦截器本质上是一个过滤器,可以通过HttpServletRequest对象获取请求体数据,JSON数据通常以POST请求的形式通过application/json Content-Type发送,因此核心步骤是:
- 获取请求输入流
- 读取流数据
- 使用JSON库解析为对象
具体实现方案
使用Jackson(Spring Boot默认)
@Component
public class JsonInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 只处理POST/PUT等包含请求体的方法
if ("POST".equals(request.getMethod()) || "PUT".equals(request.getMethod())) {
// 获取请求输入流
ServletInputStream inputStream = request.getInputStream();
// 使用Jackson解析
ObjectMapper objectMapper = new ObjectMapper();
try {
// 直接读取为Map或自定义对象
Map<String, Object> jsonMap = objectMapper.readValue(inputStream, new TypeReference<Map<String, Object>>() {});
// 将解析后的JSON存入request属性,供后续Controller使用
request.setAttribute("jsonData", jsonMap);
// 示例:解析为自定义DTO
UserDTO user = objectMapper.readValue(inputStream, UserDTO.class);
request.setAttribute("userDto", user);
} catch (IOException e) {
throw new RuntimeException("JSON解析失败", e);
}
}
return true;
}
}
使用Spring的@RequestBody结合拦截器
更推荐的方式是让Spring自动解析JSON,拦截器直接处理已解析的对象:
@Component
public class JsonBodyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 检查是否是带有@RequestBody的方法
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
MethodParameter[] parameters = handlerMethod.getMethodParameters();
for (MethodParameter parameter : parameters) {
if (parameter.hasParameterAnnotation(RequestBody.class)) {
// Spring已经解析了JSON,可以通过getAttribute获取
Object requestBody = request.getAttribute("org.springframework.web.servlet.HandlerMapping.readableRequestBody");
if (requestBody != null) {
// 处理已解析的JSON对象
processJsonBody(requestBody);
}
break;
}
}
}
return true;
}
private void processJsonBody(Object jsonBody) {
// 业务逻辑处理
}
}
使用ContentCachingRequestWrapper
对于需要多次读取请求体的场景:
@Component
public class CachingJsonInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 包装请求以支持多次读取
if (!(request instanceof ContentCachingRequestWrapper)) {
request = new ContentCachingRequestWrapper(request);
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 在请求完成后处理缓存的内容
if (request instanceof ContentCachingRequestWrapper) {
ContentCachingRequestWrapper cachingRequest = (ContentCachingRequestWrapper) request;
byte[] buf = cachingRequest.getContentAsByteArray();
if (buf.length > 0) {
String jsonBody = new String(buf, StandardCharsets.UTF_8);
// 解析并处理JSON
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> data = mapper.readValue(jsonBody, new TypeReference<Map<String, Object>>() {});
// 业务逻辑
}
}
}
}
关键注意事项
-
Content-Type检查:确保请求头包含
application/jsonString contentType = request.getContentType(); if (contentType != null && contentType.contains("application/json")) { // 处理JSON } -
流关闭问题:使用
try-with-resources确保流正确关闭try (ServletInputStream inputStream = request.getInputStream()) { // 解析逻辑 } -
字符编码:明确指定UTF-8编码
request.setCharacterEncoding("UTF-8"); -
性能考虑:避免在拦截器中进行复杂的JSON解析,可以结合Spring的
HttpMessageConverter机制
最佳实践建议
-
优先使用Spring的自动解析:让Spring的
MappingJackson2HttpMessageConverter处理JSON解析,拦截器专注于业务逻辑 -
自定义注解:结合自定义注解实现更灵活的拦截控制
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface JsonProcess { } -
异常处理:在拦截器中添加适当的异常处理机制
@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { if (ex != null) { // 处理JSON解析异常 } }
完整示例
@Component
public class JsonProcessingInterceptor implements HandlerInterceptor {
private final ObjectMapper objectMapper;
public JsonProcessingInterceptor(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (shouldProcessJson(request)) {
try {
JsonNode jsonNode = objectMapper.readTree(request.getInputStream());
request.setAttribute("parsedJson", jsonNode);
// 转换为特定DTO
if (jsonNode.has("user")) {
UserDTO user = objectMapper.treeToValue(jsonNode.get("user"), UserDTO.class);
request.setAttribute("userDto", user);
}
} catch (IOException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid JSON format");
}
}
return true;
}
private boolean shouldProcessJson(HttpServletRequest request) {
return "POST".equals(request.getMethod()) ||
"PUT".equals(request.getMethod()) ||
(request.getContentType() != null && request.getContentType().contains("application/json"));
}
}
通过以上方法,你可以在拦截器中灵活地接收和处理JSON对象,同时保持代码的健壮性和可维护性,根据项目具体需求选择最适合的实现方案,并注意处理各种边界情况。



还没有评论,来说两句吧...