RESTful intermediate path solution based on SpringMVC backend system
background
In order to standardize and version control, the current industry generally prefers to add intermediate paths such as /api/{version} in URL design based on RESTful style design.
However , SpringMVC does not provide and support such requirements by default. Of course, this problem can be solved by configuring context in a very simple Servlet container (such as tomcat ) . But this will inevitably affect the flexibility of deployment.
To this end, like this type of design, try to achieve the following design goals and requirements:
1) The intermediate path is transparent to the Servlet container;
2) The intermediate path is transparent to business development;
3) Support {version} dynamically configurable; (It is recommended to configure the service version number under the api project classpath path, such as: support.auth.api.version=v1 )
Implementation plan
Based on the OCP design ideas of Spring and SpringMVC , we can easily extend and realize our own special business requirements without destroying the business processing of the original framework.
Currently, it is mainly implemented by extending RequestMappingHandlerMapping . For details, please refer to the attached code.
package com.lachesis.support.auth.demo.handler; import java.lang.reflect.Method; import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; public class PrefixEnabledRequestMappingHandlerMapping extends RequestMappingHandlerMapping { public static final String URL_SLASH = "/"; private String contextPrefix; protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = super.getMappingForMethod(method, handlerType); RequestMappingInfo prefixedInfo = null; if (info != null) { PatternsRequestCondition patt = info.getPatternsCondition(); String[] prefixedPatts = prefixPatterns(patt.getPatterns().toArray(new String[patt.getPatterns().size()])); PatternsRequestCondition patternsCondition = new PatternsRequestCondition(prefixedPatts, this.getUrlPathHelper(), this.getPathMatcher(), this.useSuffixPatternMatch(), this.useTrailingSlashMatch(), this.getFileExtensions()); prefixedInfo = new RequestMappingInfo(info.getName(), patternsCondition, info.getMethodsCondition(), info.getParamsCondition(), info.getHeadersCondition(), info.getConsumesCondition(), info.getProducesCondition(), info.getCustomCondition()); } return prefixedInfo; } public String getContextPrefix() { return contextPrefix; } public void setContextPrefix(String contextPrefix) { this.contextPrefix = contextPrefix; } protected String[] prefixPatterns(String[] patts) { if (patts == null) { return null; } String[] prefixedPatts = new String[patts.length]; for (int i = 0; i < patts.length; i++) { if (patts[i].startsWith(URL_SLASH)) { prefixedPatts[i] = getContextPrefix() + patts[i]; } else { prefixedPatts[i] = getContextPrefix() + URL_SLASH + patts[i]; } } return prefixedPatts; } }
In the configuration, reference the custom handlerrmapping
<context:property-placeholder location="classpath:support-auth-demo-version.properties" ignore-unresolvable="true" /> <bean id="handlerMapping" class="com.lachesis.support.auth.demo.handler.PrefixEnabledRequestMappingHandlerMapping"> <property name="contextPrefix" value="/api/${support.auth.demo.version}"/> </bean> <context:component-scan base-package="com.lachesis.support.auth.demo.controller" /> <mvc:annotation-driven />