简介

现在项目中大部分采用前后端分离的架构,采用这种架构的项目,在返回数据时,几乎都是采用返回 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 可以对返回的数据进行进一步的封装,减少在业务代码中进行重复的返回值处理。例如,文章中的对返回数据进行统一加密。

Springboot 之 HandlerMethodReturnValueHandler 运用的更多相关文章

  1. 整合springboot(app后台框架搭建四)

    springboot可以说是为了适用SOA服务出现,一方面,极大的简便了配置,加速了开发速度:第二方面,也是一个嵌入式的web服务,通过jar包运行就是一个web服务: 还有提供了很多metric,i ...

  2. springboot情操陶冶-web配置(四)

    承接前文springboot情操陶冶-web配置(三),本文将在DispatcherServlet应用的基础上谈下websocket的使用 websocket websocket的简单了解可见维基百科 ...

  3. java框架之SpringBoot(5)-SpringMVC的自动配置

    本篇文章内容详细可参考官方文档第 29 节. SpringMVC介绍 SpringBoot 非常适合 Web 应用程序开发.可以使用嵌入式 Tomcat,Jetty,Undertow 或 Netty ...

  4. spring-boot添加自定义拦截器

    spring-boot中的WebMvcConfigurerAdapter类提供了很多自定义操作的方法,先贴出来大家看看 package org.springframework.web.servlet. ...

  5. springboot统一返回json数据格式并配置系统异常拦截

    本文链接:https://blog.csdn.net/syystx/article/details/82870217通常进行前后端分离开发时我们需要定义统一的json数据交互格式并对系统未处理异常进行 ...

  6. Springboot使用自定义注解实现简单参数加密解密(注解+HandlerMethodArgumentResolver)

    前言 我黄汉三又回来了,快半年没更新博客了,这半年来的经历实属不易,疫情当头,本人实习的公司没有跟员工共患难, 直接辞掉了很多人.作为一个实习生,本人也被无情开除了.所以本人又得重新准备找工作了. 算 ...

  7. 小BUG大原理:重写WebMvcConfigurationSupport后SpringBoot自动配置失效

    一.背景 公司的项目前段时间发版上线后,测试反馈用户的批量删除功能报错.正常情况下看起来应该是个小 BUG,可怪就怪在上个版本正常,且此次发版未涉及用户功能的改动.因为这个看似小 BUG 我了解到不少 ...

  8. 源码剖析Springboot自定义异常

    博主看到新服务是封装的自定义异常,准备入手剖析一下,自定义的异常是如何进行抓住我们请求的方法的异常,并进行封装返回到.废话不多说,先看看如何才能实现封装异常,先来一个示例: @ControllerAd ...

  9. 小BUG大原理 | 第一篇:重写WebMvcConfigurationSupport后SpringBoot自动配置失效

    一.背景 公司的项目前段时间发版上线后,测试反馈用户的批量删除功能报错.正常情况下看起来应该是个小BUG,可怪就怪在上个版本正常,且此次发版未涉及用户功能的改动.因为这个看似小BUG我了解到不少未知的 ...

随机推荐

  1. 密码学系列之:PEM和PKCS7,PKCS8,PKCS12

    目录 简介 PEM PKCS7 PKCS8 PKCS12 总结 简介 PEM是一种常见的保存key或者证书的格式,PEM格式的文件一般来说后缀是以.pem结尾的.那么PEM到底是什么呢?它和常用的证书 ...

  2. linux-0.11分析:init文件 main.c的第一个初始化函数mem_int 第四篇随笔

    init文件夹 mian.c 参考 [github这个博主的 厉害][ https://github.com/sunym1993/flash-linux0.11-talk ] 首先先看看这个mian. ...

  3. Floyd算法详解

    Floyd本质上使用了DP思想,我们定义\(d[k][x][y]\)为允许经过前k个节点时,节点x与节点y之间的最短路径长度,显然初始值应该为\(d[k][x][y] = +\infin (k, x, ...

  4. 8月Meetup | “数据调度+分析引擎”解锁企业数字化转型之路

    ​ 编辑切换为居中 添加图片注释,不超过 140 字(可选)   大数据是一种规模大到在获取.存储.管理.分析方面大大超出了传统数据库软件工具能力范围的数据集合,而大数据作为企业运转的基础,只有经过提 ...

  5. 我与Apache DolphinScheduler社区的故事

    我与DolphinScheduler社区的故事 Apache DolphinScheduler 是一个开源的分布式去中心化.易扩展的可视化DAG大数据调度系统. 于2017年在易观数科立项,2019年 ...

  6. 从零开始Blazor Server(11)--编辑用户

    用户编辑和角色编辑几乎一模一样,这里先直接贴代码. @page "/user" @using BlazorLearn.Entity @using Furion.DataEncryp ...

  7. [WPF]WPF设置单实例启动

    WPF设置单实例启动 使用Mutex设置单实例启动 using System; using System.Threading; using System.Windows; namespace Test ...

  8. 洛谷P4135 作诗(不一样的分块)

    题面 给定一个长度为 n n n 的整数序列 A A A ,序列中每个数在 [ 1 , c ] [1,c] [1,c] 范围内.有 m m m 次询问,每次询问查询一个区间 [ l , r ] [l, ...

  9. 移动/联通APN提升

    绝大部分的时候信号满格速度特别慢 解决办法不一定对所有人有效可尝试一下 一般流程手机的设置-移动网络-移动数据-接入点名称(APN)-新建APN 中国移动如下配置 名称:随便写 APN:cmtds m ...

  10. 未完待续【java】JavaEE学习路线总览

    这个博客会详细介绍各种技术的知识点,从零基础到入门,充当引路的作用. 同时也会发布一些Swift语言.c#语言.Xcode开发的学习笔记.一些阅读的笔记(部分读书笔记无法发布). 目前1-43的Jav ...