1. 概述

URI规范(RFC 3986)[1]定义了URI路径参数作为名称值对。矩阵变量是Spring自创的一个术语,它提供了一种处理和解析URI路径参数的替代实现。

矩阵变量在Spring MVC 3.2版本中开始支持,旨在简化包含大量参数的请求

本文将展示如何简化使用变量或可选路径参数的复杂GET请求,这些参数分布在URI的不同路径段中。

2. 配置

启用Spring MVC矩阵变量,首先需要配置:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setRemoveSemicolonContent(false);
        configurer.setUrlPathHelper(urlPathHelper);
    }
}

如果不进行配置,默认情况下它们是禁用的。

3. 如何使用矩阵变量

这些变量可以出现在路径的任何部分,使用等号(“=”)来提供值,使用分号(“;”)来分隔每个矩阵变量。在同一路径上,我们也可以重复使用同一个变量名或使用逗号(“,”)来区分不同的值。

我们的示例有一个提供员工信息的控制器,每个员工都有工作区域,我们可以按此属性进行搜索。以下请求可用于搜索:

http://localhost:8080/spring-mvc-java-2/employeeArea/workingArea=rh,informatics,admin

或者像这样:

http://localhost:8080/spring-mvc-java-2
  /employeeArea/workingArea=rh;workingArea=informatics;workingArea=admin

在Spring MVC中引用这些变量时,应使用@MatrixVariable注解。

在我们的示例中,我们将使用Employee类:

public class Employee {

    private long id;
    private String name;
    private String contactNumber;

    // standard setters and getters 
}

以及Company类:

public class Company {

    private long id;
    private String name;

    // standard setters and getters
}

这两个类将绑定请求参数。

4. 定义矩阵变量属性

我们可以为变量指定必需或默认属性。在下面的例子中,contactNumber是必需的,因此必须包含在我们的路径中,如:

http://localhost:8080/spring-mvc-java-2/employeesContacts/contactNumber=223334411

请求将由以下方法处理:

@RequestMapping(value = "/employeesContacts/{contactNumber}", 
  method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<List<Employee>> getEmployeeByContactNumber(
  @MatrixVariable(required = true) String contactNumber) {
    List<Employee> employeesList = new ArrayList<Employee>();
    ...
    return new ResponseEntity<List<Employee>>(employeesList, HttpStatus.OK);
}

结果,我们将获取所有拥有电话号码为223334411的员工。

5. 补充参数

矩阵变量可以补充路径变量。

例如,我们正在搜索一个员工的名字,但也可以包含他/她的联系电话号码的起始数字。

这个搜索请求应该如下所示:

http://localhost:8080/spring-mvc-java-2/employees/John;beginContactNumber=22001

请求将由以下方法处理:

@RequestMapping(value = "/employees/{name}", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<List<Employee>> getEmployeeByNameAndBeginContactNumber(
  @PathVariable String name, @MatrixVariable String beginContactNumber) {
    List<Employee> employeesList = new ArrayList<Employee>();
    ...
    return new ResponseEntity<>(employeesList, HttpStatus.OK);
}

结果,我们将获取所有电话号码以22001开头或名字为John的员工。

6. 绑定所有矩阵变量

如果出于某种原因,我们想获取路径上所有可用的变量,可以将它们绑定到一个Map

http://localhost:8080/spring-mvc-java-2/employeeData/id=1;name=John;contactNumber=2200112334

这个请求将由以下方法处理:

@GetMapping("employeeData/{employee}")
@ResponseBody
public ResponseEntity<Map<String, String>> getEmployeeData(
  @MatrixVariable Map<String, String> matrixVars) {
    return new ResponseEntity<>(matrixVars, HttpStatus.OK);
}

当然,我们可以限制绑定到路径变量的特定部分。例如,如果我们有以下请求:

http://localhost:8080/spring-mvc-java-2/
  companyEmployee/id=2;name=Xpto/employeeData/id=1;name=John;
  contactNumber=2200112334

并且只想获取属于employeeData部分的所有变量,那么输入参数应为:

@RequestMapping(
 value = "/companyEmployee/{company}/employeeData/{employee}",
 method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<Map<String, String>> getEmployeeDataFromCompany(
  @MatrixVariable(pathVar = "employee") Map<String, String> matrixVars) {
  ...
}

7. 部分绑定

除了简洁性,灵活性是另一个优点,矩阵变量可以以多种方式使用。例如,我们可以从每个路径段获取每个变量。考虑以下请求:

http://localhost:8080/spring-mvc-java-2/
  companyData/id=2;name=Xpto/employeeData/id=1;name=John;
  contactNumber=2200112334

如果我们只想知道companyData段的矩阵变量name,则输入参数应为:

@MatrixVariable(value="name", pathVar="company") String name

8. 防火墙设置

如果应用使用Spring Security,那么默认会使用StrictHttpFirewall。这会阻止看起来像是恶意的请求,包括使用分号分隔的矩阵变量。

我们可以在应用程序配置中自定义这个实现,允许此类变量同时拒绝其他可能的恶意请求。

然而,这种方式可能会让应用面临攻击。因此,应在仔细分析应用和安全需求后才实施这一操作。

9. 总结

本文展示了矩阵变量的一些用法。

理解这种新工具如何处理过于复杂或帮助我们在搜索中添加更多参数至关重要。

所有示例和代码片段的实现可在GitHub项目中找到——这是一个基于Maven的项目,所以导入并运行起来应该非常容易。