深入解读Spring Framework Web MVC(第四弹:使用@RequestMapping映射请求)

@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,是否启用后缀模式,或者只对注册的文件扩展名做后缀匹配。至于如何配置,这里暂且不讲。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值