Spring @GetMapping 被忽略

Posted

技术标签:

【中文标题】Spring @GetMapping 被忽略【英文标题】:Spring @GetMapping is being ignored 【发布时间】:2020-05-07 10:01:35 【问题描述】:

我有以下控制器:

@RestController
@RequestMapping("/api/brand")
public class CarController 

  private final CarService carService;

  @Autowird
  public CarController(CarService carService) 
    this.carService = carService;
  

  @GetMapping
  public Resources<Car> getCars(@PathVariable("brand") String brand) 
    return new Resources<>(carService.getCars(brand));
  

  @GetMapping(value = "/model")
  public Car getModel(@PathVariable("brand") String brand, @PathVariable("model") String model) 
    return carService.getCar(brand, model);
  

我希望对http://localhost:8080/api/bmw 的http GET 调用返回getCars 方法的结果。相反,调用被委托给getModel 方法。这会返回错误,因为没有 model 路径变量。

为什么我的 http 调用被委派给了不正确的 @GetMapping

在这里你可以看到我通过hateoas拉入的spring-boot-starter-web的版本:

[INFO] +- org.springframework.boot:spring-boot-starter-hateoas:jar:2.1.9.RELEASE:compile [信息] | +- org.springframework.boot:spring-boot-starter-web:jar:2.1.9.RELEASE:compile [信息] | | - org.springframework.boot:spring-boot-starter-tomcat:jar:2.1.9.RELEASE:compile [信息] | | +- org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.26:compile [信息] | | - org.apache.tomcat.embed:tomcat-embed-websocket:jar:9.0.26:compile [信息] | +- org.springframework.hateoas:spring-hateoas:jar:0.25.2.RELEASE:compile [信息] | - org.springframework.plugin:spring-plugin-core:jar:1.2.0.RELEASE:compile

我已经启用了 Spring Actuator 的 mappings 端点,我什至可以看到被忽略的端点可用:


  "handler": "public org.springframework.hateoas.Resources<com.example.Car> com.example.CarController.getCars(java.lang.String)",
  "predicate": "GET /api/brand, produces [application/hal+json]",
  "details": 
    "handlerMethod": 
      "className": "com.example.CarController",
      "name": "getCars",
      "descriptor": "(Ljava/lang/String;)Lorg/springframework/hateoas/Resources;"
    ,
    "requestMappingConditions": 
      "consumes": [],
      "headers": [],
      "methods": [
        "GET"
      ],
      "params": [],
      "patterns": [
        "/api/brand"
      ],
      "produces": [
        
          "mediaType": "application/hal+json",
          "negated": false
        
      ]
    
  

编辑我添加了一个interceptor,让我可以看到handlerMethod 的目标是什么。

handlerMethod 是正确的:

public org.springframework.hateoas.Resources com.example.CarController.getCars(java.lang.String)

但我仍然收到以下错误:

内部服务器错误:缺少字符串类型方法参数的 URI 模板变量“模型”

我无法理解handlerMethod 不期望model 参数这一事实,但spring 仍然因此而引发错误。

【问题讨论】:

【参考方案1】:

在您的情况下,@RequestMapping("/api/brand") 需要一个输入品牌,因为您在类级别使用了注释,所以找不到该输入品牌。您可以通过以下方式更正它:

@RestController
@RequestMapping("/api")
public class CarController 

  private final CarService carService;

  @Autowird
  public CarController(CarService carService) 
    this.carService = carService;
  

  @GetMapping(value = "/brand")
  public Resources<Car> getCars(@PathVariable("brand") String brand) 
    return new Resources<>(carService.getCars(brand));
  

  @GetMapping(value = "/brand/model")
  public Car getModel(@PathVariable("brand") String brand, @PathVariable("model") String model) 
    return carService.getCar(brand, model);
  

所以 getCars() 方法将需要一个输入品牌,而 getModel() 将需要两个输入品牌和型号。希望对您有所帮助!

【讨论】:

【参考方案2】:

再次检查您的方法映射:

正如你所说,你想根据品牌调用 gatCars 方法,你必须在 get 映射中提供值,所以函数应该是:

 @GetMapping(value = "/model")
  public Resources<Car> getCars(@PathVariable("brand") String brand) 
    return new Resources<>(carService.getCars(brand));
  

请求将通过 getModel 匹配签名。更正 getModel 签名如下。

http://localhost:8080/api/bmw/x5

  @GetMapping(value = "/model/brand")
  public Car getModel(@PathVariable("brand") String brand, @PathVariable("model") String model) 
    return carService.getCar(brand, model);
  

【讨论】:

【参考方案3】:

我认为对于整个控制器类,不能将路径变量放入注解@RequestMapping。我建议将您的@RequestMapping("/api/brand") 更改为@RequestMapping("/api") 然后更改

  @GetMapping
  public Resources<Car> getCars(@PathVariable("brand") String brand) 
    return new Resources<>(carService.getCars(brand));
  

  @GetMapping(value = "/model")
  public Car getModel(@PathVariable("brand") String brand, @PathVariable("model") String model) 
    return carService.getCar(brand, model);
  

  @GetMapping(value = "/brand")
  public Resources<Car> getCars(@PathVariable("brand") String brand) 
    return new Resources<>(carService.getCars(brand));
  

  @GetMapping(value = "/brand/model")
  public Car getModel(@PathVariable("brand") String brand, @PathVariable("model") String model) 
    return carService.getCar(brand, model);
  

【讨论】:

Adding a placeholder to the @RequestMapping is allowed。我们的应用程序中有很多 @RestControllers 可以像这样正常工作。只有这一个开始表现不正确。 @Titulum 很好,很高兴知道。我仍然会尝试我的建议,看看 Spring 是否以某种方式混合了匹配的签名并以某种方式给你一个误报匹配。我的建议使签名之间的差异更加明显。 我发布的链接不是在谈论同类型的占位符,而是如果你check out the reference, they have an example there。我已经尝试过你的建议,但它会导致相同的结果。我添加了mappings 端点(Spring Actuator)的一些输出,以表明端点确实存在。 好吧,此时我唯一的想法是将@GetMapping(value = "/brand/model") 更改为@GetMapping(value = "/brand/model/model"),这样映射匹配就会看到这两个URL 互斥。但除此之外,我目前没有任何想法 这是个好主意,但我可能需要自定义模式匹配器。【参考方案4】:

原来@RestControllerAdvice 是罪魁祸首:

@RestControllerAdvice(assignableTypes = CarController.class)
public class InterceptModelPathParameterControllerAdvice 

  @Autowired
  CarService carService;

  @ModelAttribute
  public void validateModel(@PathVariable("model") String model) 
    if (!carService.isSupportedModel(model)) throw new RuntimeException("This model is not supprted by this application.");
  

因为getCars 方法没有@PathVariable("model"),所以抛出异常。

【讨论】:

以上是关于Spring @GetMapping 被忽略的主要内容,如果未能解决你的问题,请参考以下文章

Spring 似乎忽略 required=false 请求参数

@GetMapping@PostMapping@PutMapping@DeleteMapping@PatchMapping

@RequestMapping 和 @GetMapping @PostMapping 区别

Sqoop 函数“--map-column-hive”被忽略

在 Spring 的 GetMapping 中使用参数会导致多个参数的处理程序方法不明确

Spring Boot:以 Pageable 作为请求参数的 @GetMapping 无法按预期工作