REST Web服务出现500错误时,如何获取PUT请求的原始JSON对象?
当然有办法获取到原始的JSON请求体!针对你用的JAX-RS(从配置里的jaxrs:server能看出来是Apache CXF场景),我给你几个实用的解决方案:
方案1:提前用请求过滤器缓存请求体
JAX-RS的请求体默认只能读取一次,所以我们得在请求到达业务接口前就把请求体缓存起来,这样哪怕后续抛出500错误,也能拿到原始内容。
- 实现
ContainerRequestFilter过滤器,读取并缓存请求体:
import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.core.MediaType; import javax.ws.rs.ext.Provider; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @Provider public class RequestBodyCachingFilter implements ContainerRequestFilter { @Override public void filter(ContainerRequestContext requestContext) throws IOException { // 只针对PUT类型的JSON请求处理 if ("PUT".equalsIgnoreCase(requestContext.getMethod()) && requestContext.getMediaType().isCompatible(MediaType.APPLICATION_JSON_TYPE)) { InputStream originalStream = requestContext.getEntityStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = originalStream.read(buffer)) != -1) { baos.write(buffer, 0, bytesRead); } byte[] requestBodyBytes = baos.toByteArray(); // 把缓存的字节流放回上下文,保证后续业务代码能正常读取 requestContext.setEntityStream(new ByteArrayInputStream(requestBodyBytes)); // 将原始JSON字符串存入请求属性,后续异常处理时可以直接获取 String charset = requestContext.getMediaType().getParameters().getOrDefault("charset", "UTF-8"); String rawJson = new String(requestBodyBytes, charset); requestContext.setProperty("RAW_REQUEST_JSON", rawJson); } } }
- 在
application-context.xml里注册这个过滤器:
<jaxrs:server id="VehicleConnectedMileageREST" address="/your-service-path"> <!-- 你的其他接口配置 --> <jaxrs:providers> <bean class="com.your.package.RequestBodyCachingFilter"/> </jaxrs:providers> </jaxrs:server>
方案2:用ExceptionMapper捕获500异常时提取缓存的JSON
接下来实现一个异常处理器,捕获服务器内部错误(500),并取出之前缓存的原始JSON:
import javax.ws.rs.core.Context; import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; @Provider public class GenericExceptionMapper implements ExceptionMapper<Throwable> { @Context private Request request; @Override public Response toResponse(Throwable exception) { // 从请求属性中取出缓存的原始JSON String rawJson = (String) request.getProperty("RAW_REQUEST_JSON"); // 这里可以做日志记录、错误分析等操作 if (rawJson != null) { System.err.println("触发500错误的PUT请求原始JSON:\n" + rawJson); } // 返回标准的500响应,也可以自定义响应内容 return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("服务器内部处理错误") .build(); } }
别忘了把这个异常处理器也注册到jaxrs:providers里:
<jaxrs:providers> <bean class="com.your.package.RequestBodyCachingFilter"/> <bean class="com.your.package.GenericExceptionMapper"/> </jaxrs:providers>
方案3:在业务方法内直接捕获异常处理
如果只需要在updateConnectedVehiclemileage这个特定方法里处理,也可以结合上下文直接在方法内获取缓存的JSON:
import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.core.Context; public class VehicleConnectedMileageApiImpl implements VehicleConnectedMileageApi { @Context private ContainerRequestContext requestContext; @Override public Response updateConnectedVehiclemileage(Vehicle vehicle) throws NotFoundException { String rawJson = (String) requestContext.getProperty("RAW_REQUEST_JSON"); try { // 执行业务逻辑 // ... return Response.ok().build(); } catch (Exception e) { // 发生错误时处理原始JSON if (rawJson != null) { System.err.println("当前错误请求的原始JSON:\n" + rawJson); } throw new RuntimeException("服务器内部处理失败", e); } } }
小提醒
- 如果是其他JAX-RS实现(比如Jersey),核心逻辑完全通用,只是注册过滤器/处理器的方式可能略有不同。
- 如果请求体特别大,缓存到内存可能有性能问题,这时候可以考虑把内容写入临时文件存储。
内容的提问来源于stack exchange,提问作者abderrahim_05
相关产品推荐
相关产品推荐

