使用 @ControllerAdvice 和 实现ResponseBodyAdvice接口, 拦截Controller方法默认返回参数,统一处理返回值/响应体

x33g5p2x  于2021-11-25 转载在 其他  
字(6.8k)|赞(0)|评价(0)|浏览(695)

1、Controller代码
以下是Controller方法源码:

  1. @RestController
  2. @RequestMapping("/manage/user")
  3. public class TestController {
  4. private Logger logger = LoggerFactory.getLogger(TestController.class);
  5. /** * 通过会员id获取会员信息及详情 * * @param id 会员id * @return app msg */
  6. @GetMapping("/edit/{id}")
  7. public AppMessage edit(@PathVariable Integer id) {
  8. if (null == id) {
  9. return AppMessage.error(-3, "会员ID不能为空");
  10. }
  11. // 查询会员对象
  12. User user = userService.queryById(id);
  13. if (user == null) {
  14. return AppMessage.error(-3, "根据会员ID未查询到对应会员");
  15. }
  16. return AppMessage.success(user);
  17. }
  18. }

2、拦截处理器
下面已经封装好了一个拦截处理类以供参考:

  1. package com.yclimb.test;
  2. import org.springframework.core.MethodParameter;
  3. import org.springframework.http.MediaType;
  4. import org.springframework.http.server.ServerHttpRequest;
  5. import org.springframework.http.server.ServerHttpResponse;
  6. import org.springframework.web.bind.annotation.ControllerAdvice;
  7. import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
  8. /** * 拦截Controller方法默认返回参数,统一处理返回值/响应体 */
  9. @ControllerAdvice
  10. public class TestResponseBodyAdvice implements ResponseBodyAdvice {
  11. @Override
  12. public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
  13. System.out.println("TestResponseBodyAdvice==>beforeBodyWrite:" + o.toString() + ","
  14. + methodParameter);
  15. return o;
  16. }
  17. @Override
  18. public boolean supports(MethodParameter methodParameter, Class aClass) {
  19. return true;
  20. }
  21. }

ResponseBodyAdvice

方法讲解

  • supports对你需要进行拦截的response进行判断筛选,返回true则进行拦截反之放行,
  • beforeBodyWrite对supports进行拦截的response进行处理,封装你需要的类型参数,加密等等。

个人理解:

ResponseBodyAdvice 接口是在 Controller 执行 return 之后,在 response 返回给客户端之前,执行的对 response 的一些处理,可以实现对 response 数据的一些统一封装或者加密等操作。

该接口一共有两个方法:

(1)supports —— 判断是否要执行beforeBodyWrite方法,true为执行,false不执行 —— 通过supports方法,我们可以选择哪些类或哪些方法要对response进行处理,其余的则不处理。

(2)beforeBodyWrite —— 对 response 处理的具体执行方法。

  1. package com.abc.demo;
  2. import com.github.pagehelper.Page;
  3. import com.abc.pojo.bean.ResponseResult;
  4. import org.springframework.core.MethodParameter;
  5. import org.springframework.http.MediaType;
  6. import org.springframework.http.converter.HttpMessageConverter;
  7. import org.springframework.http.server.ServerHttpRequest;
  8. import org.springframework.http.server.ServerHttpResponse;
  9. import org.springframework.web.bind.annotation.ControllerAdvice;
  10. import org.springframework.web.bind.annotation.ExceptionHandler;
  11. import org.springframework.web.bind.annotation.ResponseBody;
  12. import org.springframework.web.bind.annotation.RestController;
  13. import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
  14. import javax.servlet.http.HttpServletRequest;
  15. import javax.servlet.http.HttpServletResponse;
  16. @ControllerAdvice
  17. public class ControllerAdviceHandler implements ResponseBodyAdvice<Object> {
  18. @ResponseBody
  19. @ExceptionHandler(value = Throwable.class)
  20. public ResponseResult catchAllError(HttpServletRequest request, HttpServletResponse response, Throwable error) {
  21. return ResponseResult.exception(error);
  22. }
  23. @Override
  24. public boolean supports(MethodParameter method, Class<? extends HttpMessageConverter<?>> clazz) {
  25. return method.getContainingClass().getPackage().getName().startsWith("com.abc") &&
  26. (method.getContainingClass().getAnnotation(RestController.class) != null ||
  27. (method.getMethod() != null && method.getMethod().getAnnotation(ResponseBody.class) != null));
  28. }
  29. @Override
  30. public Object beforeBodyWrite(Object result, MethodParameter methodParameter, MediaType mediaType,
  31. Class<? extends HttpMessageConverter<?>> aClass,
  32. ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
  33. return result instanceof ResponseResult ? result :
  34. result instanceof Page ? ResponseResult.page((Page<?>) result) :
  35. ResponseResult.successData(result);
  36. }
  37. }

实例

有一个Controller类,返回参数为OutputObject,我们通过ResponseBodyAdvice,对该类的所有方法返回的OutputObject中的部分数据进行统一加密处理。

  1. // 对响应报文统一处理,对bean内容进行加密
  2. @Component
  3. //声明该类要处理的包路径
  4. @ControllerAdvice("com.cmos.edcreg.web.controller")
  5. public class ResponseAdvice implements ResponseBodyAdvice {
  6. private final Logger logger = LoggerFactory.getLogger(ResponseAdvice.class);
  7. // 对response处理的具体方法
  8. @Override
  9. public Object beforeBodyWrite(Object arg0, MethodParameter arg1,
  10. MediaType arg2, Class arg3, ServerHttpRequest arg4,
  11. ServerHttpResponse arg5) {
  12. OutputObject out = new OutputObject();
  13. try {
  14. //arg0转换为OutputObject类型
  15. ObjectMapper objectMapper=new ObjectMapper();
  16. out = objectMapper.readValue(org.json.JSONObject.valueToString(arg0), OutputObject.class);
  17. //获取加密密钥
  18. String oldEncryptKey = out.getBean().get("oldEncryptKey");
  19. //获取加密字符串
  20. DesSpecial des = new DesSpecial();
  21. String encryptData = des.strEnc(JSON.toJSONString(out.getBean()), oldEncryptKey, null, null);
  22. //封装数据(清除原来数据,放入加密数据)
  23. out.getBean().clear();
  24. out.getBean().put("data", encryptData);
  25. return out;
  26. } catch (Exception e) {
  27. logger.error("返回报文处理出错", e);
  28. out.setReturnCode(ReturnInfoEnums.PROCESS_ERROR.getCode());
  29. out.setReturnMessage(ReturnInfoEnums.PROCESS_ERROR.getMessage());
  30. return out;
  31. }
  32. }
  33. /* 选择哪些类,或哪些方法需要走beforeBodyWrite * 从arg0中可以获取方法名和类名 * arg0.getMethod().getDeclaringClass().getName()为获取方法名 */
  34. @Override
  35. public boolean supports(MethodParameter arg0, Class arg1) {
  36. return "com.cmos.edcreg.web.controller.GdH5AppointmentActiveVideoNewController".equals(arg0.getMethod().getDeclaringClass().getName());
  37. }
  38. }

在spring项目开发过程中的应用场景

对controller层返回值进行修改增强处理。比如返回值5,需要封装成:{“code”:“0”,“data”:5,“msg”:“success”} 格式返回前端

1、controller层业务代码

  1. @RestController //此注解包含@ResponseBody注解
  2. @RequestMapping("/mp")
  3. public class ResponseBodyAdviceController {
  4. @RequestMapping(value = "/hello", method = RequestMethod.GET)
  5. public int hello() {
  6. return 5;
  7. }
  8. }

2、实现ResponseBodyAdvice接口的切面类

  1. /** *此注解针对controller层的类做增强功能,即对加了@RestController注解的类进行处理 */
  2. @ControllerAdvice(annotations = RestController.class)
  3. public class RestResultWrapper implements ResponseBodyAdvice<Object> {
  4. @Override
  5. public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
  6. return true;
  7. }
  8. @Override
  9. public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
  10. ServerHttpResponse response) {
  11. //定义一个统一的返回类
  12. RestResult responseResult = new RestResult( "0", body, "success");
  13. //如果handler处理类的返回类型是String(即控制层的返回值类型),为了保证一致性,这里需要将ResponseResult转回去
  14. if(body instanceof String) {
  15. return JSON.toJSONString(responseResult);
  16. }
  17. //封装后的数据返回到前端页面
  18. return JSONObject.toJSON(responseResult);
  19. }
  20. }

3、返回公共类的创建

  1. // 统一返回Rest风格的数据结构
  2. public class RestResult<T> implements Serializable {
  3. private String code = "2000";
  4. // 成功时返回的数据,失败时返回具体的异常信息
  5. private T data;
  6. // 请求失败返回的提示信息,给前端进行页面展示的信息
  7. private String message ;
  8. public RestResult() {
  9. }
  10. @Override
  11. public String toString() {
  12. return "RestResult{" +
  13. "code='" + code + '\'' +
  14. ", data=" + data +
  15. ", message=" + message +
  16. '}';
  17. }
  18. public RestResult(String code, T data, String message) {
  19. this.code = code;
  20. this.data = data;
  21. this.message = message;
  22. }
  23. }

相关文章