英文:
Return @JsonAlias value in ControllerAdvice Method Argument Validation (@Valid) response
问题
我已经使用最新版本的javax.validation实现了一个简单的验证。
所以我的类:
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Person {
@NotNull(message = "必填字段")
@JsonAlias(value = "current_name")
private String name;
@NotNull(message = "必填字段")
private String age;
}
然后我创建了一个Advice来处理和自定义异常消息。就像这样:
@ControllerAdvice
public class CustomRestExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
List<FieldError> errors = ex.getBindingResult().getFieldErrors();
List<Field> listOfErrors = errors.stream()
.map(error -> Field.builder()
.field(error.getField())
.message(error.getDefaultMessage())
.build())
.collect(Collectors.toList());
ApiError apiError = new ApiError("validation_error", "一些无效的字段", listOfErrors);
return handleExceptionInternal(ex, apiError, headers, HttpStatus.BAD_REQUEST, request);
}
}
所以,当我获得**error.getField()**时,它是一个原始属性名称:"name"。但我需要获取别名:"current_name"。
我正在使用Jackson库。
这是可能的吗?
英文:
I has implemented a simple validation using javax.validation, latest version.
So my class:
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Person {
@NotNull(message = "Required field")
@JsonAlias(value = "current_name")
private String name;
@NotNull(message = "Required field")
private String age;
}
And then I created a Advice to handle and customize the exception message.
Like this:
@ControllerAdvice
public class CustomRestExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
List<FieldError> errors = ex.getBindingResult().getFieldErrors();
List<Field> listOfErrors = errors.stream()
.map(error -> Field.builder()
.field(error.getField())
.message(error.getDefaultMessage())
.build())
.collect(Collectors.toList());
ApiError apiError = new ApiError("validation_error", "Some invalid fields", listOfErrors);
return handleExceptionInternal(ex, apiError, headers, HttpStatus.BAD_REQUEST, request);
}
}
So, when a get the error.getField(), It is a original attribute name: "name". But I need to get the alias: "current_name".
I'm using Jackson lib.
It's possible?
答案1
得分: 1
在你的handleMethodArgumentNotValid
方法中,执行以下操作:
- 获取当前参数类型的所有字段
Field[] clsFields = ex.getParameter().getParameter().getType().getDeclaredFields();
-
找到匹配的字段以及该字段上的
@JsonAlias
注解 -
读取注解的值 --> 这是你放在注解中的值
@JsonAlias(value = "current_name")
String name;
- 使用该值代替原始字段
error.getField()
完整的工作示例:
import com.fasterxml.jackson.annotation.JsonAlias;
import lombok.*;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.*;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.*;
@SpringBootApplication
public class JsonInspect {
public static void main(String[] args) { SpringApplication.run(JsonInspect.class, args); }
}
@RestController
class Controller {
@PostMapping("/test")
public void t(@RequestBody @Valid Person p) {
System.out.println(p);
}
}
@ControllerAdvice
class CustomRestExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
Field[] clsFields = ex.getParameter().getParameter().getType().getDeclaredFields();
List<FieldError> errors = ex.getBindingResult().getFieldErrors();
List<ErrField> listOfErrors = errors.stream()
.map(e -> new ErrField(getJsonAlias(clsFields, e), e.getDefaultMessage()))
.collect(Collectors.toList());
ApiError apiError = new ApiError("validation_error", "Some invalid fields", listOfErrors);
return handleExceptionInternal(ex, apiError, headers, HttpStatus.BAD_REQUEST, request);
}
String getJsonAlias(Field[] clsFields, FieldError e) {
JsonAlias[] alisas = getAnnotationsForField(clsFields, e.getField());
if (alisas == null || alisas.length == 0) {
return e.getField();
}
String[] values = alisas[0].value();
if (values.length == 0) {
return e.getField();
}
return values[0];
}
JsonAlias[] getAnnotationsForField(Field[] clsFields, String fieldName) {
Optional<Field> first = Stream.of(clsFields).filter(f -> f.getName().equals(fieldName)).findFirst();
return first.map(field -> field.getAnnotationsByType(JsonAlias.class)).orElse(null);
}
}
@Data
@AllArgsConstructor
class ErrField {
String fld;
String message;
}
@Data
@AllArgsConstructor
class ApiError {
String type;
String message;
List<ErrField> fields;
}
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Data
class Person {
@JsonAlias("person_name")
@NotNull
String name;
@NotNull
String address;
}
所需的库:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
英文:
In your handleMethodArgumentNotValid method, do the following
- Find all the fields for current parameter type
Field[] clsFields = ex.getParameter().getParameter().getType().getDeclaredFields();
-
Find matching Field and @JsonAlias annotations on that field
-
Read value of the annotation --> which is the value that you put in
@JsonAlias(value = "current_name")
String name;
- Use that value instead of the original field
error.getField()
Complete working example:
import com.fasterxml.jackson.annotation.JsonAlias;
import lombok.*;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.*;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.*;
@SpringBootApplication
public class JsonInspect {
public static void main(String[] args) { SpringApplication.run(JsonInspect.class, args); }
}
@RestController
class Controller {
@PostMapping("/test")
public void t(@RequestBody @Valid Person p) {
System.out.println(p);
}
}
@ControllerAdvice
class CustomRestExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
Field[] clsFields = ex.getParameter().getParameter().getType().getDeclaredFields();
List<FieldError> errors = ex.getBindingResult().getFieldErrors();
List<ErrField> listOfErrors = errors.stream()
.map(e -> new ErrField(getJsonAlias(clsFields, e), e.getDefaultMessage()))
.collect(Collectors.toList());
ApiError apiError = new ApiError("validation_error", "Some invalid fields", listOfErrors);
return handleExceptionInternal(ex, apiError, headers, HttpStatus.BAD_REQUEST, request);
}
String getJsonAlias(Field[] clsFields, FieldError e) {
JsonAlias[] alisas = getAnnotationsForField(clsFields, e.getField());
if (alisas == null || alisas.length == 0) {
return e.getField();
}
String[] values = alisas[0].value();
if (values.length == 0) {
return e.getField();
}
return values[0];
}
JsonAlias[] getAnnotationsForField(Field[] clsFields, String fieldName) {
Optional<Field> first = Stream.of(clsFields).filter(f -> f.getName().equals(fieldName)).findFirst();
return first.map(field -> field.getAnnotationsByType(JsonAlias.class)).orElse(null);
}
}
@Data
@AllArgsConstructor
class ErrField {
String fld;
String message;
}
@Data
@AllArgsConstructor
class ApiError {
String type;
String message;
List<ErrField> fields;
}
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Data
class Person {
@JsonAlias("person_name")
@NotNull
String name;
@NotNull
String address;
}
Required libraries
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论