简介

现在项目中大部分采用前后端分离的架构,采用这种架构的项目,在返回数据时,几乎都是采用返回 json 格式的数据。而 spring 中返回 json 格式的数据一般采用 @RestController 或者 @ResponseBody 注解。代码样例

@ResponseBody
@RequestMapping("/reqBody")
public ResultInfo<Map<String, Object>> reqBody(){
ResultInfo<Map<String, Object>> resultInfo = new ResultInfo<>();
resultInfo.setCode(200);
resultInfo.setMessage("success"); Map<String, Object> map = new HashMap<>();
map.put("userId", 100);
map.put("tenantId", 1001);
map.put("userName", "bug弄潮儿");
resultInfo.setBody(map); return resultInfo;
}

今天定义一个注解读返回的 json 进行加密,来运用 HandlerMethodReturnValueHandler

pom.xml 文件引入依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.olive</groupId>
<artifactId>springmvc-response-body</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>springmvc-response-body</name>
<url>http://maven.apache.org</url> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.14</version>
<relativePath /> <!-- lookup parent from repository -->
</parent> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.14</version>
</dependency> </dependencies>
</project>

定义加密注解

package com.olive.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Encrypted { boolean value() default true;
}

Encrypted 注解,该注解是一个标识注解;如果打上该注解标识加密

统一返回定义

主要包含 code、message 和 body 属性定义

package com.olive.dto;

import lombok.Data;

import java.io.Serializable;

@Data
public class ResultInfo<T> implements Serializable { public int code; public String message; private T body; private boolean encrypt; }

自定义 ResponseBodyHandler

该类实现 HandlerMethodReturnValueHandler 类,主要对 @RestController 或者 @ResponseBody 注解进行解析

package com.olive.config;

import com.alibaba.fastjson2.JSON;
import com.olive.annotation.Encrypted;
import com.olive.dto.ResultInfo;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer; import java.nio.charset.StandardCharsets; public class ResponseBodyHandler implements HandlerMethodReturnValueHandler { protected final HandlerMethodReturnValueHandler handlerMethodReturnValueHandler; public ResponseBodyHandler(HandlerMethodReturnValueHandler handlerMethodReturnValueHandler){
this.handlerMethodReturnValueHandler = handlerMethodReturnValueHandler;
} @Override
public boolean supportsReturnType(MethodParameter returnType) {
//如果被@ResponseBody注解修饰的 返回true
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class))
&& returnType.hasMethodAnnotation(Encrypted.class);
} @Override
public void handleReturnValue(Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
if(returnValue instanceof ResultInfo){
ResultInfo<?> resultInfo = (ResultInfo<?>)returnValue;
ResultInfo<String> newResultInfo = new ResultInfo<>();
newResultInfo.setCode(resultInfo.getCode());
newResultInfo.setMessage(resultInfo.getMessage());
newResultInfo.setEncrypt(true);
newResultInfo.setBody(Base64Utils.encodeToString(JSON.toJSONString(resultInfo.getBody()).getBytes(StandardCharsets.UTF_8)));
//ResponseBody注解执行器
handlerMethodReturnValueHandler.handleReturnValue(newResultInfo,
returnType, mavContainer, webRequest);
}else{
handlerMethodReturnValueHandler.handleReturnValue(returnValue,
returnType, mavContainer, webRequest);
}
}
}

注册 ResponseBodyHandler 到 controller 返回值处理器里,即添加自己的返回值处理器

package com.olive.config;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor; import java.util.ArrayList;
import java.util.List; @Configuration
public class WebConfig implements InitializingBean { @Autowired
private RequestMappingHandlerAdapter adapter; @Override
public void afterPropertiesSet() throws Exception {
List<HandlerMethodReturnValueHandler> unmodifiableList = adapter.getReturnValueHandlers();
List<HandlerMethodReturnValueHandler> list = new ArrayList<>(unmodifiableList.size());
for (HandlerMethodReturnValueHandler returnValueHandler : unmodifiableList) {
if (returnValueHandler instanceof RequestResponseBodyMethodProcessor) {
//将RequestResponseBodyMethodProcessor 实际返回值替换为自定义的,实际执行为RequestResponseBodyMethodProcessor
//重要
HandlerMethodReturnValueHandler handler = new ResponseBodyHandler(returnValueHandler);
list.add(handler);
} else {
list.add(returnValueHandler);
}
}
adapter.setReturnValueHandlers(list);
}
}

测试

编写 Springboot 启动引导类

package com.olive;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; /**
* https://cloud.tencent.com/developer/article/1616704
*
* @author 2230
*
*/
@SpringBootApplication
public class Application { public static void main(String[] args) {
SpringApplication.run(Application.class);
} }

编写测试 Controller

package com.olive.controller;

import java.util.HashMap;
import java.util.Map; import com.olive.annotation.Encrypted;
import com.olive.dto.ResultInfo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; @RestController
public class TestController { @Encrypted
@RequestMapping("/reqBody")
public ResultInfo<Map<String, Object>> reqBody(){
ResultInfo<Map<String, Object>> resultInfo = new ResultInfo<>();
resultInfo.setCode(200);
resultInfo.setMessage("success"); Map<String, Object> map = new HashMap<>();
map.put("userId", 100);
map.put("tenantId", 1001);
map.put("userName", "bug弄潮儿");
resultInfo.setBody(map); return resultInfo;
} }

通过 HandlerMethodReturnValueHandler 可以对返回的数据进行进一步的封装,减少在业务代码中进行重复的返回值处理。例如,文章中的对返回数据进行统一加密。

最新文章

  1. sorl6.0+jetty+mysql搭建solr服务
  2. Java的配置文件有多少种
  3. hdu 1030 Delta-wave
  4. 判断是否是iOS8
  5. SpringInAction读书笔记--第1章Spring之旅
  6. 求帮看!!!!BZOJ 1014 [JSOI2008]火星人prefix
  7. saiku之固定维度(必选维度)
  8. 动态添加Redis密码认证
  9. android下网络通信流程
  10. Universal USB Installer – Easy as 1 2 3
  11. [Linux]安装pyenv
  12. 认识MyBatis-总述
  13. 隔壁小孩都要知道的Drupal配置
  14. mongo学习笔记---1
  15. 前端移动开发之rem
  16. Need help with git commit - Error : &quot;error: cannot run gpg: No such file or directory error: could not run gpg. fatal: failed to write commit object&quot;support (self.git)
  17. jdk1.8.0_45源码解读——Map接口和AbstractMap抽象类的实现
  18. JAVAWEB开发之HttpServletResponse和HttpServletRequest详解(下)(各种乱码、验证码、重定向和转发)
  19. Robot Framework测试框架用例脚本设计方法
  20. 【BZOJ 2039】 2039: [2009国家集训队]employ人员雇佣 (最小割)

热门文章

  1. treap(大根堆)模板
  2. mac下安装YII
  3. Odoo14 一些用的熟手的函数
  4. 跳转语句break、continue、return
  5. MySQL表操作过程的基础代码解析
  6. 过年好,新一代大数据任务调度系统 - Apache DolphinScheduler 1.3.5 发布
  7. 283. 移动零--LeetCode__双指针
  8. 刷题记录:LC1997-访问完所有房间的第一天
  9. [ARC119E] Pancakes (二维偏序,分类讨论)
  10. Java-往数据库插入日期