【问题标题】:Adding validation in RequestMapping annotation在 RequestMapping 注解中添加验证
【发布时间】:2018-09-17 10:15:59
【问题描述】:

我正在使用 Spring Boot,我想为一些 RequestMapping 添加 API 版本控制(url 路径应该以 /v[1-9]+/ 开头)。

我正在考虑创建一个注释:MyRequestMapping,它还支持 api 版本控制路径。这样,任何使用 MyRequestMapping 的人都会自动为其分配 api 版本。

由于我无法扩展 RequestMapping 注释(Java 中不允许注释扩展),我不确定如何继续使用这种方法。任何提示/想法都会很棒!

简而言之:如何支持 RequestMapping 中“路径”的 api 版本控制?谢谢..

【问题讨论】:

  • 当您使用 Spring rest 时,当您没有在我们的任何控制器中定义请求映射时,您将得到一个 404 的 URL,您是否正在尝试实现其他目标?
  • 谢谢.. 我编辑了问题以使其更清楚:如何在 RequestMapping 中为“路径”引入自定义验证检查

标签: java spring spring-boot java-annotations


【解决方案1】:

我将创建与以 /v[1-9]+/ 开头的 URL 匹配的纯类 MyRequestMapping

有关在请求映射中使用正则表达式的官方文档,请参阅 https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-requestmapping-uri-templates

然后用户可以使用方法级别的 RequestMapping 注释进一步缩小路径。

【讨论】:

  • 谢谢.. 很好,但我的问题是:如何在 RequestMapping 中添加对 url 的检查?
【解决方案2】:

您可以使用 filter ,并在 filter 内进行所需的检查。如果检查没有返回 true,只需从过滤器本身修改 httpresponse 并返回,就是这样。我在下面添加了一个示例filter,您需要对web.xml@configuration 类进行相应的更改,才能使过滤器正常工作。

 public class MyFilter implements Filter {

        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
                throws IOException, ServletException {
if("")//Do the required check here
{
//Modifiy httpservlet response .
}
            filterChain.doFilter(servletRequest, servletResponse);
        }
}

【讨论】:

    【解决方案3】:

    我找到了支持 api 版本控制的更好方法。下面是解释:

    1) 创建注解:ApiVersion:

    @Target({ ElementType.METHOD, ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @RequestMapping("/{apiVersion}")
    public @interface ApiVersion {
    
        /**
         * version
         *
         * @return
         */
        int value() default 1;
        boolean latest() default false;
    }
    

    2)创建自定义RequestCondition来放匹配逻辑:**

    公共类 ApiVersionCondition 实现 RequestCondition {

    private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("/v(\\d+)/");
    private final static Pattern LATEST_PREFIX_PATTERN = Pattern.compile("/latest/");
    
    private int apiVersion;
    private boolean latest;
    
    public ApiVersionCondition(int apiVersion, boolean latest) {
        this.apiVersion = apiVersion;
        this.latest = latest;
    }
    
    /**
     * Latest definition will take effect, that means method definition will override the classes definition
     *
     * @param otherApiVersionCondition other condition that is matching.
     * @return latest definition of matching condition.
     */
    public ApiVersionCondition combine(ApiVersionCondition otherApiVersionCondition) {
        return new ApiVersionCondition(otherApiVersionCondition.getApiVersion(), otherApiVersionCondition.isLatest());
    }
    
    /**
     * Define matcher to match the pattern for api versioning.
     * When version number requested is equal to or greater than configured, condition will be returned.
     *
     * @param request Request instance
     * @return ApiVersionCondition based on match
     */
    
    public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
        Matcher m = LATEST_PREFIX_PATTERN.matcher(request.getRequestURI());
        if (m.find() && isLatest()) {
            return this;
        }
    
        m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
        if (m.find()) {
            Integer version = Integer.valueOf(m.group(1));
            if (version >= this.apiVersion)
                return this;
        }
        return null;
    }
    
    /**
     * When more than one configured version number passes the match rule, the bigest one will take effect.
     *
     * @param otherApiVersionCondition other api version condition that was satisfied.
     * @param request                  Request instance
     * @return 1 if other version condition has greater api version, -1 if this condition has greater api version, 0 if they are same.
     */
    public int compareTo(ApiVersionCondition otherApiVersionCondition, HttpServletRequest request) {
        return otherApiVersionCondition.getApiVersion() - this.apiVersion;
    }
    
    /**
     * Getter for api version.
     *
     * @return api version for the condition.
     */
    private int getApiVersion() {
        return apiVersion;
    }
    
    /**
     * Getter for whether latest is configured in annotation.
     *
     * @return true if latest is configured in annotation, else false.
     */
    
    private boolean isLatest() {
        return latest;
    }
    

    }

    3) 创建自定义 RequestMappingHandlerMapping:

    public class ApiVersioningRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
    
        @Override
        protected RequestCondition<ApiVersionCondition> getCustomTypeCondition(Class<?> handlerType) {
            return createCondition(AnnotationUtils.findAnnotation(handlerType, ApiVersion.class));
        }
    
        @Override
        protected RequestCondition<ApiVersionCondition> getCustomMethodCondition(Method method) {
            return createCondition(AnnotationUtils.findAnnotation(method, ApiVersion.class));
        }
    
        private RequestCondition<ApiVersionCondition> createCondition(ApiVersion apiVersion) {
            return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value(), apiVersion.latest());
        }
    }
    

    4) 要求 Spring 使用自定义的 RequestMappingHandlerMapping:

    @Configuration
    public class WebMvcRegistrationsConfig implements WebMvcRegistrations {
    
        @Override
        public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
            ApiVersioningRequestMappingHandlerMapping apiVersioningRequestMappingHandlerMapping = new ApiVersioningRequestMappingHandlerMapping();
            apiVersioningRequestMappingHandlerMapping.setOrder(0);
            apiVersioningRequestMappingHandlerMapping.setRemoveSemicolonContent(false);
            return apiVersioningRequestMappingHandlerMapping;
        }
    
    }
    

    5) 在控制器中使用:

    @ApiVersion //default is version 1
    //@RequestMapping("{apiVersion}/test") //add this if want to specify a common root e.g. v<x>/test for all below request mapping
    @RestController
    public class GreetingController {
    
        private static final String template = "Hello , %s!";
        private final AtomicLong counter = new AtomicLong();
    
        @RequestMapping("/greeting")    // URI: /v1/greeting will be mapped to this method
        public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
            return new Greeting(counter.incrementAndGet(),
                    String.format(template, name));
        }
    
        @ApiVersion(2)
        @RequestMapping("/greeting")    // URI: /v2/greeting will be mapped to this method
        public Greeting greetingV2(@RequestParam(value = "name", defaultValue = "World version 2") String name) {
            return new Greeting(counter.incrementAndGet(),
                    String.format(template, name));
        }
    
        @ApiVersion(value = 3, latest = true)
        @RequestMapping("/greeting")    // URI: /v3/greeting OR /latest/greeting will be mapped to this method
        public Greeting greetingV3(@RequestParam(value = "name", defaultValue = "World version 3") String name) {
            return new Greeting(counter.incrementAndGet(),
                    String.format(template, name));
        }
    }
    

    还有一些微调要做,但这对于我想要实现的目标来说是一个好的开始。如果找到更好的想法或一些改进,将更新。如果有人发现改进,请告诉我们。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-03
      • 2016-07-23
      • 2014-07-11
      • 2017-05-15
      • 1970-01-01
      相关资源
      最近更新 更多