如何为带有和不带请求参数的请求定义不同的Spring MVC请求处理程序



Spring MVC有没有办法为没有请求参数的请求和有请求参数的请求定义不同的处理程序?

有一个简单的控制器:

@RestController
@RequestMapping("/strategies")
public class StrategyController {
    ...
    @GetMapping
    public List<Strategy> getAll() {
        return service.getBeans().stream()
            .map(mapper::toDto)
            .collect(toList());
    }
    @GetMapping
    public List<Strategy> search(StrategyFilter filter) {
        return service.search(new StrategySearchSpecification(
                filter.getCode(),
                filter.getName(),
                filter.getType()
            )).stream()
            .map(mapper::toDto)
            .collect(toList());
    }
}

我想要getAll()方法来处理没有请求参数的请求: /strategies

我希望search(StrategyFilter filter)方法可以使用请求参数处理请求: /strategies?name=SomeName&type=SomeType

似乎不可能通过@GetMapping的属性来区分这种情况params因为StrategyFilter的任何属性都可以省略。

在此配置中,我得到一个明显的错误:

Caused by: java.lang.IllegalStateException: Ambiguous mapping. 
Cannot map 'strategyController' method 
public List<Strategy> StrategyController.getAll() to {[/strategies],methods=[GET]}: 
There is already 'strategyController' bean method public List<Strategy> StrategyController.search(StrategyFilter) mapped.

当然,可以写这样的东西:

@GetMapping
public List<Strategy> get(StrategyFilter filter) {
    return noFilterProvided(filter) ? getAll() : search(filter);
}

但是,每次过滤器的属性数量发生变化时,都需要更改"noFilterGiven(StrategyFilter filter("。

Spring 框架使用基于点的匹配。它从可用匹配中选择最高的匹配项。通过标准的人越多,得分就越高。如果您在一个匹配中定义请求的查询参数,则当参数存在时,它将接受。其他情况是另一种情况。

要定义请求的参数,请将它们作为直接属性传递,而不是作为策略过滤器属性传递。在缺少参数的情况下,像这样的实例初始化也会成功(这些 attr 不会被初始化,它们保持默认状态:"/0/false(。因此会发生不明确的匹配错误。

最后:使用直接属性而不是策略过滤器。

设计的另一个问题是直接的StrategySearchSpecification实例化。它不能以这种方式进行单元测试。将其定义为弹簧组件。

@Component
@Getter // Lombok annotation to generate getter methods
@Setter // Lombok annotation to generate setter methods
public class StrategySearchSpecification
{
  private CODE_TYPE code;
  private String name;
  private TYPE_TYPE type;
}

并将其作为参数注入(正确的实现/模拟(并使用它的 setter 方法。

@RestController
@RequestMapping("/strategies")
public class StrategyController {
    ...
    @GetMapping
    public List<Strategy> getAll() {
        return service.getBeans().stream()
            .map(mapper::toDto)
            .collect(toList());
    }
    @GetMapping
    public List<Strategy> search(@RequestParam CODE_TYPE code, @RequestParam String name, @RequestParam TYPE_TYPE type, StrategySearchSpecification specification ) {
        specification.setCode( code );
        specification.setName( name );
        specification.setType( type );
        return service.search( specification
            )).stream()
            .map(mapper::toDto)
            .collect(toList());
    }
}

如果StrategyFilter具有属性nametype,这应该可以工作:

@RestController
@RequestMapping("/strategies")
public class StrategyController {
    ...
    @GetMapping
    public List<Strategy> getAll() {
        return service.getBeans().stream()
            .map(mapper::toDto)
            .collect(toList());
    }
    @GetMapping("{name}/{type}")
    public List<Strategy> search(StrategyFilter filter) {
        return service.search(new StrategySearchSpecification(
                filter.getCode(),
                filter.getName(),
                filter.getType()
            )).stream()
            .map(mapper::toDto)
            .collect(toList());
    }
}

最新更新