对路径变量使用MVC类型转换并在null参数上返回404

时间:2016-03-11 23:37:18

标签: spring spring-mvc spring-boot

我的控制器。请注意自定义@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从路径变量转换为Client实例的转换器:

@Component
public class StringToClient implements Converter<String, Client> {

    @Autowired
    private ClientDAO clientDAO;

    @Override
    public Client convert(String source) {
        return clientDAO.getClientById(source);
    }
}

用于触发404的ResourceNotFoundException例外

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
}

我的控制器方法根据需要接收转换后的客户端。如果URL中使用的client id与客户端匹配,则一切正常。如果id不匹配,则client参数在<{1}}控制器方法中为 null 为空(使用默认构造函数)。

我现在无法工作的是声明性检查客户端是否为空(即id指的是现有客户端)。如果它为null,则应抛出handle()。检查方法体中的参数是否为null并抛出我的自定义ResourceNotFoundException很容易,但重复性(如this一样)。此外,这种声明方法应该适用于实现接口ResourceNotFoundException的所有模型类,因此它可以用于多种模型类型。

我搜索过Spring文档,但我还没有找到如何实现这一目标。我需要在类型转换之后和控制器的ModelWithId方法之前插入一些处理。

我正在使用Spring Boot 1.3.3

1 个答案:

答案 0 :(得分:2)

在类型转换之后和控制器方法之前有一个验证。您可以实现自定义验证器并在其中引发异常。将新验证程序添加到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) {
        // ...
    }
}

当然,您可以将验证器声明为单独的类,并在其他控制器中重复使用它。实际上,您可以在转换器中直接引发异常,但有可能在您的应用程序的其他位置无需例外地进行转换。