webflux - 处理 POJO 请求参数 自定义属性
使用场景
对接 第三方 接口文档时,使用 POJO 接收对象参数, 第三方有请求多,并且,字段属性是下滑线,我们在定义 POJO 接收请求参数时,大都使用 CamelCase 方式,造成请求参数解析不到
我们定义 POJO:
public class Pojo{
private String userId;
}
@GetMapping("/index")
public String index(Pojo pojo){
//omit...
}
实际上 第三方接口字段为 user_id
, 这样就解析不到这个参数了
实现原理
在数据绑定的时候,使用POJO 中的属性替换请求参数中的下滑线参数
了解 webflux 中解析 POJO 的过程
webflux 处理 POJO 的解析 过程是在 ModelAttributeMethodArgumentResolver
类中(而 spring mvc 中为 ModelAttributeMethodProcesso
),看 ModelAttributeMethodArgumentResolver
:
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
return true;
}
else if (this.useDefaultResolution) {
//在这里!! 判断是否为 POJO
return checkParameterType(parameter, type ->
!BeanUtils.isSimpleProperty(type));
}
return false;
}
自定义注解
自定义注解: RequestParamAlia,RequestParamAliaFor
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ParamAlia {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ParamAliaFor {
String value() default "";
}
CustomizedWebExchangeDataBinder
, 从 WebExchangeDataBinder 拷贝, 大体相同:
public class CustomizedWebExchangeDataBinder extends WebDataBinder {
// 其他方法不变
public Mono<Void> bind(ServerWebExchange exchange) {
return getValuesToBind(exchange)
.doOnNext(values ->
{
//修改在这里
Object target = getTarget();
Field[] declaredFields = target.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
RequestParamAliaFor annotation = declaredField.getAnnotation(ParamAliaFor.class);
if (!Objects.isNull(annotation) && StrUtil.isNotBlank(annotation.value())){
String key = annotation.value();
if (values.containsKey(key)){
values.put(declaredField.getName(), values.get(key));
}
}
}
doBind(new MutablePropertyValues(values));
})
.then();
}
}
CustomizedModelMethodArgumentResolver
, 从ModelAttributeMethodArgumentResolver
拷贝:
@Override
public Mono<Object> resolveArgument(
MethodParameter parameter, BindingContext context,
ServerWebExchange exchange) {
// same as ModelAttributeMethodArgumentResolver
return valueMono.flatMap(value -> {
//这里变化
CustomizedWebExchangeDataBinder binder = new CustomizedWebExchangeDataBinder(value, name);
return bindRequestParameters(binder, exchange)
.doOnError(bindingResultMono::onError)
.doOnSuccess(aVoid -> {
BindingResult errors = binder.getBindingResult();
model.put(BindingResult.MODEL_KEY_PREFIX + name, errors);
model.put(name, value);
bindingResultMono.onNext(errors);
})
.then(Mono.fromCallable(() -> {
// same as ModelAttributeMethodArgumentResolver
}));
});
}
ParamAliaWebfluxConfigurer
@Component
public class AliaWebfluxConfigurer implements WebFluxConfigurer {
@Override
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
configurer.addCustomResolver(new CustomizedModelMethodArgumentResolver(new ReactiveAdapterRegistry()));
}
}
使用
public class Pojo{
@ParamAliaFor("user_id")
private String userId;
}
@GetMapping("/index")
public String index(@ParamAliaPojo pojo){
//omit...
}
good luck!