使用 JAX-RS 时,有没有办法将变量放入自定义 ConstraintValidator 中?
Is there a way to get a variable in to a custom ConstraintValidator when using JAX-RS?
当 de-serializing 一个 JSON 有效载荷时,我想使用自定义验证器验证一个 phone 数字。此验证器需要知道客户所在的国家/地区才能正确解析 phone 号码。我可以在 http header Accept-Language 中找到国家/地区。
问题是我如何为自定义验证器提供这些额外的元数据?
我确信@Context 会将 HttpHeaders 注入到 PhoneNumberValidator 中,但这不起作用:
@Context
private HttpHeaders mHttpHeaders;
字段为空。
API终点:
@Path("customer")
public class CustomerResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response postCustomer(@Valid Customer pCustomer) {
return Response.ok(pCustomer).build();
}
}
POJO:
@Getter
@Setter
public class Customer {
@NotNull
private String firstName;
@ValidPhoneNumber
private String mobileNumber;
}
验证器接口:
@Documented
@Retention(RUNTIME)
@Target({ TYPE, FIELD })
@Constraint(validatedBy = { PhoneNumberValidator.class })
public @interface ValidPhoneNumber {
String message() default "Phone number is not valid!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
自定义验证器:
@Slf4j
public class PhoneNumberValidator implements ConstraintValidator<ValidPhoneNumber, String> {
@Override
public boolean isValid(String vTelephone, ConstraintValidatorContext pContext) {
final PhoneNumberUtil vPhoneNumberUtil = PhoneNumberUtil.getInstance();
// TODO: Get the country in here somehow
final String vCountry = "GB";
try {
PhoneNumber vPhoneNumber = vPhoneNumberUtil.parse(vTelephone, vCountry);
if (!vPhoneNumberUtil.isValidNumber(vPhoneNumber)) {
log.error("Invalid {} phone number: {}", vCountry, vTelephone);
return false;
}
} catch (NumberParseException e) {
log.error("Error parsing {} as a phone number in {}: {}", vTelephone, vCountry, e.getMessage());
return false;
}
return true;
}
}
HttpHeaders
object(还)不是 CDI 管理的 bean - 我说是因为它是 Jakarta 的 JAX-RS 3.0 成为 CDI bean 的计划的一部分,但对于 JAX-RS 2.1 和更早版本,它不是。
但是资源 class 有可能是 CDI 管理的 bean。因此,您可以将 HttpHeaders
注入 CustomerResource
class,然后提供一个 getter 方法来访问 headers。然后在你的验证器 class 中使用 @Inject
来注入你的资源。然后你可以从验证器访问headers。
像这样:
@RequestScoped
public class PhoneNumberValidator implements ConstraintValidator<ValidPhoneNumber, String> {
@Inject
CustomerResource customerResource;
@Override
public boolean isValid(String vTelephone, ConstraintValidatorContext pContext) {
// Get the country in here using the original resource instance
final String vCountry = customerResource.getHeaderString("COUNTRY");
try {
validate(vTelephone, vCountry);
} catch (Throwable t) {
System.out.printf("Error parsing {0} as a phone number in {1}: {2}", vTelephone, vCountry, t.getMessage());
return false;
}
return true;
}
...
像这样更新资源class:
@RequestScoped
@Path("customer")
public class CustomerResource {
@Context
private HttpHeaders headers;
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response postCustomer(@Valid Customer pCustomer) {
return Response.ok(pCustomer).build();
}
String getHeaderString(String headerName) {
return headers.getHeaderString(headerName);
}
...
我在这里写了一个可能有用的工作示例:
https://github.com/andymc12/ContextVarInValidatorSample
安迪,希望这对您有所帮助
当 de-serializing 一个 JSON 有效载荷时,我想使用自定义验证器验证一个 phone 数字。此验证器需要知道客户所在的国家/地区才能正确解析 phone 号码。我可以在 http header Accept-Language 中找到国家/地区。
问题是我如何为自定义验证器提供这些额外的元数据?
我确信@Context 会将 HttpHeaders 注入到 PhoneNumberValidator 中,但这不起作用:
@Context
private HttpHeaders mHttpHeaders;
字段为空。
API终点:
@Path("customer")
public class CustomerResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response postCustomer(@Valid Customer pCustomer) {
return Response.ok(pCustomer).build();
}
}
POJO:
@Getter
@Setter
public class Customer {
@NotNull
private String firstName;
@ValidPhoneNumber
private String mobileNumber;
}
验证器接口:
@Documented
@Retention(RUNTIME)
@Target({ TYPE, FIELD })
@Constraint(validatedBy = { PhoneNumberValidator.class })
public @interface ValidPhoneNumber {
String message() default "Phone number is not valid!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
自定义验证器:
@Slf4j
public class PhoneNumberValidator implements ConstraintValidator<ValidPhoneNumber, String> {
@Override
public boolean isValid(String vTelephone, ConstraintValidatorContext pContext) {
final PhoneNumberUtil vPhoneNumberUtil = PhoneNumberUtil.getInstance();
// TODO: Get the country in here somehow
final String vCountry = "GB";
try {
PhoneNumber vPhoneNumber = vPhoneNumberUtil.parse(vTelephone, vCountry);
if (!vPhoneNumberUtil.isValidNumber(vPhoneNumber)) {
log.error("Invalid {} phone number: {}", vCountry, vTelephone);
return false;
}
} catch (NumberParseException e) {
log.error("Error parsing {} as a phone number in {}: {}", vTelephone, vCountry, e.getMessage());
return false;
}
return true;
}
}
HttpHeaders
object(还)不是 CDI 管理的 bean - 我说是因为它是 Jakarta 的 JAX-RS 3.0 成为 CDI bean 的计划的一部分,但对于 JAX-RS 2.1 和更早版本,它不是。
但是资源 class 有可能是 CDI 管理的 bean。因此,您可以将 HttpHeaders
注入 CustomerResource
class,然后提供一个 getter 方法来访问 headers。然后在你的验证器 class 中使用 @Inject
来注入你的资源。然后你可以从验证器访问headers。
像这样:
@RequestScoped
public class PhoneNumberValidator implements ConstraintValidator<ValidPhoneNumber, String> {
@Inject
CustomerResource customerResource;
@Override
public boolean isValid(String vTelephone, ConstraintValidatorContext pContext) {
// Get the country in here using the original resource instance
final String vCountry = customerResource.getHeaderString("COUNTRY");
try {
validate(vTelephone, vCountry);
} catch (Throwable t) {
System.out.printf("Error parsing {0} as a phone number in {1}: {2}", vTelephone, vCountry, t.getMessage());
return false;
}
return true;
}
...
像这样更新资源class:
@RequestScoped
@Path("customer")
public class CustomerResource {
@Context
private HttpHeaders headers;
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response postCustomer(@Valid Customer pCustomer) {
return Response.ok(pCustomer).build();
}
String getHeaderString(String headerName) {
return headers.getHeaderString(headerName);
}
...
我在这里写了一个可能有用的工作示例: https://github.com/andymc12/ContextVarInValidatorSample
安迪,希望这对您有所帮助