I have a SpringBoot rest POST endpoint where in body I POST an enum value. This call does not fail on wrong value input. I would like the rest call to fail instead of returning null for a value which can not be deserialised.
I have tried with the following custom ObjectMapper configuration, but any wrong input i put as enum deserialises to null.
@Bean
@Primary
public ObjectMapper customJsonObjectMapper() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
ObjectMapper objectMapper = builder.build();
objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, false);
SimpleModule module = new SimpleModule();
objectMapper.registerModule(module);
return objectMapper;
}
For example if i have the enum:
public enum CouponOddType {
BACK("back"),
LAY("lay");
private String value;
CouponOddType(String value) {
this.value = value;
}
@Override
@JsonValue
public String toString() {
return String.valueOf(value);
}
@JsonCreator
public static CouponOddType fromValue(String text) {
for (CouponOddType b : CouponOddType.values()) {
if (String.valueOf(b.value).equals(text)) {
return b;
}
}
return null;
}
}
the dto where the request is mapped to:
@ApiModel(description = "Filter used to query coupons. Filter properties are combined with AND operator")
@Validated
@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.SpringCodegen", date = "2020-07-07T13:12:58.487+02:00[Europe/Ljubljana]")
public class CouponQueryFilter {
@JsonProperty("statuses")
@Valid
private List<CouponStatus> statuses = null;
@JsonProperty("oddTypes")
@Valid
private List<CouponOddType> oddTypes = null;
public CouponQueryFilter statuses(List<CouponStatus> statuses) {
this.statuses = statuses;
return this;
}
public CouponQueryFilter addStatusesItem(CouponStatus statusesItem) {
if (this.statuses == null) {
this.statuses = new ArrayList<>();
}
this.statuses.add(statusesItem);
return this;
}
/**
* Get statuses
* @return statuses
**/
@ApiModelProperty(value = "")
@Valid
public List<CouponStatus> getStatuses() {
return statuses;
}
public void setStatuses(List<CouponStatus> statuses) {
this.statuses = statuses;
}
public CouponQueryFilter oddTypes(List<CouponOddType> oddTypes) {
this.oddTypes = oddTypes;
return this;
}
public CouponQueryFilter addOddTypesItem(CouponOddType oddTypesItem) {
if (this.oddTypes == null) {
this.oddTypes = new ArrayList<>();
}
this.oddTypes.add(oddTypesItem);
return this;
}
/**
* Get oddTypes
* @return oddTypes
**/
@ApiModelProperty(value = "")
@Valid
public List<CouponOddType> getOddTypes() {
return oddTypes;
}
public void setOddTypes(List<CouponOddType> oddTypes) {
this.oddTypes = oddTypes;
}
}
and in the POST request i put the enum value in json array:
{
"statuses": [
"wrong value"
],
"oddTypes": [
"wrong value"
]
}
I would like that this type of request results in an HTTP 404 error, instead of deserialising into null.
In this case, Jackson is actually behaving as intended and there is an issue in your deserialization logic. Ultimately, you want bad enum values to throw an error and return that error to the user. This is infact the default behaviour of spring and Jackson, and will result in a HTTP 400 BAD REQUEST
error. IMO This is the appropriate error to return (not 404) since the user has supplied bad input.
Unless there is a specific reason for you to implement a custom @JsonCreator
in your enum class, I would get rid of it. What is happening here is that Jackson is being told to use this method for converting a string into an enum value instead from the defualt method. When a text is passed that is not a valid value of your enum, you are returning null
which results into that values deserializing to null.
A quick fix, would be to delete the JsonCreator
and allow jackson to use its default behaviour for handling enums. The extra properties methods you have added are unnecessary in most cases
public enum CouponOddType {
BACK("back"),
LAY("lay");
private String value;
CouponOddType(String value) {
this.value = value;
}
}
If you need to preserve the creator for some other reason, then you will need to add business logic to determine if any of the enum
values in the arrays evaluated to null
.
private Response someSpringRestEndpoint(@RequestBody CouponQueryFilter filter){
if (filter.getOddTypes() != null && filter.getOddTypes().contains(null){
throw new CustomException()
}
if (filter.getStatuses() != null && filter.getStatuses().contains(null){
throw new CustomException()
}
//... other business logic
}