我试图弄清楚如何使用枚举类作为输入参数来处理错误。我想实现的是某种验证,它返回一条错误消息,突出显示所有允许的类别。
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Builder
public class UserDTO {
private String name;
private int age;
private GenderTypeEnum gender;
private List<HobbyEnum> hobby;
}
和/或
public enum GenderTypeEnum {
MALE("male"),
FEMALE("female");
private final String value;
private GenderTypeEnum(String value) {
this.value = value;
}
@JsonValue
public String getGender(){
return this.value;
}
@JsonCreator
public static GenderTypeEnum fromValues(String value) {
for (GenderTypeEnum category : GenderTypeEnum.values()) {
if (category.getGender().equalsIgnoreCase(value)) {
return category;
}
}
throw new IllegalArgumentException(
"Unknown enum type " + value + ", Allowed values are " + Arrays.toString(values()));
}
}
和/或
public enum HobbyEnum {
BIKE("bike"),
CLIMB("climb"),
TENNIS("tennis");
private final String value;
private HobbyEnum(String value) {
this.value = value;
}
@JsonValue
public String getGender(){
return this.value;
}
@JsonCreator
public static HobbyEnum fromValues(String value) {
for (HobbyEnum category : HobbyEnum.values()) {
if (category.getGender().equalsIgnoreCase(value)) {
return category;
}
}
throw new IllegalArgumentException(
"Unknown enum type " + value + ", Allowed values are " + Arrays.toString(values()));
}
}
和/或
@RestController
@RequestMapping("api/v1")
public class UserController {
@GetMapping(path = "/user")
@ResponseBody
public ResponseEntity<?> getUser(
@RequestBody
UserDTO user
){
return ResponseEntity
.ok()
.body(user);
}
}
它当前返回以下类型的错误消息(例如,将male
错放为malex
)
在Sping Boot 控制台上,我得到了以下消息:
2023-06-08T18:26:34.233+02:00 WARN 28248 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.example.enumhandler.enumerator.GenderTypeEnum`, problem: Unknown enum type malex, Allowed values are [MALE, FEMALE]]
我用下面的方法实现了一个验证器,但是没有得到希望的消息(https://www.baeldung.com/javax-validations-enums)。
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.10.Final</version>
</dependency>
和/或
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = GenderTypeEnumSubSetValidator.class)
public @interface GenderTypeEnumSubset {
GenderTypeEnum[] anyOf();
String message() default "must be any of {anyOf}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
和/或
package com.example.enumhandler.validator;
import com.example.enumhandler.enumerator.GenderTypeEnum;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
public class GenderTypeEnumSubSetValidator
implements ConstraintValidator<GenderTypeEnumSubset, GenderTypeEnum> {
private GenderTypeEnum[] subset;
@Override
public void initialize(GenderTypeEnumSubset constraint) {
this.subset = constraint.anyOf();
}
@Override
public boolean isValid(GenderTypeEnum value, ConstraintValidatorContext context) {
return value == null || Arrays.asList(subset).contains(value);
}
}
并更新了类UserDTO
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Builder
public class UserDTO {
private String name;
private int age;
@GenderTypeEnumSubset(anyOf = {GenderTypeEnum.MALE, GenderTypeEnum.FEMALE})
private GenderTypeEnum gender;
private List<HobbyEnum> hobby;
}
但最后我得到了和以前一样的错误消息,没有改进:
我也找到了这个解决方案[https://medium.com/@vandernobrel/creating-custom-bean-validation-annotations-2c21f5e24b06][4],我尝试实现:
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = GenderTypeEnumSubSetValidator.class)
public @interface GenderTypeEnumSubset {
Class<? extends Enum<?>> enumClass();
String message() default "must be any of {anyOf}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
和/或
import com.example.enumhandler.enumerator.GenderTypeEnum;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class GenderTypeEnumSubSetValidator
implements ConstraintValidator<GenderTypeEnumSubset, GenderTypeEnum> {
private List<String> acceptedValues;
@Override
public void initialize(GenderTypeEnumSubset constraint) {
acceptedValues = Stream.of(constraint.enumClass().getEnumConstants())
.map(Enum::name)
.collect(Collectors.toList());
}
@Override
public boolean isValid(GenderTypeEnum value, ConstraintValidatorContext context) {
Arrays.asList(subset).contains(value);
if(value == null){
return true;
}
return acceptedValues.contains(value.toString());
}
}
已更新UserDTO
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Builder
public class UserDTO {
private String name;
private int age;
//@GenderTypeEnumSubset(anyOf = {GenderTypeEnum.MALE, GenderTypeEnum.FEMALE})
//private GenderTypeEnum gender;
@GenderTypeEnumSubset(enumClass = GenderTypeEnum.class)
private String gender;
private List<HobbyEnum> hobby;
}
但还是要说:
[![在此处输入图像描述][5]][5]
这是@Jay Yadav建议的解决方案(谢谢,但还是不行):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>
spring-boot-starter-validation
</artifactId>
<version>3.1.0</version>
</dependency>
和/或
package com.example.enumhandler.dto;
import com.example.enumhandler.enumerator.GenderTypeEnum;
import com.example.enumhandler.enumerator.HobbyEnum;
import com.example.enumhandler.validator.GenderTypeEnumSubset;
import lombok.*;
import java.util.List;
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Builder
public class UserDTO {
private String name;
private int age;
@GenderTypeEnumSubset(anyOf = {GenderTypeEnum.MALE, GenderTypeEnum.MALE})
private GenderTypeEnum gender;
private List<HobbyEnum> hobby;
}
和/或
package com.example.enumhandler.enumerator;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import java.util.Arrays;
public enum GenderTypeEnum {
MALE("male"),
FEMALE("female");
private final String value;
private GenderTypeEnum(String value) {
this.value = value;
}
@JsonValue
public String getGender(){
return this.value;
}
@JsonCreator
public static GenderTypeEnum fromValues(String value) {
for (GenderTypeEnum category : GenderTypeEnum.values()) {
if (category.getGender().equalsIgnoreCase(value)) {
return category;
}
}
throw new IllegalArgumentException(
"Unknown enum type " + value + ", Allowed values are " + Arrays.toString(values()));
}
}
和/或
package com.example.enumhandler.enumerator;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import java.util.Arrays;
public enum HobbyEnum {
BIKE("bike"),
CLIMB("climb"),
TENNIS("tennis");
private final String value;
private HobbyEnum(String value) {
this.value = value;
}
@JsonValue
public String getGender(){
return this.value;
}
@JsonCreator
public static HobbyEnum fromValues(String value) {
for (HobbyEnum category : HobbyEnum.values()) {
if (category.getGender().equalsIgnoreCase(value)) {
return category;
}
}
throw new IllegalArgumentException(
"Unknown enum type " + value + ", Allowed values are " + Arrays.toString(values()));
}
}
和/或
package com.example.enumhandler.validator;
import com.example.enumhandler.enumerator.GenderTypeEnum;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = GenderTypeEnumSubSetValidator.class)
public @interface GenderTypeEnumSubset {
GenderTypeEnum[] anyOf();
String message() default "must be any of {anyOf}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
和/或
package com.example.enumhandler.validator;
import com.example.enumhandler.enumerator.GenderTypeEnum;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.Arrays;
public class GenderTypeEnumSubSetValidator implements ConstraintValidator<GenderTypeEnumSubset, GenderTypeEnum> {
private GenderTypeEnum[] subset;
@Override
public void initialize(GenderTypeEnumSubset constraint) {
this.subset = constraint.anyOf();
}
@Override
public boolean isValid(GenderTypeEnum value, ConstraintValidatorContext context) {
return value == null || Arrays.asList(subset).contains(value);
}
}
和/或
package com.example.enumhandler.controller;
import com.example.enumhandler.dto.UserDTO;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("api/v1")
public class UserController {
@GetMapping(path = "/user")
@ResponseBody
public ResponseEntity<?> getUser(
@RequestBody
@Valid
UserDTO user
){
return ResponseEntity
.ok()
.body(user);
}
}
终于
[![在此处输入图像描述][6]][6]
和/或
[![在此处输入图像描述][7]][7]
带错误处理程序的UPEDATE
package com.example.enumhandler.exception;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import java.time.LocalDateTime;
@AllArgsConstructor
@Data
@Builder
class ApiError {
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss")
private LocalDateTime timestamp;
private String status;
private int code;
private String message;
}
和/或
package com.example.enumhandler.exception;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.time.LocalDateTime;
@RestControllerAdvice
public class UserExceptionHandler {
@ExceptionHandler(value = {HttpMessageNotReadableException.class})
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public ResponseEntity<?> test(HttpMessageNotReadableException ex, HttpServletRequest httpServlet){
ApiError error = ApiError
.builder()
.timestamp(LocalDateTime.now())
.status(HttpStatus.BAD_REQUEST.getReasonPhrase())
.code(HttpStatus.BAD_REQUEST.value())
.message(ex.getLocalizedMessage())
.build();
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
}
和/或
[![在此输入图像描述][8]][8]
或
[![在此输入图像描述][9]][9]
但是
[![在此输入图像描述][10]][10]
1.是否可以不显示**“JSON解析错误:无法构造com.example.enumhandler.enumerator.GenderTypeEnum
的示例,问题:“我不喜欢显示类的内部代号;?
1.例如,是否可以报告“允许的值是[male,female]"而不是“允许的值是[MALE,FEMALE]"**(也称为枚举值而不是名称)?
1.是否可以在一个唯一的消息中收集所有ENUM错误?
[4]:https://medium.com/@vandernobrel/creating-custom-bean-validation-annotations-2c21f5e24b06 [5]:https://i.stack.imgur.com/E5t7k.png [6]:https://i.stack.imgur.com/ktHH6.png [7]:https://i.stack.imgur.com/llpxz.png [8]:https://i.stack.imgur.com/AFKu9.png [9]:https://i.stack.imgur.com/cQSgA.png [10]:https://i.stack.imgur.com/D9Uo0.png
2条答案
按热度按时间k5ifujac1#
about是否可以报告,例如,“允许的值是[男性,女性]”而不是“允许的值是[男性,女性]”(又名枚举值而不是名称)?
lnlaulya2#
您错过了在控制器上添加
@Valid
注解以使自定义注解工作!!还有一个建议是使用
spring-boot-starter-validation
依赖项,而不是像spring docs中那样使用hibernate-validator
!!另外,如果您想处理由于传递的无效枚举而发生的异常,则需要创建自定义异常处理程序
Is it possible to not show "JSON parse error: Cannot construct instance of com.example.enumhandler.enumerator.GenderTypeEnum, problem:" I don't like show internal code name class;?
参考以下内容:https://stackoverflow.com/a/64576320/16769477
Is it possible to report, for example, "Allowed values are [male, female]" instead of "Allowed values are [MALE, FEMALE]" (aka enum value and not name)?
在你的枚举类(HobbyEnum.java)中,你在传递非法的值!!