【问题标题】:Spring @GetMapping is being ignoredSpring @GetMapping 被忽略
【发布时间】: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 仍然因此而引发错误。

【问题讨论】:

    标签: java spring spring-restcontroller request-mapping get-mapping


    【解决方案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"),所以抛出异常。

        【讨论】:

          猜你喜欢
          • 2014-04-28
          • 2018-07-26
          • 1970-01-01
          • 2020-03-12
          • 1970-01-01
          • 2012-05-19
          • 2015-08-03
          • 2021-08-26
          • 2021-11-04
          相关资源
          最近更新 更多