@RequestMapping的基本用法
我们可以在类或指定handler方法上,使用@RequestMapping注解来映射URL。我们来看一个例子:
@Controller
@RequestMapping("/appointments")
public class AppointmentsController {
private final AppointmentBook appointmentBook;
@Autowired
public AppointmentsController(AppointmentBook appointmentBook) {
this.appointmentBook = appointmentBook;
}
@RequestMapping(method = RequestMethod.GET)
public Map<String, Appointment> get() {
return appointmentBook.getAppointmentsForToday();
}
@RequestMapping(method = RequestMethod.POST)
public String add(@Valid AppointmentForm appointment, BindingResult result) {
if (result.hasErrors()) {
return "appointments/new";
}
appointmentBook.addAppointment(appointment);
return "redirect:/appointments";
}
@RequestMapping(path = "/new", method = RequestMethod.GET)
public AppointmentForm getNewForm() {
return new AppointmentForm();
}
@RequestMapping(path = "/{day}", method = RequestMethod.GET)
public Map<String, Appointment> getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model) {
return appointmentBook.getAppointmentsForDay(day);
}
}
在上述例子中,在多处地方使用了@RequestMapping。
第一个用在了类上,表示@RequestMapping这个控制器下的所有handler方法都是相对/appointments路径而言的。
第二个用在了get()方法上,对@RequestMapping做了进一步的细化,此方法只接收GET请求方式,也就是说/appointments的GET请求会调用这个方法。
第三个用在了add()方法上,也做一个类似的细化,此方法只接收POST请求方式。
第四个用在了getNewForm()方法上,在RequestMapping上组合定义了http方法和路径,也就是说此方法会处理/appointments/new的GET请求。
第五个用在了getForDay()方法上,这是@RequestMapping的另外一种使用方式,URI模版。
在类上添加@RequestMapping不是必须的,如果没有,所有的路径都是绝对路径,而不是相对路径。
@RequestMapping的简化
正如上面我们看到的,如果要handler方法只接受GET请求,则要这样写:
@RequestMapping(method = RequestMethod.GET)
在Spring Framework 4.3以上版本中,支持一种简化的写法,可以在handler方法上用@GetMapping来表示handler方法只接受GET请求。
另外还有如下几个简化的@RequestMapping:
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @PatchMapping
URI模版模式
URI模版是一个类似于URI的字符串,其中包含了一个或多个变量。当你将这些变量替换掉,就变回了URI。
可在方法入参上使用注解@PathVariable绑定URI的模版参数:
@GetMapping("/owners/{ownerId}")
public String findOwner(@PathVariable String ownerId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
model.addAttribute("owner", owner);
return "displayOwner";
}
URI模版”/owners/{ownerId}”指定了参数owernId。当控制器处理这个请求时,会将URI中匹配的部分赋值给owernId变量。如,当传入/owners/fred请求时,owernId的值就是fred。
在处理@PathVariable 注解时,Spring MVC默认是根据名称来匹配URI模版变量的。也就是说,URI模板大括号里的参数名要和@PathVariable注解的参数名要相同。
但是如果想要不同也可以,只需在注解里指定这个名称即可:
@GetMapping("/owners/{ownerId}")
public String findOwner(@PathVariable("ownerId") String theOwner, Model model) {
// implementation omitted
}
一个方法可以有多个@PathVariable注解,如:
@GetMapping("/owners/{ownerId}/pets/{petId}")
public String findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
Pet pet = owner.getPet(petId);
model.addAttribute("pet", pet);
return "displayPet";
}
此时,findPet方法可以处理类似/owners/42/pets/21的URI。
URI模板还能这样写:
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@RequestMapping("/pets/{petId}")
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
// implementation omitted
}
}
此时,findPet方法同样可以处理类似/owners/42/pets/21的URI。
@PathVariable参数可以是任意的简单类型,如int,long,Date等,Spring会自动将其进行类型转换,转换出错会抛出TypeMismatchException。
另外,@PathVariable注解也可以用在Map<String, String>
类型的参数上,此时,所有URI模版变量都会填充到map里去。
在URI模版上使用正则表达式
有时在URI模版变量里,我们会需要用到更加精确的控制。比如 “/spring-web/spring-web-3.0.5.jar” 这样的URI,该如何拆分成多个部分放到相应的参数里去呢?
@RequestMapping注解支持在URI模版变量里使用正则表达式。语法是 {变量名:正则表达式},第一个部分定义变量的名称,第二部分是正则表达式。如:
@RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}")
public void handle(@PathVariable String symbolicName, @PathVariable String version, @PathVariable String extension) {
// ...
}
路径模式
@RequestMapping注解处理支持URI模版,也支持使用Ant风格模式,如:/myPath/*.do
)。同时支持组合使用URI模版和Ant风格模式,如: /owners/*/pets/{petId}
。
路径模式比较
当一个URL与多个模式匹配时,会设法找出最具体的那一个路径。如:
/hotels/{hotel}/*
比/hotels/{hotel}/**
更具体;
/foo/bar*
比/foo/*
更具体;
/hotels/{hotel}
比/hotels/*
更具体;
任意模式都比/**
更具体;
模式的后缀匹配
Spring MVC默认自动执行.*
的后缀匹配,所以当一个控制器匹配/person
时,其也隐式匹配/person.*
。这样的设计允许通过文件扩展名来说明内容的类型名比如/person.pdf
,/person.xml
等。
然而,这里会有一个常犯的陷阱,当路径最后的片段是URI模版变量时,如/person/{id}
,请求/person/1.json
可以正确匹配路径,变量 id=1,拓展名为json,可当id自身包含点.
的时候,如/person/joe@email.com
,那匹配结果就不是我们所期望的,显然.com
不是文件扩展名而是参数的一部分。
我们可以避免让参数里出现点.
,或者出现点.
的时候对他进行encode。
然而解决这个问题的正规方法应该是配置Spring MVC,是否启用后缀模式,或者只对注册的文件扩展名做后缀匹配。至于如何配置,这里暂且不讲。