如何处理MicroProfile REST服务输入枚举字段的校验错误
问题根因
枚举值不匹配的错误发生在JSON反序列化阶段,该阶段早于接口方法调用、参数校验阶段,因此请求不会进入你实现的RetrieveService业务方法,默认会直接返回400错误。
MicroProfile完全兼容JAX-RS规范,你可以通过以下两种方案实现自定义错误返回:
方案1:全局异常拦截(推荐)
通过JAX-RS的ExceptionMapper全局拦截反序列化阶段的枚举格式错误,无需修改业务代码。
根据你项目使用的JSON处理工具选择对应实现:
若使用Jackson(MicroProfile默认常用实现)
import com.fasterxml.jackson.databind.exc.InvalidFormatException; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; import java.util.Collections; import java.util.stream.Collectors; import java.util.stream.Stream; @Provider public class EnumFormatExceptionMapper implements ExceptionMapper<InvalidFormatException> { @Override public Response toResponse(InvalidFormatException e) { // 判断是否为枚举类型反序列化错误 if (e.getTargetType().isEnum()) { String allowedValues = Stream.of(e.getTargetType().getEnumConstants()) .map(Object::toString) .collect(Collectors.joining("、")); String errorMsg = String.format("非法参数值 '%s',%s字段允许的值为:%s", e.getValue(), e.getPath().get(0).getFieldName(), allowedValues); return Response.status(Response.Status.BAD_REQUEST) .entity(Collections.singletonMap("error", errorMsg)) .build(); } // 其他格式错误自定义返回 return Response.status(Response.Status.BAD_REQUEST) .entity(Collections.singletonMap("error", "请求参数格式错误")) .build(); } }
若使用JSON-B
import javax.json.bind.JsonbException; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; import java.util.Collections; import java.util.regex.Matcher; import java.util.regex.Pattern; @Provider public class JsonbEnumExceptionMapper implements ExceptionMapper<JsonbException> { private static final Pattern ENUM_ERROR_PATTERN = Pattern.compile("No enum constant (\\w+)\\.(\\w+)\\.(\\w+)"); @Override public Response toResponse(JsonbException e) { Matcher matcher = ENUM_ERROR_PATTERN.matcher(e.getMessage()); if (matcher.find()) { String errorMsg = String.format("非法的%s参数值,允许的值为:LONDON、LIVERPOOL", matcher.group(3)); return Response.status(Response.Status.BAD_REQUEST) .entity(Collections.singletonMap("error", errorMsg)) .build(); } return Response.status(Response.Status.BAD_REQUEST) .entity(Collections.singletonMap("error", "请求参数格式错误")) .build(); } }
加了@Provider注解的异常处理器会被MicroProfile运行时自动扫描生效,无需额外配置。
方案2:业务层校验(符合你想在RetrieveService里处理的需求)
把User类的枚举字段改为String类型,避免反序列化阶段报错,再在业务方法里手动做枚举转换校验:
- 修改User类定义
public class User { String name; String surname; String city; // 从CityEnum改为String类型 // getter和setter保持@NotNull注解不变 }
- 在RetrieveService里做校验
@Service public class RetrieveService implements Retrieve { @Override public Response retrieveValue(@Valid User user) { CityEnum cityEnum; try { // 手动转换为枚举 cityEnum = CityEnum.valueOf(user.getCity().toUpperCase()); } catch (IllegalArgumentException e) { // 自定义错误返回 return Response.status(Response.Status.BAD_REQUEST) .entity(Collections.singletonMap("error", "城市参数非法,允许的值为:LONDON、LIVERPOOL")) .build(); } // 后续正常业务逻辑 return Response.ok().build(); } }
内容的提问来源于stack exchange,提问作者Prodox21
相关产品推荐
相关产品推荐

