/**
* Compare two patterns to determine which should match first, i.e. which
* is the most specific regarding the current path.
* @return a negative integer, zero, or a positive integer as pattern1 is
* more specific, equally specific, or less specific than pattern2.
*/
@Override
public int compare(String pattern1, String pattern2) {
PatternInfo info1 = new PatternInfo(pattern1);
PatternInfo info2 = new PatternInfo(pattern2);
if (info1.isLeastSpecific() && info2.isLeastSpecific()) {
return 0;
}
else if (info1.isLeastSpecific()) {
return 1;
}
else if (info2.isLeastSpecific()) {
return -1;
}
boolean pattern1EqualsPath = pattern1.equals(path);
boolean pattern2EqualsPath = pattern2.equals(path);
if (pattern1EqualsPath && pattern2EqualsPath) {
return 0;
}
else if (pattern1EqualsPath) {
return -1;
}
else if (pattern2EqualsPath) {
return 1;
}
if (info1.isPrefixPattern() && info2.getDoubleWildcards() == 0) {
return 1;
}
else if (info2.isPrefixPattern() && info1.getDoubleWildcards() == 0) {
return -1;
}
if (info1.getTotalCount() != info2.getTotalCount()) {
return info1.getTotalCount() - info2.getTotalCount();
}
if (info1.getLength() != info2.getLength()) {
return info2.getLength() - info1.getLength();
}
if (info1.getSingleWildcards() < info2.getSingleWildcards()) {
return -1;
}
else if (info2.getSingleWildcards() < info1.getSingleWildcards()) {
return 1;
}
if (info1.getUriVars() < info2.getUriVars()) {
return -1;
}
else if (info2.getUriVars() < info1.getUriVars()) {
return 1;
}
return 0;
}
Suffix Match 后缀匹配
Spring MVC 默认执行.* 后缀匹配,因此,如一个controller 映射到 /person,则会隐式的
匹配 /person.* ,然后使用文件扩展名来解释请求内容来替换Accept header,
如:/person.pdf,/person.xml等
当浏览器使用Accept header 发送请求时使用文件扩展名是必须的,现在,使用文件扩展名
已经是非必须的,使用Accept header是个更好的选择。
过去使用文件扩展名会在各种方式上有很多问题,当在URI变量、path 参数和URI 编码时使
用会导致各种歧义,对于URL的安全和授权校验也变得困难
为了彻底的关闭文件扩展名,你可以设置如下:
.useSuffixPatternMatching(false), see PathMatchConfigurer
.favorPathExtension(false), see ContentNegotiationConfigurer
指定请求的 Consumable Media Types consumes
可以指定Content-Type来限定映射请求
@PostMapping(path = "/pets", consumes = "application/json")
public void addPet(@RequestBody Pet pet) {
// ...
}
consumes 支持!表达式,如!text/plain则表示除了 text/plain的内容类型
都可以,该属性也可以在类上使用,不过方法上的该属性会覆盖类上的属性
指定响应的 Producible Media Types produces
可以定位到基于Accept header请求的映射,列出方法返回的媒体类型,如:
@GetMapping(path = "/pets/{petId}", produces = "application/json;charset=UTF-8")
@ResponseBody
public Pet getPet(@PathVariable String petId) {
// ...
}
该媒体类型也可以指定字符集编码,也支持!表达式,对于JSON内容类型,应该指定
具体的编码字符集
HTTP HEAD
@GetMapping( @RequestMapping(method=HttpMethod.GET))支持 HTTP HEAD 请求映射,
Controller 方法不需要做任何改变,一个响应包装类在 javax.servlet.http.HttpServlet 应用,
确保 Content-Length header 被设置为字节数(实际没有写任何内容到响应结果)
protected void doHead(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
NoBodyResponse response = new NoBodyResponse(resp);
doGet(req, response);
response.setContentLength();
}
HTTP HEAD 隐式的被 HTTP GET 支持,进行 HTTP HEAD 请求时实际调用的为doGet()
HTTP OPTIONS
默认 HTTP OPTIONS 被处理成设置响应的 Allow 为@RequestMapping指定的请求方法,
若不指定则默认设置为匹配所有的方法,所以,controller 中的方法应该总是指定请
求方法类型
protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
Method[] methods = getAllDeclaredMethods(this.getClass());
boolean ALLOW_GET = false;
boolean ALLOW_HEAD = false;
boolean ALLOW_POST = false;
boolean ALLOW_PUT = false;
boolean ALLOW_DELETE = false;
boolean ALLOW_TRACE = true;
boolean ALLOW_OPTIONS = true;
for (int i=0; i<methods.length; i++) {
String methodName = methods[i].getName();
if (methodName.equals("doGet")) {
ALLOW_GET = true;
ALLOW_HEAD = true;
} else if (methodName.equals("doPost")) {
ALLOW_POST = true;
} else if (methodName.equals("doPut")) {
ALLOW_PUT = true;
} else if (methodName.equals("doDelete")) {
ALLOW_DELETE = true;
}
}
// we know "allow" is not null as ALLOW_OPTIONS = true
// when this method is invoked
StringBuilder allow = new StringBuilder();
if (ALLOW_GET) {
allow.append(METHOD_GET);
}
if (ALLOW_HEAD) {
if (allow.length() > 0) {
allow.append(", ");
}
allow.append(METHOD_HEAD);
}
if (ALLOW_POST) {
if (allow.length() > 0) {
allow.append(", ");
}
allow.append(METHOD_POST);
}
if (ALLOW_PUT) {
if (allow.length() > 0) {
allow.append(", ");
}
allow.append(METHOD_PUT);
}
if (ALLOW_DELETE) {
if (allow.length() > 0) {
allow.append(", ");
}
allow.append(METHOD_DELETE);
}
if (ALLOW_TRACE) {
if (allow.length() > 0) {
allow.append(", ");
}
allow.append(METHOD_TRACE);
}
if (ALLOW_OPTIONS) {
if (allow.length() > 0) {
allow.append(", ");
}
allow.append(METHOD_OPTIONS);
}
resp.setHeader("Allow", allow.toString());
}
自定义注解
Spring MVC支持组合注解来处理请求映射,如@GetMapping,也支持根据自己的逻辑来处理请求逻辑,你可以
重写 RequestMappingHandlerMapping 中的getCustomMethodCondition 方法来实现自己的处理逻辑
显示注册
你可以编码方式注册handler 方法,如不同URL下同一处理程序的不同实例:
@Configuration
public class MyConfig {
@Autowired
public void setHandlerMapping(RequestMappingHandlerMapping mapping,
UserHandler handler) throws NoSuchMethodException {
RequestMappingInfo info = RequestMappingInfo
.paths("/user/{id}").methods(RequestMethod.GET).build();
Method method = UserHandler.class.getMethod("getUser", Long.class);
mapping.registerMapping(info, handler, method);
}
}
1、注入目标handler,在处理 controller 的handler
2、准备请求映射的元数据
3、获取要处理的方法
4、添加注册