首页 > 其他分享 >@RequestBody默认值注入踩坑及解决

@RequestBody默认值注入踩坑及解决

时间:2022-12-06 13:02:43浏览次数:31  
标签:return appDetail 校验 class RequestBody 默认值 parameter public 注入


一、环境

<properties>
<spring.version>5.3.22</spring.version>
<spring-boot.version>2.7.3</spring-boot.version>
<spring-cloud.version>3.1.3</spring-cloud.version>
<spring-cloud-dependencies.version>2021.0.3</spring-cloud-dependencies.version>
<spring-cloud-starter-alibaba.version>2021.0.1.0</spring-cloud-starter-alibaba.version>
</properties>

二、场景描述

接手老系统的对接API重构,目前项目是重构出来核心业务服务、开放平台、应用中心、网关 以前老系统都是客户端通过请求传入orgCode、orgName信息,存在的问题:

  1. 该信息机构可以随意更改,需要单独校验
  2. 机构业务上传的SDK的侵入,这个信息可以通过token获取应用信息获得

所以目前是希望,基础鉴权模块提供自动注入功能,不侵入业务执行

三、思路

3.1 基于JsonDeserializer

使用自定义注解@OrgCodeAutowired

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@JacksonAnnotationsInside
@JsonDeserialize(using = AppOrgCodeDeserializer.class)
public @interface OrgCodeAutowired {
}
public class OrgCodeDeserializer extends JsonDeserializer<String> implements ContextualDeserializer {

@Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) {
return getOrgCode();
}

private String getOrgCode() {
AppDetail appDetail = (AppDetail) AuthContext.get();
if (Objects.isNull(appDetail)) {
throw new BizException(ErrorCode.APP_DETAIL_IS_NULL);
}
return appDetail.getAppKey();
}

@Override
public JsonDeserializer<?> createContextual(DeserializationContext prov, BeanProperty property) throws JsonMappingException {
OrgCodeAutowired annotation = property.getAnnotation(OrgCodeAutowired.class);
if (Objects.nonNull(annotation)) {
return this;
}
return prov.findContextualValueDeserializer(property.getType(), property);
}
}

断点发现,​​createContextual​​​方法可以进入,但是​​deserialize​​​方法无法进入 查询stackoverflow得知需要重写​​getNullValue​​方法

​​stackoverflow​​

补上

public class OrgCodeDeserializer extends JsonDeserializer<String> implements ContextualDeserializer {

@Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) {
return getOrgCode();
}
@Override
public String getNullValue(DeserializationContext ctxt) {
return getOrgCode();
}

private String getOrgCode() {
AppDetail appDetail = (AppDetail) AuthContext.geAuthenticationt();
if (Objects.isNull(appDetail)) {
throw new BizException(ErrorCode.APP_DETAIL_IS_NULL);
}
return appDetail.geOrgCode();
}

@Override
public JsonDeserializer<?> createContextual(DeserializationContext prov, BeanProperty property) throws JsonMappingException {
OrgCodeAutowired annotation = property.getAnnotation(OrgCodeAutowired.class);
if (Objects.nonNull(annotation)) {
return this;
}
return prov.findContextualValueDeserializer(property.getType(), property);
}
}

断点发现还是无法进入​​deserialize​​,尝试在请求参数中加入

{
"orgCode": null
}

发现可以进入了,没有该​​attribute​​​则不会触发​​deserialize​​​,如果想实现估计得看下​​Jackson​​​的源码修改​​objectMapper​

3.2 尝试Filter

通过实现​​HttpServletRequestWrapper​​​,然后解析​​json​​,在设置参数 缺点:

  1. 全局拦截
  2. 需要来回转json,性能不太好

不满足需求,放弃该方案

3.3 基于HandlerMethodArgumentResolver

首先定义orgCode超类

public interface OrgCode {

void setOrgCode(String orgCode);

}

请求参数继承该超类

@Data
@ApiModel
public class DemoReq implements Serializable, OrgCode {

private static final long serialVersionUID = 1L;

@NotBlank
private String orgCode;
}

实现​​HandlerMethodArgumentResolver​

public class OrgCodeResolver implements HandlerMethodArgumentResolver {

private RequestResponseBodyMethodProcessor handlerMethodArgumentResolver;

public OrgInfoResolver(RequestResponseBodyMethodProcessor handlerMethodArgumentResolver) {
this.handlerMethodArgumentResolver = handlerMethodArgumentResolver;
}

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class) && OrgCode.class.isAssignableFrom(parameter.getParameterType());
}

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
OrgCode object = (OrgCode) handlerMethodArgumentResolver.resolveArgument(parameter,mavContainer,webRequest,binderFactory);

AppDetail appDetail = (AppDetail) AuthContext.geAuthenticationt();
if (Objects.isNull(appDetail)) {
throw new BizException(ErrorCode.APP_DETAIL_IS_NULL);
}

object.setOrgCode(appDetail.geOrgCode());
return object;
}
}

配置自动装配类

public class HandlerMethodArgumentResolverAutoConfiguration implements InitializingBean {

@Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

@Override
public void afterPropertiesSet() throws Exception {
List<HandlerMethodArgumentResolver> argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();
List<HandlerMethodArgumentResolver> customArgumentResolvers = new ArrayList<>();

for (HandlerMethodArgumentResolver argumentResolver : argumentResolvers) {
if(argumentResolver instanceof RequestResponseBodyMethodProcessor){
customArgumentResolvers.add(new OrgCodeResolver((RequestResponseBodyMethodProcessor) argumentResolver));
}
customArgumentResolvers.add(argumentResolver);
}

requestMappingHandlerAdapter.setArgumentResolvers(customArgumentResolvers);

}
}

至此解决,踩了不少坑,效率有点低

坑:因为使用了​​RequestResponseBodyMethodProcessor​​​进行​​json​​​解析,而​​RequestResponseBodyMethodProcessor​​​在其代码中内置了​​@Validate​​​参数校验模块,故如果使用​​@Validate​​校验,则无法对注入的参数进行校验。

@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

//判断入参是不是Optional类型,是则返回嵌套类型
parameter = parameter.nestedIfOptional();
//使用消息转换器转换参数
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());

//获取参数类型的短名称
String name = Conventions.getVariableNameForParameter(parameter);
if (binderFactory != null) {
//获取web数据绑定器
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null){
//判断参是是否使用了@Validated注解或者使用了@Valid开头的注解,则使用Validator接口实现类校验数据(如果适用)
validateIfApplicable(binder, parameter);
//判断校验结果是否有错误,然后判断当前参数后挨着的是不是BindingResult对象
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
return adaptArgumentIfNecessary(arg, parameter);
}

解决方法:

  1. 不进行注入参数的校验,通过其他组件进行校验,比如网关鉴权插件
  2. 进行校验,则可以通过自定义​​ValidateUtils​​工具类,进行参数后置校验
  3. 继承​​RequestResponseBodyMethodProcessor​​​重写​​resolveArgument​​方法

参考

​​[记一次springboot项目自定义HandlerMethodArgumentResolver不生效原因与解法]​​

标签:return,appDetail,校验,class,RequestBody,默认值,parameter,public,注入
From: https://blog.51cto.com/u_15814313/5915642

相关文章

  • 以修改内存的方式实现导入表动态注入
    简介搜索"导入表注入",网上大堆的博客和代码,统统都是修改PE文件实现的.这里将介绍exe加载到内存后,修改主模块映像,而不必去改变本地的exe文件的注入方法.原理注......
  • 从Deserialization和覆盖trustURLCodebase进行JNDI注入
    DeserializationLDAP在通过LDAP协议访问远程服务的时候,我们可以跟进到 LdapCtx#c_lookup方法中这里调用了 doSearchOnce方法获取LDAP远程返回的Result数据到最后会......
  • Spring依赖注入的方式
    Spring依赖注入有两种类型:setter注入,和构造器注入。setter注入中有简单类型的注入和引用类型的注入,构造器注入也是分这两个类型。setter注入简单类型(直接类型):就是在<prop......
  • Angular 复习与进阶系列 – Dependency Injection 依赖注入
    前言本来是想先介绍AngularComponent的,但里头有涉及到一些DependencyInjection(简称DI)的概念.所以还是先介绍DI吧. 介绍我在 ASP.NETCore–Depende......
  • day431 spring框架简介 & 2 IOC控制反转 & 3 DI依赖注入、AOP面向切面编程
    SpringSpringframework就是我们平时说的Spring框架,Spring框架是全家桶内其他框架的基础和核心Spring以IoC(控制反转)和AOP(面向切面编程)为内核控制反转IOC(InverseofCon......
  • Spring Boot注入静态变量
    SpringBoot注入静态变量@value或者@Autowired不能直接注入值给静态属性,spring不允许/不支持把值注入到静态变量中;spring支持set方法注入,我们可以利用非静态setter方法......
  • 013.注入集合对象(list、set、map、properties)
    1.注入list  2.注入set(不允许重复)  3.注入Map  4.注入Properties ......
  • 012.预防SQL注入攻击
    1.什么是SQL注入攻击  2.Mybatis的俩种传值方式  3.Mybatis的俩种传值方式的使用场景3.1goods.xml<selectid="selectByTitle"parameterType="java.uti......
  • .Net Core 静态类获取注入服务
    由于静态类中无法使用有参构造函数,从而不能使用常规的方式(构造函数获取)获取服务,我们可以采取通过IApplicationBuilder获取1.首先创建一个静态类usingMicrosoft.......
  • IOC容器-Bean管理XML方式、集合的注入
    1、IOC操作Bean管理(xml注入集合属性)1、注入数组类型属性2、注入list集合类型属性3、注入map集合类型属性4、注入set集合类型属性类型方式数组<array><val......