1

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

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

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

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

4

3 回答 3

0

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

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

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

于 2018-04-07T15:18:48.980 回答
0

我找到了支持 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));
    }
}

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

于 2018-04-08T06:12:19.740 回答
0

您可以使用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);
        }
}
于 2018-04-08T03:51:58.997 回答