SpringBoot series of tutorials custom web articles of a request matches the conditions RequestCondition

Custom 191222-SpringBoot chapter of the series of tutorials web request matches the conditions RequestCondition

In the spring mvc, we know that user-initiated request can be matched by the url to us by @RequestMappingthe service endpoint defined; do not know if we have had a few questions to think

A project, whether there is exactly the same url?

Http protocol has an understanding of students may soon be able to give the answer, of course, be the same url, different methods can be requested; it can appear the same url and the request method l also do the same?

This article will explain how to use the RequestConditioncombination RequestMappingHandlerMappingto achieve the expansion url matching rules to support the case presented above

I. environment-related

This article describes the content and practical case will be based on spring-boot-2.2.1.RELEASEversion, if at the time of the test and found that some places can not compatible, determine what version

1. Project to build

First, we need to build a web project to facilitate the subsequent examples demonstrate servelt registered, you can create a project by spring boot official website, you can also create a maven project, the following configuration in pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
<repositories>
    <repository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>https://repo.spring.io/libs-snapshot-local</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/libs-milestone-local</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>spring-releases</id>
        <name>Spring Releases</name>
        <url>https://repo.spring.io/libs-release-local</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>
复制代码

2. RequestCondition Introduction

In the spring mvc by DispatchServletreceiving a request initiated by the client after the corresponding request will be acquired by HanderMapping processor; and how to find HanderMapping processor can handle this request it, which requires RequestCondition to decide the

Interface is shown below, there are three main methods,

public interface RequestCondition<T> {

	// 一个http接口上有多个条件规则时,用于合并
	T combine(T other);

	// 这个是重点,用于判断当前匹配条件和请求是否匹配;如果不匹配返回null
	// 如果匹配,生成一个新的请求匹配条件,该新的请求匹配条件是当前请求匹配条件针对指定请求request的剪裁
	// 举个例子来讲,如果当前请求匹配条件是一个路径匹配条件,包含多个路径匹配模板,
	// 并且其中有些模板和指定请求request匹配,那么返回的新建的请求匹配条件将仅仅
	// 包含和指定请求request匹配的那些路径模板。
	@Nullable
	T getMatchingCondition(HttpServletRequest request);

	// 针对指定的请求对象request发现有多个满足条件的,用来排序指定优先级,使用最优的进行响应
	int compareTo(T other, HttpServletRequest request);

}
复制代码

Simply put under the action of the three interfaces

  • combine: When an interface has multiple rules, merger - such as the class specified @RequestMappingin the url is root- designated method on @RequestMappingthe url is method- when you get this url matching rules interface, class scan once, scan method once, this time you need to merge these two into one, indicating that the interface matchesroot/method

  • getMatchingCondition: - determine success, failure to return null; otherwise, it returns the matching conditions for success

  • compareTo: - a plurality of conditions are satisfied, to specify a specific choice

In Spring MVC, the default provides the following categories

class Explanation
PatternsRequestCondition Path matching, i.e. url
RequestMethodsRequestCondition Request method, http request method refers to note
ParamsRequestCondition Request parameters that match the criteria
HeadersRequestCondition Request header matches
ConsumesRequestCondition Consumable MIME matching condition
ProducesRequestCondition MIME matching condition may be generated

II. Example illustrates

Simply look at the instructions, may do not really understand how it is used, then we pass a practical case, to demonstrate the use posture

1. Scene Description

We have a service simultaneously for app / wap / pc three platforms, we want to specify some interfaces can only provide services for a particular platform

2. Implement

First we define by request header x-platformto distinguish platform; that is user-initiated requests, the need to carry this request header

Define enumeration class platform

public enum PlatformEnum {
    PC("pc", 1), APP("app", 1), WAP("wap", 1), ALL("all", 0);

    @Getter
    private String name;

    @Getter
    private int order;

    PlatformEnum(String name, int order) {
        this.name = name;
        this.order = order;
    }

    public static PlatformEnum nameOf(String name) {
        if (name == null) {
            return ALL;
        }

        name = name.toLowerCase().trim();
        for (PlatformEnum sub : values()) {
            if (sub.name.equals(name)) {
                return sub;
            }
        }
        return ALL;
    }
}
复制代码

Then define a comment @Platform, if you need to specify an interface platform, you can add this comment

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Platform {
    PlatformEnum value() default PlatformEnum.ALL;
}
复制代码

Definition of matching rules PlatformRequestConditioninherited from RequestConditionachieve three interfaces, the request header acquisition platform, according to whether or not the same over the internet can be supported is determined whether a request

public class PlatformRequestCondition implements RequestCondition<PlatformRequestCondition> {
    @Getter
    @Setter
    private PlatformEnum platform;

    public PlatformRequestCondition(PlatformEnum platform) {
        this.platform = platform;
    }

    @Override
    public PlatformRequestCondition combine(PlatformRequestCondition other) {
        return new PlatformRequestCondition(other.platform);
    }

    @Override
    public PlatformRequestCondition getMatchingCondition(HttpServletRequest request) {
        PlatformEnum platform = this.getPlatform(request);
        if (this.platform.equals(platform)) {
            return this;
        }

        return null;
    }

    /**
     * 优先级
     *
     * @param other
     * @param request
     * @return
     */
    @Override
    public int compareTo(PlatformRequestCondition other, HttpServletRequest request) {
        int thisOrder = this.platform.getOrder();
        int otherOrder = other.platform.getOrder();
        return otherOrder - thisOrder;
    }

    private PlatformEnum getPlatform(HttpServletRequest request) {
        String platform = request.getHeader("x-platform");
        return PlatformEnum.nameOf(platform);
    }
}
复制代码

After completion of the specified matching rules need to register to take effect on HandlerMapping, here we have a customPlatformHandlerMapping

public class PlatformHandlerMapping extends RequestMappingHandlerMapping {
    @Override
    protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
        return buildFrom(AnnotationUtils.findAnnotation(handlerType, Platform.class));
    }

    @Override
    protected RequestCondition<?> getCustomMethodCondition(Method method) {
        return buildFrom(AnnotationUtils.findAnnotation(method, Platform.class));
    }

    private PlatformRequestCondition buildFrom(Platform platform) {
        return platform == null ? null : new PlatformRequestCondition(platform.value());
    }
}
复制代码

Finally, there is the need to register to our HandlerMapping Spring MVC container, where we use WebMvcConfigurationSupportto manually register (note the different versions, the following method may be not the same oh)

@Configuration
public class Config extends WebMvcConfigurationSupport {
    @Override
    public RequestMappingHandlerMapping requestMappingHandlerMapping(
            @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
            @Qualifier("mvcConversionService") FormattingConversionService conversionService,
            @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
        PlatformHandlerMapping handlerMapping = new PlatformHandlerMapping();
        handlerMapping.setOrder(0);
        handlerMapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
        return handlerMapping;
    }
}
复制代码

3. Test

Next, enter the actual link, defined several interfaces, specify different platforms

@RestController
@RequestMapping(path = "method")
public class DemoMethodRest {
    @Platform
    @GetMapping(path = "index")
    public String allIndex() {
        return "default index";
    }

    @Platform(PlatformEnum.PC)
    @GetMapping(path = "index")
    public String pcIndex() {
        return "pc index";
    }


    @Platform(PlatformEnum.APP)
    @GetMapping(path = "index")
    public String appIndex() {
        return "app index";
    }

    @Platform(PlatformEnum.WAP)
    @GetMapping(path = "index")
    public String wapIndex() {
        return "wap index";
    }
}
复制代码

If our normal rules can take effect, set different in the request header x-platform, the returned results should be different, the measured results are as follows

Note that the last two, one specifies a request header does not match our platform is not a corresponding request header, are both the default matching rules; this is because we are PlatformRequestConditionmade compatible, it can not match the platform when assigned to the defaultPlatform.ALL

Then there is a little doubt that if there is no distinction between a service platform, so do not add @Platformannotations if you can do?

@GetMapping(path = "hello")
public String hello() {
    return "hello";
}
复制代码

Of course, it can be measured as follows:

Without plus @Platformwhen annotation thing to note, at this time can not appear the same multiple url and request method, the startup will be directly thrown oh

III. Other

web series Bowen

Source Project

1. A gray Blog

Believe everything the book is not as good, above, is purely one of the words, due to limited personal capacity, it is inevitable omissions and mistakes, such as find a bug or have better suggestions are welcome criticism and generous gratitude

Here a gray personal blog, recording all study and work in the blog, welcome to go around

A gray blog

Guess you like

Origin juejin.im/post/5e00a22351882512243fa54d