5、Spring MVC 之 URI
发布日期:2021-06-29 12:52:07 浏览次数:2 分类:技术文章

本文共 9522 字,大约阅读时间需要 31 分钟。

URI模板可以用于方便访问方法上标注了@RequestMapping中的URL中指定的元素.URI模板就像一个URI的String,包含一个或多个变量名.替换掉这些变量的值时,URI模板就变成了一个URI。例如,这个URI模板{userId}包含变量userId。当使用这个URL访问时,Spring MVC就会把这个变量替换为fred.

1、URI Template

在Spring MVC中,你在方法参数中能够使用@PathVariable这个注解用来绑定这个值用来替换URI模板中的变量。

@RequestMapping(path="/owners/{ownerId}", method=RequestMethod.GET)public String findOwner(@PathVariable String ownerId, Model model) {    Owner owner = ownerService.findOwner(ownerId);    model.addAttribute("owner", owner);    return "displayOwner";}

在上面这个例子中,” /owners/{ownerId}”这个URI模板用方法中变量名的ownerId来指定。在Controller处理request请求的时候,ownerId的值设置为URI中的{ownerId}这个变量。例如,当一个发送/owners/fred这个请求,URI中的ownerId的值就会替换为fred.

注意:当处理@PathVariable的时候,Spring MVC需要根据name找到URI模板对应的变量。你可以在注解中指定它:

@RequestMapping(path="/owners/{ownerId}", method=RequestMethod.GET)public String findOwner(@PathVariable("ownerId") String theOwner, Model model) {    // implementation omitted}

或者如果URI模板变量名称匹配方法参数名称可以省略细节.只要你的代码不是编译没有调试信息.Spring MVC将会用标注了@PathVariable注解的参数用来匹配URI模板的变量名.

@RequestMapping(path="/owners/{ownerId}", method=RequestMethod.GET)public String findOwner(@PathVariable String ownerId, Model model) {    // implementation omitted}

在Contoller中,一个方法可以有多个@PathVariable注解。

@RequestMapping(path="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET)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";}

当@PathVariable注解被用来使用在Map

@Controller@RequestMapping("/owners/{ownerId}")public class RelativePathUriTemplateController {
@RequestMapping("/pets/{petId}") public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { // implementation omitted }}

@PathVariable注解可以用来定义到很多简单类型参数,包括int,long,Date等等.Spring MVC会自动转换,如果转换失败就会抛出TypeMismatchException.你也可以自定义自己的参数用来解析附加的对象类型.可以看以后的blog关于”方法参数与类型转换”和”自定义WebDataBinder初始化”

2、正则表达式的URI模板

有时你需要更多的精确定义URI模板变量。考虑下这个URL”/spring-web/spring-web-3.0.5.jar”,你怎么把它分成多个部分?

@RequestMapping注解同样也支持在URI模板变量使用正则表达式。语法格式为:{varName:regex},其中第一部分定义变量名称,后面定义正则表达式。可以看看下面的例子:

@RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}")    public void handle(@PathVariable String version, @PathVariable String extension) {        // ...    }}

3、Path模式

除URI模板之外,@RequestMapping注解同样支持Ant-style的路径模板.(例如,/myPath/.do).URI模板变量与Ant-style的混全类型也同样支持。(e.g. /owners//pets/{petId})

4、Path模式比较

当一个URL匹配多个模式,一种规则会被用来找到最匹配的。

例如,当/hotels/{hotel}/有一个URI变量与一个通配符,而/hotels/{hotel}/*有一个URI变量与2个通配符。这样会认为第一个URI会比第二个URI更加匹配。

如果2个patterns的数据都相同,其中更长的会被认为是更匹配的。例如,/foo/bar*比/foo/更长,这样/foo/bar会被认为是比后者更加匹配。

当2个patterns有同样的数量与长度。那个拥有更少的通配符的会被认为更加匹配。例如/hotels/{hotel}比/hotels/*更加匹配。

还有一些额外的特殊规则:

  1. 默认映射模式:例如:/**的匹配度会低于其它模式。例如,/api/{a}/{b}/{c}的匹配度会就会高于它。
  2. 带前缀的模式:例如:/public/**的配置度会低于其它的模式,但是并不包含2个通配符的。例如/public/path3/{a}/{b}/{c}的匹配度就会高于它。

如果你想了解全部细节,可以看AntPathMatcher中的AntPatternComparator。注意:PathMatcher可以自定义。这允许自定义URL映射相关的各种设置和路径匹配。

下面是一个示例Java配置:

@Configuration@EnableWebMvcpublic class WebConfig extends WebMvcConfigurerAdapter {
@Override public void configurePathMatch(PathMatchConfigurer configurer) { configurer .setUseSuffixPatternMatch(true) .setUseTrailingSlashMatch(false) .setUseRegisteredSuffixPatternMatch(true) .setPathMatcher(antPathMatcher()) .setUrlPathHelper(urlPathHelper()); } @Bean public UrlPathHelper urlPathHelper() { //... } @Bean public PathMatcher antPathMatcher() { //... }}

同样也可以使用XML,使用元素。

5、带有占位符的path模式

@RequestMapping注解中的pattern同样支持${…​} 占位符。这样就不利于对局部属性和/或系统属性和环境变量。在一个controller中如果需要配置自定义的映射这样可能会有用。如果想要了解更多关于占位符的信息。可以看看PropertyPlaceholderConfigurer 这个类的javadocs。

6、后缀模式的匹配

Spring MVC当执行”.“这种后缀模式时,它会默认执行匹配到一个controller映射到/person可样也会暗中地映射到/person.。这样通过URL模式(e.g. /person.pdf, /person.xml)会很容易的请求代表不同资源。

后缀模式匹配可以关闭或限制为一组明确注册的用于内容协商的扩展路径。这通常是建议减少歧义与通用请求映射,例如/person/{id}这个URI。一个点可能不代表一个文件扩展名,.e.g. /person/joe@email.com与/person/joe@email.com.json。此外,如下请注意解释后缀模式匹配以及在某些情况下可以使用内容协商尝试恶意攻击和有意义有充分的理由来限制他们。

7、后缀模式匹配与RFD

反射型文件下载(RFD)攻击是Trustwave在2014年的一篇论文中描述的。这个攻击与XSS类似。它是依靠输入(e.g. 请求参数,URI变量)做为响应的反映。然而JavaScript插入HTML不同的是,一个RFD攻击依赖于浏览器切换执行下载和如果双击运行基于文件扩展名,它会作为一个可执行的脚本处理这个反应。 (e.g. .bat, .cmd).

在Spring MVC中@ResponseBody与ResponseEntity方法具有风险。客户可以通过URL路径请求包括扩展,因为他们可以呈现不同的内容类型。但是要注意,禁用后缀模式匹配和禁用路径扩展的使用内容协商的目的仅是有效的防止RFD攻击。

为了对RFD全面的保护,在Spring MVC可以优先添加”Content-Disposition:inline;filename=f.txt”在reponse body中,这样显示一个固定和安全下载文件文件名。这是只有在URL路径包含一个文件扩展名,既不是白名单也不是明确注册用于内容协商。然而,直接当url输入到浏览器中可能有副作用。

许多常见的路径扩展默认白名单。此外REST API调用通常不应该被用作直接在浏览器的url。然而使用自定义的应用程序HttpMessageConverter实现可以明确注册文件扩展名对内容协商和附加头不会被添加这样的扩展。

注意:这是最初引入作为工作的一部分在.以下是额外的建议报告:

  1. 编码而不是逃避JSON响应。对于如何做,可以看Spring的一个例子。see
  2. 配置后缀模式匹配关闭或限制明确注册后缀。
  3. 配置内容协商的属性“useJaf”和“ignoreUnknownPathExtensions”设置为false,将导致406响应与未知扩展url。但是要注意,如果url自然预计快要结束的时候有一个点,这样做可能不好。
  4. 在响应头上添加”X-Content-Type-Options: nosniff”.Spring Security 4会默认干这件事。

8、矩阵变量

在URI说明书中定义了路径段内的可能性包括名称-值对.没有特定的术语规范.一般“URI路径参数”可以应用。虽然”“很独特。来自Tim Berners-Lee的一个老的post,也经常使用,相当出名。在Spring MVC中这些被称为矩阵变量。

矩阵变量可以出现在任何路径部分。每一个matrix变量通过”;”(分号)分隔。例如:”/cars;color=red;year=2012”.多个值可以通过”,”(逗号)来分隔.例如:”color=red,green,blue”或者使用变量名来做分隔”color=red;color=green;color=blue”。

如果一个URL将包含矩阵变量,他请求映射模式必须代表他们与URI模板。这样可以确保请求可以正确匹配无论矩阵变量存在与否和给他们以什么顺序提供的。

下面是一个例子,提取矩阵变量”q”:

// GET /pets/42;q=11;r=22@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET)public void findPet(@PathVariable String petId, @MatrixVariable int q) {    // petId == 42    // q == 11}

因为所有路径片段可能包含矩阵变量,在某些情况下,你将需要更具体的确定的变量预计:

// GET /owners/42;q=11/pets/21;q=22@RequestMapping(path = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)public void findPet(        @MatrixVariable(name="q", pathVar="ownerId") int q1,        @MatrixVariable(name="q", pathVar="petId") int q2) {    // q1 == 11    // q2 == 22}

一个矩阵变量可以定义为可选的,默认值指定:

// GET /pets/42@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET)public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {    // q == 1}

所有的矩阵变量可以通过Map获取:

// GET /owners/42;q=11;r=12/pets/21;q=22;s=23@RequestMapping(path = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)public void findPet(        @MatrixVariable Map
matrixVars, @MatrixVariable(pathVar="petId"") Map
petMatrixVars) { // matrixVars: ["q" : [11,22], "r" : 12, "s" : 23] // petMatrixVars: ["q" : 11, "s" : 23]}

注意:为了确保能够使用matrix变量。你必须设置RequestMappingHandlerMapping中的属性removeSemicolonContent为false。默认它是true。你可以通过自定义MVC通过Java Config.也就是自定义RequestMappingHandlerMapping 。

@Configurationpublic class WebConfig extends DelegatingWebMvcConfiguration {
@Override public void addInterceptors(InterceptorRegistry registry){ // ... } @Override @Bean public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { // Create or let "super" create the adapter // Then customize one of its properties }}

同时,你也可以通过xml通过mvc命名空间来做。元素有一个enable-matrix-variables属性。这个值应该设置为true。默认的它被设置为false.

9、Consumable Media Types

你可以通过指定特定的consumable media types去缩小你的匹配程度。请求头里面的Content-Type与你的media type匹配了,该请求才会有效的匹配。例如:

@Controller@RequestMapping(path = "/pets", method = RequestMethod.POST, consumes="application/json")public void addPet(@RequestBody Pet pet, Model model) {    // implementation omitted}

Consumable media type也可以使用非号.例如!text/plain 将会匹配所有Content-Type不是text/plain的请求。同样也可以使用MediaType这个类中使用的常量.比如说APPLICATION_JSON_VALUE和APPLICATION_JSON_UTF8_VALUE。

注意:consumes条件在type和方法级别上都是支持的.和大多数情况不同的是,当你在type上使用Consumable media的时候,method-level的Consumable media将会去覆盖掉type上的Consumable media,而不是去继承他。

10、Producible Media Types

你可以指定producible media types去精确匹配你的请求,可接受的请求头里面只要匹配了你配置的值的里面的其中一个,该请求就会被匹配上。例如:

@Controller@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)@ResponseBodypublic Pet getPet(@PathVariable String petId, Model model) {    // implementation omitted}

请注意,在produces条件中还可以指定的媒体类型选择指定字符集。例如,我们默认配置指定与在上面的代码片段中相同的媒体类型在MappingJackson2HttpMessageConverter中,包含UTF-8字符集。

与consumers一样producible media type表达式也同样支持非号。例如使用!text/plain就会匹配所有请求中Accept头中不是”text/plain”.同样也可以使用MediaType这个类中使用的常量.比如说APPLICATION_JSON_VALUE和APPLICATION_JSON_UTF8_VALUE。

注意:produces条件在type和方法级别上都是支持的.和大多数情况不同的是,当你在type上使用produces media的时候,method-level的produces media将会去覆盖掉type上的produces media,而不是去继承他。

11、请求参数与Header值

你可以通过指定特定的请求参数条件去缩小你的匹配.例如”myParam”, “!myParam”或者”myParam=myValue”.前两个测试请求参数存在/没有而第三个主要是一个特定的参数值。这是一个关于请求参数值条件的例子:

@Controller@RequestMapping("/owners/{ownerId}")public class RelativePathUriTemplateController {
@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET, params="myParam=myValue") public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { // implementation omitted }}

同样可以做测试请求头的存在/缺失或基于一个特定的请求头的值:

@Controller@RequestMapping("/owners/{ownerId}")public class RelativePathUriTemplateController {
@RequestMapping(path = "/pets", method = RequestMethod.GET, headers="myHeader=myValue") public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { // implementation omitted }}

注意:即使你可通过通配符来匹配Content-type与Accept header的值。 (例如 “content-type=text/*”可以”text/plain” and “text/html”),但是推荐你最好使用consumes和produces conditions来分别代替它们。因为 它们是专门用于这一目的。

因为水平有限,翻译不足之处还望见谅。

原文地址:

转载地址:https://carlzone.blog.csdn.net/article/details/51975615 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:6、Spring MVC 之 定义@RequestMapping处理方法
下一篇:UML类图几种关系的总结

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年04月30日 12时32分02秒