ตัวอย่างการเขียน Spring-boot WebFlux Manual Validation
pom.xml
...
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
...
@SpringBootApplication
@ComponentScan(basePackages = {"com.pamarin"})
public class AppStarter {
public static void main(String[] args) {
SpringApplication.run(AppStarter.class, args);
}
}
@Setter
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class LoginRequest {
@NotBlank(message = "require username")
@Length(max = 50, message = "username more than {max} characters")
private String username;
@NotBlank(message = "require password")
@Length(min = 8, max = 50, message = "password must between {min} to {max} characters")
private String password;
}
- @NotBlank คือ ห้ามเป็น null หรือ ค่าว่าง
- @Length คือ ต้องมีขนาดตามที่ระบุ
อ่านเพิ่มเติม : https://beanvalidation.org/
public class ManualValidation {
private final Object object;
private final String objectName;
private Class<?> groupClass;
private ManualValidation(Object object, String objectName) {
this.object = object;
this.objectName = objectName;
}
/**
* @param object
* @param objectName
* @return ManualValidator instance
*/
public static ManualValidation of(Object object, String objectName) {
return new ManualValidation(object, objectName);
}
/**
* @param object
* @return ManualValidator instance
*/
public static ManualValidation of(Object object) {
return of(object, "");
}
/**
* @param groupClass
* @return ManualValidator instance
*/
public ManualValidation group(Class<?> groupClass) {
this.groupClass = groupClass;
return this;
}
/**
* @param validator
* @throws BindException
*/
public void validate(Validator validator) throws BindException {
BindingResult bindingResult = new BindException(object, objectName);
WebExchangeBindException ex = new WebExchangeBindException(null, bindingResult);
ValidationUtils.invokeValidator(validator, object, ex, groupClass, Default.class);
if (ex.hasErrors()) {
throw ex;
}
}
}
@Slf4j
@RestController
public class LoginController {
private final Validator validator;
@Autowired
public LoginController(Validator validator) {
this.validator = validator;
}
@PostMapping("/login")
public void login(@RequestBody LoginRequest req) throws BindException, NoSuchMethodException {
ManualValidation.of(req).validate(validator);
log.debug("username => {}", req.getUsername());
log.debug("password => {}", req.getPassword());
}
}
ตัวจัดการ Error ให้เรียนรู้จากหัวข้อ spring-boot-webflux-custom-error-handler
@Component
public class ErrorResponseWebExchangeBindExceptionHandler extends ErrorResponseExceptionHandlerAdapter<WebExchangeBindException> {
@Override
public Class<WebExchangeBindException> getTypeClass() {
return WebExchangeBindException.class;
}
@Override
protected ErrorResponse buildError(ServerWebExchange exchange, WebExchangeBindException ex) {
return ErrorResponse.builder()
.error("bad_request")
.errorDescription("Validate fail")
.errorStatus(HttpStatus.BAD_REQUEST.value())
.errorFields(
ex.getFieldErrors()
.stream()
.map(f -> {
return ErrorResponse.Field.builder()
.name(f.getField())
.code(f.getCode())
.description(f.getDefaultMessage())
.build();
})
.collect(toList())
)
.build();
}
}
cd ไปที่ root ของ project จากนั้น
$ mvn clean install
$ mvn spring-boot:run
เปิด browser แล้วเข้า http://localhost:8080
POST : http://localhost:8080/login
ได้ผลลัพธ์
{
"error": "bad_request",
"state": null,
"error_status": 400,
"error_description": "Validate fail",
"error_timestamp": 1561611745664,
"error_uri": "https://developer.pamarin.com/document/error/",
"error_code": "1e71a8a1-48eb-41d1-b8c0-101a067dd176",
"error_field": [
{
"name": "password",
"code": "Length",
"description": "password must between 8 to 50 characters"
}
]
}