问题描述
在controller层,通过实现HandlerMethodReturnValueHandler类,拦截返回数据,并统一处理。
public class AutoResultReturnValueHandler implements HandlerMethodReturnValueHandler {
private static Logger logger = LoggerFactory.getLogger(AutoResultReturnValueHandler.class);
public AutoResultReturnValueHandler() {
}
// 判断是否支持返回的类型
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (isRestController(returnType) || (isController(returnType) && isResponseBody(returnType))) && isAutoResult(returnType);
}
/**
* 处理返回响应数据,将返回的内容封装成ResponseResult结果返回
* @Author wangwei
* @Date 2024/5/11
*/
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(HttpServletRequest.class);
mavContainer.setRequestHandled(true);
HttpServletResponse response = (HttpServletResponse) webRequest.getNativeResponse(HttpServletResponse.class);
if (returnValue instanceof ResponseResult) {
((ResponseResult<?>) returnValue).setRequestId(requestId);
writeResponse(request, response, info, returnValue);
return;
}
ResponseResult<Object> result = new ResponseResult<>();
result.setData(returnValue);
result.setRequestId(requestId);
writeResponse(request, response, info, result);
}
private boolean isRestController(MethodParameter returnType) {
RestController annotation = (RestController) returnType.getDeclaringClass().getAnnotation(RestController.class);
return annotation != null;
}
private boolean isController(MethodParameter returnType) {
Controller annotation = returnType.getDeclaringClass().getAnnotation(Controller.class);
return annotation != null;
}
private boolean isResponseBody(MethodParameter returnType) {
ResponseBody methodAnnotation = (ResponseBody) returnType.getMethodAnnotation(ResponseBody.class);
return methodAnnotation != null;
}
private boolean isAutoResult(MethodParameter returnType) {
AutoResult methodAnnotation = (AutoResult) returnType.getMethodAnnotation(AutoResult.class);
if (methodAnnotation != null) {
return methodAnnotation.value();
} else {
AutoResult annotation = (AutoResult) returnType.getDeclaringClass().getAnnotation(AutoResult.class);
return annotation != null && annotation.value();
}
}
}
最后的返回结果如下:
{
"data": "hello ice",
"errorCode": "0",
"errorMsg": "成功",
"requestId": "f53e5275452942d58d18a340c7fd9f1b",
"success": true
}
这里不是讲解HandlerMethodReturnValueHandler的用法。
但是当Controller写出如下数据
// AutoResult注解,用于标注当前信息是否处理成封装结果
@AutoResult
@RequestMapping(value = "/test1", method = RequestMethod.POST)
public void test1(HttpServletResponse response) {
System.out.println("测速");
}
本想要的结构是
{
"errorCode": "0",
"errorMsg": "成功",
}
但是什么都没有返回,这种并不自己想要的结果。虽然知道去掉了HttpServletResponse response请求参数,就得到自己想要的结果,但还是想明白其中的原理和出现问题的地方是哪里
如何处理问题
一般是通过断点调试,追踪关键节点的方式找到问题的答案。
在controller层所请求的方法处打上断点,并且在AutoResultReturnValueHandler的handleReturnValue方法处打上断点,查看执行到这个地方时,调用了哪些方法
在请求调用中,具体执行方式是在
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
出
如果走到下一步,此处数据返回为null,又因为下一处判断为真,则直接返回了
了解到了mavContainer.isRequestHandled()返回是为真的,但是它的默认值是false,那么一定有一个地方是处理为真了,那么在setRequestHandled(boolean requestHandled) 出打上断点,查看那个地方设置为true,
通过断点,发现了在ServletResponseMethodArgumentResolver,处理参数时设置了
此时大概猜到了,我们传入的HttpServletResponse参数时,调用了该方法,导致了设置为true,如果去掉,则正常。
根据一些经验,我们得出:传入了参数HttpServletResponse时,Spring框架默认是用户将结果直接写入到了HttpServletResponse,并不需要后续handleReturnValue的处理。
总结
找问题的经验,在请求的地方打印断点,然后追踪各个请求方法,然后在关键节点出反复调试,根据经验猜对应的结果。