0.需求

在实际的开发过程中,服务间调用一般使用Json传参的模式,SpringBoot项目无法使用@RequestParam接收Json传参

只有@RequestBody支持Json,但是每次为了一个接口就封装一次实体类比较麻烦

如果使用Map来进行参数接收,则会导致参数不可控,会在接口中新增较多判断进行入参控制

其次,在实际的开发过程中,我们偶尔会传入两个实体类,如果使用@RequestBody也会出错

因为传入的参数只能够读取一次,一般这里也会封装一次实体类,不够方便

也有重写HttpServletRequestWrapper的处理办法,但不能解决上一个问题

1.思路

因为一个注解只能读取一次,按照重写HttpServletRequestWrapper的思路,将请求中的Json参数进行缓存

另外自定义一个注解,来把参数进行注入。

1.1.自定义@JsonFmt注解

import java.lang.annotation.*;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JsonFmt {
/**
* 值
*/
String value() default ""; /**
* 是否必须
*/
boolean require() default true;
}

这里的值,不是给参数的默认值(defaultValue),而是类似于@RequestParam注解中的value、name,是用来指定入参的key

1.2.自定义注解的实现类

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer; import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.util.HashMap;
import java.util.Map; @Slf4j
public class JsonFmtHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { //自定义key
private static final String KEY = "TEST_JSON_BODY_KEY";
private static ObjectMapper objectMapper = new ObjectMapper(); @Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(JsonFmt.class);
} @Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
JsonFmt jsonFmt = parameter.getParameterAnnotation(JsonFmt.class);
JSONObject jsonObject = getJsonObject(webRequest); String value = getParamName(parameter,jsonFmt);
boolean require = jsonFmt.require();
Object paramValue = getParamValue(jsonObject,value); if (paramValue == null && require) {
throw new Exception("parameter[" + value + "]不能为空。");
}
if (paramValue == null) {
return null;
} Class<?> classType = parameter.getParameterType(); if (paramValue.getClass().equals(JSONObject.class)){
paramValue = objectMapper.readValue(paramValue.toString(),classType);
} return paramValue;
} private String getParamName(MethodParameter parameter, JsonFmt jsonFmt) {
String value = jsonFmt.value();
if (StringUtils.isEmpty(value)) {
value = parameter.getParameterName();
}
return value;
} private Object getParamValue(JSONObject jsonObject,String value) {
for (String key: jsonObject.keySet()) {
if(key.equalsIgnoreCase(value)){
return jsonObject.get(key);
}
}
return null;
} private JSONObject getJsonObject(NativeWebRequest webRequest) throws Exception {
String jsonBody = (String) webRequest.getAttribute(KEY, NativeWebRequest.SCOPE_REQUEST);
if(StringUtils.isEmpty(jsonBody)){
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
BufferedReader reader = request.getReader();
StringBuilder sb = new StringBuilder();
char[] buf = new char[1024];
int rd;
while ((rd = reader.read(buf)) != -1) {
sb.append(buf, 0, rd);
} jsonBody = sb.toString(); if(StringUtils.isEmpty(jsonBody)){
Map<String,String[]> params = request.getParameterMap(); Map tmp = new HashMap();
for (Map.Entry<String,String[]> param:params.entrySet()) {
if(param.getValue().length == 1){
tmp.put(param.getKey(),param.getValue()[0]);
}else{
tmp.put(param.getKey(),param.getValue());
} }
jsonBody = JSON.toJSONString(tmp);
} webRequest.setAttribute(KEY, jsonBody, NativeWebRequest.SCOPE_REQUEST);
} return JSONObject.parseObject(jsonBody);
}
}

方法说明:

supportsParameter:说明支持的注解,只要方法参数有@JsonFmt就启用该实现类

resolveArgument:解决方法,注解的具体实现

getJsonObject:获取请求体,这里的实现逻辑就是从请求中获取Json体,如果没有获取到,则从请求参数中获取(兼容From模式),将请求体封装为JsonObject

getParamName:获取注解参数的key,先获取注解的value,如果为空,则使用方法参数的名称

getParamValue:这个可以不加,我这里是为了让key不区分大小写,如果需要区分,直接使用jsonObject.get(key)即可

1.3.加入自定义注解

import com.example.demo.jsonfmt.JsonFmtHandlerMethodArgumentResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; @Configuration
public class AppConfig implements WebMvcConfigurer { @Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new JsonFmtHandlerMethodArgumentResolver());
} }

2.使用

到这里我们就能愉快的使用我们的自定义注解@JsonFmt来进行参数接收了

目前在Json传参中,能完美的接收实体类、List、Map以及其他基础类型

在Form传参中,能够支持List、Map以及其他基础类型,对于实体类暂时还不能兼容

因为后台接收到的是Map,不容易区分哪些是实体类的字段,无法进行填充,这种建议使用@RequestBody

SpringBoot:自定义注解实现后台接收Json参数的更多相关文章

  1. Ajax前台返回JSON数据后再Controller中直接转换成类型使用,后台接收json转成实体的方法

    之前写过一篇记录文章,写的是将一个比较复杂的数据结构在前台组合起来后传递到后台. 当时并不太了解@RequestBody,也并没有使用js提供的JSON.stringify()方法 所有都是自己写的, ...

  2. [技术博客] SPRINGBOOT自定义注解

    SPRINGBOOT自定义注解 在springboot中,有各种各样的注解,这些注解能够简化我们的配置,提高开发效率.一般来说,springboot提供的注解已经佷丰富了,但如果我们想针对某个特定情景 ...

  3. SpringBoot 自定义注解 实现多数据源

    SpringBoot自定义注解实现多数据源 前置学习 需要了解 注解.Aop.SpringBoot整合Mybatis的使用. 数据准备 基础项目代码:https://gitee.com/J_look/ ...

  4. SpringBoot配置Swagger实例(POST接收json参数)

    工程目录结构: 首先,引入jar包,只需要以下两个即可 <dependency> <groupId>io.springfox</groupId> <artif ...

  5. ASP.NET MVC 后台接收集合参数和 jquery ajax 传值

    MVC 接收参数数组(集合)   示例样本:   public class Person {      public string FirstName { get; set; }      publi ...

  6. java/springboot自定义注解实现AOP

    java注解 即是注释了,百度解释:也叫元数据.一种代码级别的说明. 个人理解:就是内容可以被代码理解的注释,一般是一个类. 元数据 也叫元注解,是放在被定义的一个注解类的前面 ,是对注解一种限制. ...

  7. 使用IDEA创建SpringBoot自定义注解

    创建SpringBoot项目 添加组织名 选择web 输入项目名称 创建后目录结构为 使用Spring的AOP先加入Maven依赖 <dependency> <groupId> ...

  8. SpringBoot自定义注解、AOP打印日志

    前言 在SpringBoot中使用自定义注解.aop切面打印web请求日志.主要是想把controller的每个request请求日志收集起来,调用接口.执行时间.返回值这几个重要的信息存储到数据库里 ...

  9. SpringBoot自定义注解

    1.注解的概念 注解是一种能被添加到java代码中的元数据,类.方法.变量.参数和包都可以用注解来修饰.注解对于它所修饰的代码并没有直接的影响. 2.注解的使用范围 1)为编译器提供信息:注解能被编译 ...

随机推荐

  1. VMware Workstation批量克隆虚拟机

    由于经常要用vmware创建虚拟机做一些测试,集群的测试使用连接克隆,可以节省磁盘的空间(如果不是因为穷,没人愿意向生活低头) 于是找到了这个bat脚本,做了一些修改和学习,为大家加上了一些注释,方便 ...

  2. Realtime Data Processing at Facebook

    概要 这篇论文发表于2016年,主要是介绍Facebook内部的流式计算平台的设计与思考,对于流式计算的关键特性的实现选型上进行深度对比分析. 流式计算系统5个衡量指标 文中提到有5个重要的考量部分 ...

  3. VS2019如何设置程序以管理员权限启动

    最重要的一点.本文解释的是C#项目如何以管理员权限启动. 一个很大的误导项 该图片是C++程序的项目配置属性.C#项目中并找不到.然而网上的很多教程没有说清楚.导致我找了这个菜单找了很久. C#项目的 ...

  4. WPF/MVVM Quick Start Tutorial - WPF/MVVM 快速入门教程 -原文,翻译及一点自己的补充

    转载自 https://www.codeproject.com/articles/165368/wpf-mvvm-quick-start-tutorial WPF/MVVM Quick Start T ...

  5. service与systemctl命令比较

    本文将比较 linux 的 service 和 systemctl 命令,先分别简单介绍这两个命令的基础用法,然后进行比较. 从 CentOS 7.x 开始,CentOS 开始使用 systemd 服 ...

  6. Nginx 配置apple-app-site-association

    ios突然给我发了如上链接和一个json,说他那边需要放一个 apple-app-site-association 文件用来支持他那边的功能,文件不需要后缀. 先说一下要求:线上官网的地址后面跟上他所 ...

  7. .Net Core AOP之AuthorizeAttribute

    一.简介 在.net core 中Filter分为以下六大类: 1.AuthorizeAttribute(权限验证) 2.IResourceFilter(资源缓存) 3.IActionFilter(执 ...

  8. k8s被删除的pod一直Terminating状态

    微服务项目,部分服务无法delete,一直处于Terminating状态 kubectl get po -n gift 强制删除product:kubectl delete -n gift po/pr ...

  9. 传统式BI工具和自助式BI工具到底有什么区别

    相信很多人都听说过BI工具,但是你听说过自助BI工具吗?自助式BI工具面向没有IT背景的业务分析师,比传统的BI工具灵活易用,在一定程度上摆脱了对IT部门的大幅度依赖,使数据产品链更加大众化,更加理解 ...

  10. 【C# IO 操作 】内存包装类 Memory <T>和 Span<T> 相关类型

    简介 .NET 包含多个相互关联的类型,它们表示任意内存的连续的强类型区域. 这些方法包括: System.Span<T> 用于访问连续的内存区域 得到该类型的实例: 1个T类型的数组 1 ...