对路径变量使用MVC类型转换,并在空参数上返回404
问题内容:
我的控制器。注意自定义@Exists
注释:
@RestController
public class ClientApiController {
@RequestMapping(path = "/{client}/someaction", method = RequestMethod.GET)
String handleRequest(@Exists Client client) {
// ...
}
}
该Exists
注释:
/**
* Indicates that a controller request mapping method parametet should not be
* null. This is meant to be used on model types to indicate a required entity.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Exists {}
转换器将String
path变量从转换为Client
实例:
@Component
public class StringToClient implements Converter<String, Client> {
@Autowired
private ClientDAO clientDAO;
@Override
public Client convert(String source) {
return clientDAO.getClientById(source);
}
}
ResourceNotFoundException
用于触发404 的异常
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
}
我的控制器方法根据需要接收转换后的客户端。如果client
URL中使用的ID与客户端匹配,则一切正常。如果id不匹配,则controller方法中的client
参数为
空(使用默认构造函数)null
handle()
。
我现在无法工作的是 声明式
检查Client是否不为空(即,该ID指向现有的客户端)。如果为null,ResourceNotFoundException
则应引发a。检查方法主体中的参数是否为null并抛出我的自定义ResourceNotFoundException
很容易,但是重复。同样,此声明方法应适用于实现接口的所有模型类,ModelWithId
因此可以用于多种模型类型。
我已经搜索过Spring文档,但还没有找到实现该目标的方法。我需要在类型转换之后和控制器handleRequest
方法之前的某个位置插入一些处理。
我正在使用Spring Boot 1.3.3
问题答案:
在类型转换之后,在控制器的方法之前,需要进行验证。您可以实现自定义验证器并在其中引发异常。将新的验证器添加到DataBinder
,并将方法的参数标记为@Validated
:
@RestController
public class ClientApiController {
@InitBinder
public void initBinder(DataBinder binder){
binder.addValidators(new Validator() {
@Override
public boolean supports(Class<?> aClass) {
return aClass==Client.class;
}
@Override
public void validate(Object o, Errors errors) {
Client client = (Client)o;
if(client.getId()==null) throw new ResourceNotFoundException();
}
});
}
@RequestMapping(path = "/{client}/someaction", method = RequestMethod.GET)
String handleRequest(@Validated @Exists Client client) {
// ...
}
@RequestMapping(path = "/{client}/anotheraction", method = RequestMethod.GET)
String handleAnotherRequest(@Validated @Exists Client client) {
// ...
}
}
当然,您可以将验证器声明为单独的类,并在其他控制器中重复使用它。实际上,您可以在转换器中引发异常,但是有可能在应用程序的其他位置毫无例外地需要转换。