1. 概述
在本教程中,我們將探討使用開源框架 Jersey 中的 Bean 驗證。
正如我們在之前的文章中所看到,Jersey 是一個用於開發 RESTful Web Services 的開源框架。
驗證是核實某些數據是否符合預定義的約束的過程。這在大多數應用程序中是一個非常常見的用例。
Java Bean Validation 框架 (JSR-380) 已成為 Java 中處理此類操作的默認標準。為了回顧 Java Bean Validation 的基本知識,請參考我們的上一篇教程。
Jersey 包含一個擴展模塊,用於支持 Bean Validation。要使用此功能,我們首先需要在我們的應用程序中進行配置。在下一部分,我們將看到如何配置我們的應用程序。
3. 應用程序設置
現在,讓我們基於來自《Jersey MVC 支持》出色文章的簡單水果 API 示例進行構建。
3.1. Maven 依賴項
首先,讓我們將 Bean 驗證依賴項添加到我們的 pom.xml 中:
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<version>3.1.1</version>
</dependency>
我們可以從 Maven Central 獲取最新版本。
3.2. 配置服務器
在 Jersey 中,我們通常在自定義資源配置類中註冊我們想要使用的擴展功能。
但是,對於 Bean 驗證擴展,不需要進行此註冊。 幸運的是,這是 Jersey 框架自動註冊的少數擴展之一。
最後,為了將驗證錯誤發送到客户端 我們將一個服務器屬性添加到我們的自定義資源配置:
public ViewApplicationConfig() {
packages("com.baeldung.jersey.server");
property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
}
4. 有效的 JAX-RS 資源方法
在本節中,我們將解釋兩種使用約束註解進行輸入參數驗證的方法:
- 使用內置 Bean Validation API 約束
- 創建自定義約束和驗證器
4.1. 使用內置約束註解
我們首先看一下內置的約束註解:
@POST
@Path("/create")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void createFruit(
@NotNull(message = "Fruit name must not be null") @FormParam("name") String name,
@NotNull(message = "Fruit colour must not be null") @FormParam("colour") String colour) {
Fruit fruit = new Fruit(name, colour);
SimpleStorageService.storeFruit(fruit);
}
在這個例子中,我們使用兩個表單參數
這會對我們的表單參數施加一個簡單的非空約束。
當然,我們將用一個單元測試來演示這一點:
@Test
public void givenCreateFruit_whenFormContainsNullParam_thenResponseCodeIsBadRequest() {
Form form = new Form();
form.param("name", "apple");
form.param("colour", null);
Response response = target("fruit/create").request(MediaType.APPLICATION_FORM_URLENCODED)
.post(Entity.form(form));
assertEquals("Http Response should be 400 ", 400, response.getStatus());
assertThat(response.readEntity(String.class), containsString("Fruit colour must not be null"));
}
在這個例子中,
對於內置的驗證約束列表,請查看 文檔。
4.2. 定義自定義約束註解
有時我們需要施加更復雜的約束。
假設我們使用簡單的水果 API 示例,我們需要驗證所有水果具有有效的序列號:
@PUT
@Path("/update")
@Consumes("application/x-www-form-urlencoded")
public void updateFruit(@SerialNumber @FormParam("serial") String serial) {
//...
}
在這個例子中,
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { SerialNumber.Validator.class })
public @interface SerialNumber {
String message()
default "Fruit serial number is not valid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
接下來,我們將定義驗證器類
public class Validator implements ConstraintValidator<SerialNumber, String> {
@Override
public void initialize(SerialNumber serial) {
}
@Override
public boolean isValid(String serial,
ConstraintValidatorContext constraintValidatorContext) {
String serialNumRegex = "^\\d{3}-\\d{3}-\\d{4}$";
return Pattern.matches(serialNumRegex, serial);
}
}
關鍵點在於
5. 資源驗證
此外,Bean驗證API也允許我們使用註解來驗證對象。
在下一部分,我們將解釋使用該註解驗證資源類兩種不同的方法:
- 首先,請求資源驗證
- 其次,響應資源驗證
讓我們先向我們的Fruit對象添加註解:
@XmlRootElement
public class Fruit {
@Min(value = 10, message = "Fruit weight must be 10 or greater")
private Integer weight;
//...
}
5.1. 請求資源驗證
首先,我們將啓用請求資源驗證中使用註解:
@POST
@Path("/create")
@Consumes("application/json")
public void createFruit(@Valid Fruit fruit) {
SimpleStorageService.storeFruit(fruit);
}
在上面的示例中,如果我們嘗試創建一個重量小於10的果實,我們將收到驗證錯誤。
5.2. 響應資源驗證
同樣,在下一個示例中,我們將看到如何驗證響應資源:
@GET
@Valid
@Produces("application/json")
@Path("/search/{name}")
public Fruit findFruitByName(@PathParam("name") String name) {
return SimpleStorageService.findByName(name);
}
注意,我們如何使用相同的註解。但這一次我們使用它在資源方法級別,以確保響應有效。
6. 自定義異常處理程序
在這一部分,我們將簡要了解如何創建自定義異常處理程序。 這在我們需要違反特定約束時返回自定義響應時非常有用。
首先,讓我們定義我們的 FruitExceptionMapper:
public class FruitExceptionMapper implements ExceptionMapper<ConstraintViolationException> {
@Override
public Response toResponse(ConstraintViolationException exception) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(prepareMessage(exception))
.type("text/plain")
.build();
}
private String prepareMessage(ConstraintViolationException exception) {
StringBuilder message = new StringBuilder();
for (ConstraintViolation<?> cv : exception.getConstraintViolations()) {
message.append(cv.getPropertyPath() + " " + cv.getMessage() + "\n");
}
return message.toString();
}
}
首先,我們定義一個自定義異常映射提供程序。為了做到這一點,我們使用 ConstraintViolationException 實現 ExceptionMapper 接口。
因此,當此異常 被拋出時,我們的自定義異常映射器實例的 toResponse 方法將被調用。
此外,在這個簡單的示例中,我們迭代所有違反項並將每個屬性和消息附加到要發送到響應中的響應。
接下來,為了使用我們的自定義異常映射器,我們需要註冊我們的提供程序:
@Override
protected Application configure() {
ViewApplicationConfig config = new ViewApplicationConfig();
config.register(FruitExceptionMapper.class);
return config;
}
最後,我們添加一個端點以返回無效的 Fruit 以顯示異常處理程序在行動:
@GET
@Produces(MediaType.TEXT_HTML)
@Path("/exception")
@Valid
public Fruit exception() {
Fruit fruit = new Fruit();
fruit.setName("a");
fruit.setColour("b");
return fruit;
}
7. 結論
綜上所述,在本教程中,我們探討了 Jersey Bean Validation API 擴展。
首先,我們介紹了 Bean Validation API 可以如何用於 Jersey。 此外,我們還查看了如何配置示例 Web 應用程序。
最後,我們研究了使用 Jersey 進行驗證的多種方法,以及如何編寫自定義異常處理程序。