Springboot 之 HandlerMethodReturnValueHandler 运用
简介
现在项目中大部分采用前后端分离的架构,采用这种架构的项目,在返回数据时,几乎都是采用返回 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 运用的更多相关文章
- 整合springboot(app后台框架搭建四)
springboot可以说是为了适用SOA服务出现,一方面,极大的简便了配置,加速了开发速度:第二方面,也是一个嵌入式的web服务,通过jar包运行就是一个web服务: 还有提供了很多metric,i ...
- springboot情操陶冶-web配置(四)
承接前文springboot情操陶冶-web配置(三),本文将在DispatcherServlet应用的基础上谈下websocket的使用 websocket websocket的简单了解可见维基百科 ...
- java框架之SpringBoot(5)-SpringMVC的自动配置
本篇文章内容详细可参考官方文档第 29 节. SpringMVC介绍 SpringBoot 非常适合 Web 应用程序开发.可以使用嵌入式 Tomcat,Jetty,Undertow 或 Netty ...
- spring-boot添加自定义拦截器
spring-boot中的WebMvcConfigurerAdapter类提供了很多自定义操作的方法,先贴出来大家看看 package org.springframework.web.servlet. ...
- springboot统一返回json数据格式并配置系统异常拦截
本文链接:https://blog.csdn.net/syystx/article/details/82870217通常进行前后端分离开发时我们需要定义统一的json数据交互格式并对系统未处理异常进行 ...
- Springboot使用自定义注解实现简单参数加密解密(注解+HandlerMethodArgumentResolver)
前言 我黄汉三又回来了,快半年没更新博客了,这半年来的经历实属不易,疫情当头,本人实习的公司没有跟员工共患难, 直接辞掉了很多人.作为一个实习生,本人也被无情开除了.所以本人又得重新准备找工作了. 算 ...
- 小BUG大原理:重写WebMvcConfigurationSupport后SpringBoot自动配置失效
一.背景 公司的项目前段时间发版上线后,测试反馈用户的批量删除功能报错.正常情况下看起来应该是个小 BUG,可怪就怪在上个版本正常,且此次发版未涉及用户功能的改动.因为这个看似小 BUG 我了解到不少 ...
- 源码剖析Springboot自定义异常
博主看到新服务是封装的自定义异常,准备入手剖析一下,自定义的异常是如何进行抓住我们请求的方法的异常,并进行封装返回到.废话不多说,先看看如何才能实现封装异常,先来一个示例: @ControllerAd ...
- 小BUG大原理 | 第一篇:重写WebMvcConfigurationSupport后SpringBoot自动配置失效
一.背景 公司的项目前段时间发版上线后,测试反馈用户的批量删除功能报错.正常情况下看起来应该是个小BUG,可怪就怪在上个版本正常,且此次发版未涉及用户功能的改动.因为这个看似小BUG我了解到不少未知的 ...
随机推荐
- 沙漠世界1.0 c++小游戏
#include<bits/stdc++.h> #include<time.h> #include<windows.h> #include<stdlib.h& ...
- 初学者入门:使用WordPress搭建一个专属自己的博客
体验简介 阿里云云起实验室提供相关实验资源,点击前往 场景将提供一台基础环境为CentOS 的ECS(云服务器)实例,这台服务器上已经内置LAMP环境.我们将会在这台服务器上安装 WordPress ...
- 上穷碧落下凡尘:Win10系统下基于Docker配置Elasticsearch7配合Python3进行全文检索交互
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_166 基于文档式的全文检索引擎大家都不陌生,之前一篇文章:使用Redisearch实现的全文检索功能服务,曾经使用Rediseac ...
- Odoo14 groups && rule
# Odoo14 groups && rule # admin账户以及权限的来源: # admin创建代码在:odoo/odoo/addons/base/data/res_users_ ...
- C#静态类、静态成员、静态方法
一.作用 静态类和非静态类重要的区别是在于静态类不能被实例化,也就是说不能使用 new 关键字创建静态类类型的变量,防止程序员写代码来实例化该静态类或者在类的内部声明任何实例字段或方法. 用于存放不 ...
- Java基础 | Stream流原理与用法总结
Stream简化元素计算: 一.接口设计 从Java1.8开始提出了Stream流的概念,侧重对于源数据计算能力的封装,并且支持序列与并行两种操作方式:依旧先看核心接口的设计: BaseStream: ...
- CF 559C - Gerald and Giant Chess (组合计数)
\(C_{x+y}^y\)的公式,DP容斥删多余贡献. #include <cstdio> #include <iostream> #include <cstring&g ...
- mybatis-plus 生成全套crud
pom依赖: <!-- web依赖--> <dependency> <groupId>org.springframework.boot</groupId> ...
- Python 爬取网站数据
一.使用request库实现批量下载HTML 二.使用BeautifulSoup库实现html解析 官网:https://beautifulsoup.readthedocs.io/zh_CN/v4.4 ...
- Ubuntu locale设置
/bin/bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8) 解决方法: 1 sudo locale-gen &q ...