Redian新闻
>
减少10%的代码:自定义参数解析器真的很强大,你不来了解一下?

减少10%的代码:自定义参数解析器真的很强大,你不来了解一下?

公众号新闻

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入芋道快速开发平台知识星球。下面是星球提供的部分资料: 

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号、CRM 等等功能:

  • Boot 仓库:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 仓库:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK 21 + SpringBoot 3.2.2、JDK 8 + Spring Boot 2.7.18 双版本 

来源:juejin.cn/post/
7223705034412015675


Part1 前言

springMvc中提供了很多好用的参数绑定的方式方法,那枚举呢?或者参数的值是一个json字符串的时候?你是怎么处理的?下面分享一下我的处理方式。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

Part2 枚举

普通的枚举类型,比如单列值的那种:one ,two... 。这种事不需要特殊处理的,我们是可以直接接收值并绑定数据的。

要是下面这种枚举类型呢?而且我们的参数传递的是:0,1这种数字,方法参数是枚举类型。spring还能帮我们自动绑定参数嘛?

public enum StatusEnum {
    online(1),
    offline(0);

    private Integer value;

    StatusEnum(Integer value) {
        this.value = value;
    }

    public Integer getValue() {
        return value;
    }
}

这时候spring就无法自动帮我们绑定参数了,报如下错误:

1 实现方式

  • 通过定时枚举参数注解来标记参数:这是一个枚举类型的参数。
  • 通过自定义参数解析器来分析枚举参数注解,来实现参数的绑定。
定义注解
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnumParam {
    String value() default "";
    /**
     * 赋值调用方法
     * 如果为空,默认调用name()方法
     * 该方法必须是一个不含参数的方法,否则将会调用失败
     *
     * @return
     */

    String valueMethod() default "";
}
  • value() : value用于绑定请求参数和方法参数名一致时的对应关系。比如user?statusNo=1

    • 方法的参数写法如下:getUser(@EnumParam(value="statusNo") int status) 或者 getUser(@EnumParam() int statusNo)
  • valueMethod() : 赋值时调用枚举中的方法。

    • 如果该属性不传值则默认调用枚举类默认提供的 “valueOf()” 方法。
    • 如果自定义一个方法,该方法必须是一个不含参数的方法,否则将会调用失败。比如上述示例枚举 StatusEnum的getValue()方法。
定义枚举参数解析器

核心代码:

// 1
public class EnumParamArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(EnumParam.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        // 2 
        EnumParam annotation = parameter.getParameterAnnotation(EnumParam.class);
        Object object = null;
        if (annotation != null) {
            String parameterName = annotation.value();
            // 3
            if (!StringUtils.hasText(parameterName)) {
                parameterName = annotation.name();
            }
            if (!StringUtils.hasText(parameterName)) {
                parameterName = parameter.getParameterName();
            }

            String value = webRequest.getParameter(parameterName);

            if (StringUtils.hasText(value)) {
                // 4 
                Class<?> objectType = parameter.getParameterType();
                String method = Objects.equals(annotation.valueMethod(), "") ? "valueOf" : annotation.valueMethod();
                Object[] enumConstants = objectType.getEnumConstants();
                // 如果方法没了就 抛出异常
                Method declaredMethod = objectType.getDeclaredMethod(method);
                try {
                    for (Object enumConstant : enumConstants) {
                        // 5
                        Object invoke = method.equals("valueOf") ? declaredMethod.invoke(enumConstant, enumConstant.toString()) : declaredMethod.invoke(enumConstant);
                        if (invoke != null) {
                            if (Convert.toStr(invoke).equals(Convert.toStr(value))) {
                                object = enumConstant;
                                break;
                            }
                        }
                    }
                } catch (Exception e) {
                    log.error("参数enum转换失败:{}.{}[{}]", parameter.getContainingClass().getName(), parameter.getMethod().getName(), parameterName);
                    object = null;
                }
            }
            mavContainer.addAttribute(parameterName, object);
        }
        return object;
    }
}
  1. 枚举参数解析器(EnumParamArgumentResolver)实现 spring mvc的扩展接口HandlerMethodArgumentResolver。
  2. 从参数中获取是否标记了 EnumParam 注解,如果是则进行解析。
  3. 处理 EnumParam.value() 的值并进行赋值给parameterName。
  4. 通过返回的方式拿到需要执行的方法和目标枚举类的值。
  5. 通过循环枚举的值然后比较。如果匹配则立即跳出循环并mavContainer.addAttribute(parameterName, object);然后返回。

以上就是实现枚举参数解析器的全部步骤。

示例

方法示例:

请求示例:

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

Part3 json字符串

我们有时候可能会遇到这种请求:

localhost:8088/prt/jsonParams?user={"age":12,"id":"1","name":"凹凸曼"} 那么这种我们可能一般都是使用String接收,然后在调用转JSON的API进行处理。可是这种代码每个方法都去写的话,太不优雅了。毕竟:「温柔永不落伍, 优雅永不过时 」

2 实现方式

  • 通过定时JSON参数注解来标记参数:这是一个JSON字符串的参数。
  • 通过自定义参数解析器来分析JSON字符串参数注解,来实现参数和对象属性的绑定。
定义注解
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JsonParam {
    String value() default ""

    Class<?> objectType() default JsonParam.class
;
}
  1. value() : value用于绑定请求参数和方法参数名一致时的对应关系。和 EnumParam中的value定义差不多。
  2. objectType() : 当参数是数组对象时,赋值属性。
定义Json参数解析器

核心代码:

public class JsonParamArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation( JsonParam.class );
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        JsonParam annotation = parameter.getParameterAnnotation( JsonParam.class );
        Object object = null;
        if (annotation != null) {
            String parameterName = annotation.value();
            if (StringUtils.isBlank( parameterName )) {
                parameterName = annotation.name();
            }

            if (StringUtils.isBlank( parameterName )) {
                parameterName = parameter.getParameterName();
            }

            String value = webRequest.getParameter( parameterName );
            if (StringUtils.isNotBlank( value )) {
                // 2
                Class<?> objectType = annotation.objectType();
                try {
                    if (objectType != JsonParam.class{
                        object = JSON.parseArray( value, objectType );
                    } else {
                        object = JSON.parseObject( value, parameter.getParameterType() );
                    }
                } catch (Exception e) {
                    LoggerFactory.getLogger( JsonParamArgumentResolver.class )
                            .error( "参数Json转换失败:" + parameter.getContainingClass().getName() + "." + parameter.getMethod().getName() + "[" + parameterName + "]" )
;
                    object = null;
                }
            }
            //this.validate( parameter, mavContainer, object, parameterName );
            mavContainer.addAttribute( parameterName, object );
        }
        return object;
    }
}
  1. 上述步骤的大部分逻辑和 枚举参数解析器 大体一致。
  2. 步骤2是判断objectType是否是JsonParam类型,如果是则是对象类型;如果不是JsonParam,这是数组对象类型。

以上就是实现Json参数解析器的全部步骤。

示例

示例1

普通对象方法示例:

请求示例:

示例2

数组对象方法示例:

请求示例:

Part4 SpringMvc自带解析器

3 普通参数绑定&@RequestParam

一般我们普通的参数我们无需加任何额外的注解标记,spring既可以给我们自定绑定参数。

这种,当我们的请求参数与方法参数不一致时可以使用@RequestParam

如下:

4 @PathVariable

在Controller方法的参数前面添加@PathVariable注解,将路径参数的值绑定到对应的参数上。

如下:

5 @RequestBody

在Controller方法的参数前面添加@RequestBody注解,将请求体的值绑定到对应的参数上 。注意这种模式不支持: Content-Type: application/x-www-form-urlencoded Content-Type: application/x-www-form的请求。

6 @ModelAttribute

在Controller方法的参数前面添加@ModelAttribute注解,将表单参数的值绑定到对应的参数上。同上这种模式不支持 Content-Type: application/json的请求。

Part5 附录

7 问题1

springBoot+tomcat报错:_Invalid character found in the request target \[...\]. The valid characters are defined in RFC 7230 and RFC 3986_

原因是:SpringBoot 2.0.0 以上都采用内置tomcat8.0以上版本,而tomcat8.0以上版本遵从RFC规范添加了对Url的特殊字符的限制,url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~四个特殊字符以及保留字符( ! * ’ ( ) ; : @ & = + $ , / ? # \[ \] ) (26*2+10+4+18=84)这84个字符,请求中出现了{}大括号或者\[\],所以tomcat报错。

处理办法:在application.yml配置文件中如下配置:

server:
 tomcat:
    relaxed-path-chars: ['|','{','}','[',']']
    relaxed-query-chars: ['|','{','}','[',']'

欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

文章有帮助的话,在看,转发吧。

谢谢支持哟 (*^__^*)

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
段永平经典分享:改正错误要尽快,多大的代价都是最小的代价(古詩英譯) 次北固山下 - 王湾情人节只会说"I love you"?15句经典英文情话了解一下~微软颠覆生产力:Copilot推自定义版,AI PC原生支持PyTorch,奥特曼预告新模型直申结果不理想?!录取率是直申的两倍,这些Top30转学友好大学了解一下!AI也造代码屎山!研究发现GitHub Copilot代码可维护性差,偏爱“无脑重写”而非重构复用已有代码(古詩英譯)锦瑟 - 李商隐28、长篇家庭伦理小说《嫁接》第七章 非法打工(2)中国神药了解一下:这个几块钱一支的小药膏,究竟有多强?生活很强大。教育质量和名气都想要?Top30文理学院3+2项目了解一下?“吃蓝莓吗?”“吃!”“去洗一下?”“那不吃了”去加拿大留学哪个省更适合你?迅速了解一下!告别H-1B抽签烦恼,90天快速获准绿卡申请攻略了解一下?美股基本面 - 2024_01_23 * 晨报 * “一月效应”或再次应验!降息预期降温背景下美股为什么签证申请等了很久没消息?了解一下加拿大的背景调查……当大模型公司都在卷大参数,面壁智能却在尽可能把参数做小下水道堵有异味, 试试这招!!陈年油垢、毛发“嗖”的一下都通了,小飞虫也不来了!Linus 开喷谷歌内核贡献者:你的代码是垃圾!网友:我们熟悉的 Linus 回来了使用 GDM 设置来自定义 GNOME 中的登录屏幕 | Linux 中国安省PSW缺口3.3万!了解一下这个可移民、学费政府补贴的第二职业[电脑] 豪华大平层+自定义冷头装饰盖,ROG Z790A 吹雪S+酷冷HAF700装机分享留学生执业友好的心理专业,确定不了解一下吗?|正在答疑中两轮游日本 - 我的所见,所闻和所想 回首大阪阑珊处,有位佳人,在水一方骨人狂代!骨科cp代了解一下新加坡儿童保护中心了解一下?国产HER2靶向药美国获批,了解一下啥是生物类似药字节系多款 App 被指控抄袭源代码,连拼错的代码都抄走了?美摄回应:我们不是碰瓷,是在维权70B模型秒出1000token,代码重写超越GPT-4o,来自OpenAI投资的代码神器Cursor团队【酷玩亲子】4.27周六出发!两天一夜亲子游,周末溜娃攻略了解一下!上能走秀、下能实穿,这样的秀款你确定不看一下?人形机器人会变魔术了,春晚节目组了解一下(doge)这种房贷了解一下!在法国如何获得社会住房?戴高乐机场跌出全球前五!重大车祸26伤不想卷中国高考,到新加坡换个赛道冲一下?私荐||最近明星礼服不追国际大牌了?这几个出圈的中国设计师不得不了解一下(下)
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。