我正在使用Jersey实现REST API。我想验证我的服务的所有输入(查询参数,路径参数,DTO),并且我正在研究一些选项 - 看起来它的工作是Jersey Bean Validation。我还希望强烈输入服务中的所有内容 - 例如,而不是使用String
来表示所有数据位,您可以使用这样的函数:
public Order getOrder(String customerId);
而是为每个数据位定义类型(这样做的好处是让编译器捕获传递给函数的不正确数据,能够混淆toString
方法中的基础值进行日志记录,知道如果你有一个实例,那么value绝对有效),所以你最终会得到这样的函数:
public Order getOrder(CustomerId customerId);
这样的类型:
public class CustomerId {
private final String value;
public CustomerId(String value) {
this.value = validate(value);
}
public String getValue() {
return value;
}
private String validate(String value) {
// Do some validation here
}
}
Jersey Bean Validation示例不使用上面的强类型。例如:
@Path("/")
class MyResourceClass {
@POST
@Consumes("application/x-www-form-urlencoded")
public void registerUser(
@Pattern(regexp="[a-zA-Z -]{1,50}") @FormParam("name") String name) {
...
}
}
验证的内置很好,因为你可以免费获得一些功能:
然而,有一些问题:
有谁知道如何获得所有这些好处。我尝试定义这样的类型:
public class CustomerId {
private final String value;
public CustomerId(String value) {
this.value = validate(value);
}
public String getValue() {
return value;
}
private String validate(String value) {
if (!Pattern.matches("[a-zA-Z -]{1,50}", value)) {
throw new ConstraintViolationException(new HashSet<ConstraintViolation<?>>());
}
return value;
}
}
但似乎泽西没有以同样的方式处理异常,如果验证失败,你获得的响应代码是404而不是400。
有谁知道如何充分利用两个世界?
答案 0 :(得分:1)
这是关于构造@XxxParam
s
...如果[...]注释了
@MatrixParam
,@QueryParam
或@PathParam
,则实现必须生成NotFoundException
(404状态)的实例,包装抛出的异常而没有实体;如果字段或属性使用@HeaderParam
或@CookieParam
注释,那么实现必须生成一个实例BadRequestException
(400状态)包装抛出的异常而没有实体。
虽然此处未列出,但@FormParam
属于400括号。
“有没有人知道如何充分利用两个世界?”
我们可以通过抛出WebApplicationException
来覆盖此行为。然后,我们可以为异常创建ExceptionMapper
,然后委派给通常处理ExceptionMapper
的{{1}}。我找不到关于此行为的任何清除详细信息。我的意思是你会期望ConstraintViolationException
应该被调用,但如果它不是ExceptionMapper
的实例则不会。因此,您可以将异常扩展为WebApplicationException
。
WebApplicationException
然后为它创建一个public static class MyException extends WebApplicationException {
private final ConstraintViolationException cve;
public MyException(ConstraintViolationException cve) {
this.cve = cve;
}
public ConstraintViolationException getConstraintViolationException() {
return cve;
}
}
。在映射器中,我们只需委托original mapper that handles ConstraintViolationException
ExceptionMapper
然后你可以抛出public static class MyExceptionMapper implements ExceptionMapper<MyException> {
@Context
private Providers providers;
@Override
public Response toResponse(MyException exception) {
ExceptionMapper<ValidationException> mapper
= providers.getExceptionMapper(ValidationException.class);
return mapper.toResponse(exception.getConstraintViolationException());
}
}
。如果您不关心错误响应正文,并且您想要的只是400状态,则可以忘记上面的所有内容并简单地抛出MyException
。或者,如果您不关心BadRequestException
映射器发出的响应实体,您可以在ConstraintViolationException
中创建自己的响应,或在MyExceptionMapper
内创建Response
} class并将CustomerId
构造函数传递给它。所以你有一些选择。
我可以看到这种方法令人头疼的是你需要创建自己的BadRequestException
。那可以很快变老。
我能看到的另一种方法是使用ConstraintViolation
和@BeanParam
@Valid
这种方法的问题在于您的bean现在卡在public static class CustomerId {
@FormParam("cust")
@Pattern(regexp="[a-zA-Z -]{1,50}")
private String value;
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
@POST
@Path("form")
@Consumes("application/x-www-form-urlencoded")
public String postForm(@BeanParam @Valid CustomerId custId) {
,并且无法与其他@FormParam
重复使用。
所以你需要做出一些权衡。希望这能为您提供一些好的信息。
哦,我能想到的最后一个选项,与上面的第二个选项类似,但你没有被绑定到bean中的@XxxParam
@XxxParam
认为最后一个选项可能是要走的路,但你仍然需要记住总是使用public static class CustomerId {
//@FormParam("cust")
@javax.validation.constraints.Pattern(regexp="[a-zA-Z -]{1,50}")
private String value;
public CustomerId(String value) {
//this.value = validate(value);
this.value = value;
}
...
}
@POST
@Path("form")
@Consumes("application/x-www-form-urlencoded")
public String postForm(@FormParam("cust") @Valid CustomerId custId) {
进行注释,这听起来像是你试图避免的。