前言

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

枚举

一般的枚举类型,比方单列值的那种: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就无法自动帮咱们绑定参数了,报如下过错:

自定义参数解析器,减少10%的代码

完成方式

  • 经过定时枚举参数注解来符号参数:这是一个枚举类型的参数。
  • 经过自界说参数解析器来剖析枚举参数注解,来完成参数的绑定。

界说注解

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnumParam {
    String value() default "";
    /**
     * 赋值调用办法
     * 假如为空,默许调用name()办法
     * 该办法有必要是一个不含参数的办法,不然将会调用失利
     *
     * @return
     */
    String valueMethod() default "";
}
  1. value() : value用于绑定恳求参数和办法参数名共同时的对应联系。比方user?statusNo=1 。 办法的参数写法如下:getUser(@EnumParam(value=”statusNo”) int status) 或许 getUser(@EnumParam() int statusNo)
  2. valueMethod() : 赋值时调用枚举中的办法。
    1. 假如该特点不传值则默许调用枚举类默许提供的 “valueOf()” 办法。
    2. 假如自界说一个办法,该办法有必要是一个不含参数的办法,不然将会调用失利。比方上述示例枚举 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);然后回来。

以上便是完成枚举参数解析器的悉数步骤。

示例

办法示例:

自定义参数解析器,减少10%的代码

恳求示例:

自定义参数解析器,减少10%的代码

json字符串

咱们有时候或许会遇到这种恳求:

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

完成方式

  • 经过定时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

一般目标办法示例:

自定义参数解析器,减少10%的代码

恳求示例:

自定义参数解析器,减少10%的代码

示例2

数组目标办法示例:

自定义参数解析器,减少10%的代码

恳求示例:

自定义参数解析器,减少10%的代码

SpringMvc自带解析器

一般参数绑定&@RequestParam

一般咱们一般的参数咱们无需加任何额定的注解符号,spring既可以给咱们自定绑定参数。

这种,当咱们的恳求参数与办法参数不共同时可以使用@RequestParam

如下:

自定义参数解析器,减少10%的代码

自定义参数解析器,减少10%的代码

@PathVariable

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

如下:

自定义参数解析器,减少10%的代码

自定义参数解析器,减少10%的代码

@RequestBody

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

自定义参数解析器,减少10%的代码

自定义参数解析器,减少10%的代码

@ModelAttribute

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

自定义参数解析器,减少10%的代码

自定义参数解析器,减少10%的代码

附录

问题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: ['|','{','}','[',']']