Spring:来自多个路径元素的@PathVariable 就像jax-rs?

Spring: @PathVariable from multiple path elements like with jax-rs?

我正在将一些路由从基于 jax-rs 的应用程序迁移到 SpringBoot.在 jax-rs 中,我可以使用 @Path 来定义一个包含多个 URL 路径元素的正则表达式:

@Path("{id:[^/]+/y[\d]{4}/m[\d]{1,2}/d[\d]{1,2}/h[\d]{1,2}}/")

方法主体中的 id 变量将成为 URL 的匹配段,我可以继续我的一天了。

在 Spring 中使用 @RequestMapping 这不起作用。只要在正则表达式中加入正斜杠,就会出现 PatternParseException。

PathContainer pathContainingSlash = PathContainer.parsePath("/api/test/y1978/m07/d15");
PathPatternParser parser = new PathPatternParser();
assertThrows(PatternParseException.class, () -> 
parser.parse("/api/test/{ticketId:y[\d]{4}/m[\d]{1,2}/d[\d]{1,2}}"));

AntPathMatcher 似乎也会出现同样的问题。

AntPathMatcher antPathMatcher = new AntPathMatcher();
assertThrows(IllegalStateException.class, () -> 
  antPathMatcher.extractUriTemplateVariables(
    "/api/test/{ticketId:y[\d]{4}/m[\d]{1,2}/d[\d]{1,2}}",
    "/api/test/y1978/m07/d15"));

这是个问题,因为我有大约 78 个 URL 模式。我将不得不单独定义每个模式,每个路径元素都是一个单独的变量。然后我将不得不使用字符串连接以路径的格式将它们重新组合在一起。

@GetMapping("/{year:y[\d]{4}}/{month:m[\d]1,2}/{day:d[\d]{1,2}")
public ResponseEntity<Void> foo(@PathVariable String year,
  @PathVariable String month, 
  @PathVariable String day) {
    String date = year + "/" + month + "/" + day;
}

除了在我的 SpringBoot 应用程序中使用 Jax-rs 之外,还有其他方法吗?可以像这样写它们,但它似乎不是最优的。

为清楚起见,我真的想要一种方法将多个路径元素从 URL 提取到 @PathVariable 中。我想要这样的东西:

@GetMapping("/api/test/{date:y[\d]{4}/m[\d]{1,2}/d[\d]{1,2}}")
public ResponseEntity<Void> foo(@PathVariable String date) {}

所以那个日期现在等于 y1978/m07/d15

此外,这只是一种示例模式。有 78 种独特的模式,它们具有不同数量的路径元素和元素内容。在使用 @Path 的 Jax-RS 中,我可以将这些正则表达式组合在一起并创建一个路由,并且可以在方法内部访问路径变量。

如何添加 spring-boot-starter-validation 进行验证

  • 需要添加以下jar org.springframework.boot:spring-boot-starter-validation

  • 在控制器顶部添加 @org.springframework.validation.annotation.Validated class

  • @javax.validation.constraints.Patternregex 属性添加到 @PathVariable 方法参数


    @GetMapping("{year}/{month}/{day}")
    public ResponseEntity<Void> foo(
            @PathVariable @Pattern(regexp = "[\d]{4}", message = "year must be ..") String year,
            @PathVariable @Pattern(regexp = "[\d]{1,2}", message = "month must ..") String month,
            @PathVariable @Pattern(regexp = "[\d]{1,2}", message= "day must be ..") String day) {
        String date = year + "/" + month + "/" + day;
  • 到returnhttp 400状态,添加一个方法来处理ConstraintViolationException

    @ExceptionHandler(value = { ConstraintViolationException.class })
    protected ResponseEntity<List<String>> handleConstraintViolations(ConstraintViolationException ex, WebRequest request) {
        List<String> errorMessages = ex.getConstraintViolations().stream()
                .map(violation -> violation.getMessage()).collect(Collectors.toList());
        return new ResponseEntity<List<String>>(errorMessages, HttpStatus.BAD_REQUEST);
    }

这里有更多验证示例: https://reflectoring.io/bean-validation-with-spring-boot/

这里有更多异常处理选项: https://www.baeldung.com/exception-handling-for-rest-with-spring

使用此线程重写路径的可能选项 Spring MVC Getting PathVariables containing dots and slashes

添加

<dependency>
    <groupId>org.tuckey</groupId>
    <artifactId>urlrewritefilter</artifactId>
    <version>4.0.3</version>
</dependency>

将重写规则添加到 src/main/webapp/WEB-INF/urlrewrite.xml

<urlrewrite>
    <rule>
       <from>^/api/test/(y[\d]{4})/(m[\d]{2})/(d[\d]{2})$</from>
       <to>/api/test?date=//</to>
    </rule>
</urlrewrite>

创建控制器方法匹配重写规则的 to 路径与查询参数

@GetMapping("/api/test")
public ResponseEntity<Void> foo(@RequestParam String date) {
    
    System.out.println(date);
    return new ResponseEntity<Void>(HttpStatus.OK);
    
}

添加配置class以使用urlPatterns注册重写过滤器来过滤

@Configuration
public class FiltersConfig {
    @Bean
    public FilterRegistrationBean<Filter> someFilterRegistration() {
        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<Filter>();
        registration.setFilter(rewriteFilter());

        // add paths to filter
        registration.addUrlPatterns("/api/*");

        registration.setName("urlRewriteFilter");
        registration.setOrder(1);
        return registration;
    }

    public Filter rewriteFilter() {
        return new UrlRewriteFilter();
    }
}