In this tutorial, we’ll shed light on how to handle enum mapping in a case-insensitive manner when passing it as request parameter in Spring Boot.

First, we’ll start with a few insights on how Spring Boot maps enums as request parameters by default.

Then, we’ll illustrate using practical examples how to customize enum mapping to address the case sensitivity limitation.

Passing Enum as Request Parameter in Spring Boot

By default, Spring uses StringToEnumConverterFactory under the hood to convert the passed request parameters to actual enum constants.

StringToEnumConverterFactory converts a RequestParameter from String type to a particular Java enum by calling the Enum.valueOf(Class, String) method.

Please bear in mind that the passed parameter must match exactly the specified enum constant.

Which means that we need to pass an uppercase string. Otherwise, a conversion exception will be raised.

Now, let’s exemplify how to pass an enum as a request param. For example, let’s create the DaysEnum enum:

    
        public enum DaysEnum {
            MONDAY, 
            TUESDAY, 
            WEDNESDAY, 
            THURSDAY, 
            FRIDAY, 
            SATURDAY, 
            SUNDAY
        }
    

Next, we will create a Spring controller with a method to handle the default enum mapping:

    
        @RestController
        public class EnumMappingController {
            @GetMapping("/mapenum")
            public String getByLevel(@RequestParam DaysEnum day) {
                if (day == null) {
                    return null;
                }
                return day.name();
            }
        }
    

As we can see, we pass the DaysEnum enum directly to the method handler. We didn’t add any logic to convert the RequestParam to a DaysEnum object.

Now, let’s confirm that everything works as expected using a test case:

    
        @ExtendWith(SpringExtension.class)
        @WebMvcTest(EnumMappingController.class)
        class EnumMappingAppTests {
            @Autowired
            private MockMvc mockMvc;

            @Test
            void whenPassingValidEnumConstant_thenConvert() throws Exception {
                mockMvc.perform(get("/mapenum?day=THURSDAY"))
                    .andExpect(status().isOk())
                    .andExpect(content().string(DaysEnum.THURSDAY.name()));
            }
        }       
    

As we mentioned earlier, the StringToEnumConverterFactory converter calls the Enum.valueOf() method.

So, when the passed string parameter doesn’t match one of the DaysEnum constants, Spring will fail with the ConversionFailedException exception.

To learn more about exceptions in Spring Boot, our article on how to handle exceptions in REST API does a great job in covering this topic.

Let’s confirm this with another test case:

    
        @Test
        void whenPassingLowerCaseEnumConstant_thenFailWithError() throws Exception {
            mockMvc.perform(get("/mapenum?day=monday"))
                .andExpect(status().is4xxClientError());
        }
    

As shown above, MockMvc fails with a client error. The main reason is that we pass the value monday, which is not an enum constant, instead of MONDAY.

Another way to test this would be using Postman. So, let’s send localhost:8080/mapenum?day=monday as a GET request:

default request param enum mapping in Spring Boot

Looking at the stacktrace, Spring Boot fails with ConversionFailedException:

    
        Failed to convert value of type 'java.lang.String' to required type 'com.devwithus.enummapping.enums.DaysEnum'; 
        nested exception is org.springframework.core.convert.ConversionFailedException:
        Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam com.devwithus.enummapping.enums.DaysEnum] for value 'monday'; 
        nested exception is java.lang.IllegalArgumentException: No enum constant com.devwithus.enummapping.enums.DaysEnum.monday]
    

Customize Enum Mapping to Handle Case Sensitivity

Spring offers multiple handy options to tackle the challenge of case sensitivity when passing an enum constant as RequestParam.

So, let’s get down the rabbit hole and see how to use each option in practice.

Using StringToEnumIgnoringCaseConverterFactory

StringToEnumIgnoringCaseConverterFactory is a built-in converter that implements the ConverterFactory interface.

As the name indicates, this out-of-the-box converter allows us to pass an enum without worrying about the case sensitivity problem.

However, it’s not configured by default, so we need to register it to make it available.

To do so, we need to use ApplicationConversionService which extends the FormattingConversionService interface:

    
        @Configuration
        public class EnumMappingAppConfig implements WebMvcConfigurer {
            
            @Override
            public void addFormatters(FormatterRegistry registry) {
                ApplicationConversionService.configure(registry);
            }
        }
    

Please note that ApplicationConversionService registers multiple built-in converters and StringToEnumIgnoringCaseConverterFactory is one of them.

Now, let’s see what happens when we pass a lowercase version of an enum constant:

    
        @Test
        void whenPassingLowerCaseEnumConstant_thenConvert() throws Exception {
            mockMvc.perform(get("/mapenum?day=monday"))
                .andExpect(status().isOk())
                .andExpect(content().string(DaysEnum.MONDAY.name()));
        }
    

The test case shows that StringToEnumIgnoringCaseConverterFactory did its job and the conversion of monday into MONDAY is done with success.

Using Custom Formatter

Spring provides SPI formatters to print and parse Java objects. The main purpose of a formatter is to format an object from a specific Java type to another.

Here, we will create a custom formatter to handle the mapping between a request param, which a string, into a DaysEnum object.

For instance, let’s consider the DaysEnumFormatter:

    
        public class DaysEnumFormatter implements Formatter<DaysEnum> {
            @Override
            public String print(DaysEnum object, Locale locale) {
                return null;
            }

            @Override
            public DaysEnum parse(String text, Locale locale) throws ParseException {
                if (StringUtils.isBlank(text)) {
                    return null;
                }

                return EnumUtils.getEnum(DaysEnum.class, text.toUpperCase());
            }
        }
    

As we can see, we implement the parse() method and add the conversion logic from a String object to *DaysEnum*.

As a matter of a fact, we don’t add any logic in the print() method because we don’t need it.

Next, we need to make DaysEnumFormatter available for use:

    
        @Configuration
        public class EnumMappingAppConfig implements WebMvcConfigurer {

            DaysEnumFormatter daysEnumFormatter() {
                return new DaysEnumFormatter();
            }

            @Override
            public void addFormatters(FormatterRegistry registry) {
                // ApplicationConversionService.configure(registry);
                registry.addFormatter(this.daysEnumFormatter());
            }
        }
    

Lastly, let’s send localhost:8080/mapenum?day=monday using Postman:

handle case-insensitive enum mapping using custom formatter

As shown above, the monday param is successfully converted to the DaysEnum constant MONDAY.

Using Custom Converter

Typically, a converter can be used to convert a source object of type S into a target of type T.

So, a custom converter could be another good option to handle the mapping between the DaysEnum enum and a RequestParam in Spring Boot.

For example, let’s create the DaysEnumConverter converter:

    
        public class DaysEnumConverter implements Converter<String, DaysEnum> {
            @Override
            public DaysEnum convert(String source) {
                if (StringUtils.isBlank(source)) {
                    return null;
                }

                return EnumUtils.getEnum(DaysEnum.class, source.toUpperCase());
            }
        }      
    

Basically, our converter implements the Converter interface which provides the convert() method.

As we can use see, we kept the same logic used before. The main idea here is to convert the String object into an object of type DaysEnum.

Similarly, we need to add the missing piece of the puzzle. So, let’s register our DaysEnumConverter:

    
        @Override
        public void addFormatters(FormatterRegistry registry) {
            registry.addConverter(this.daysEnumConverter());
        }  
    

Now that everything is set, we can pass monday or Monday as a request param without any issue.

Using Custom PropertyEditor

Alternatively, another solution would be using a custom PropertyEditor.

In short, the main purpose of PropertyEditor is to parse string values from request params or print Java objects as human-readable values.

To create a custom property editor, we will need to extend the PropertyEditorSupport class:

    
        public class DaysEnumPropertyEditor extends PropertyEditorSupport {
            @Override
            public void setAsText(String text) {
                if (StringUtils.isBlank(text)) {
                    setValue(null);
                } else {
                    setValue(EnumUtils.getEnum(DaysEnum.class, text.toUpperCase()));
                }
            }
        }
    

As shown above, we overrode the setAsText() method. Then, we converted the string param object in uppercase before calling the EnumUtils.getEnum() method.

That way, we make sure we get a valid DaysEnum constant from the passed request parameter.

Now that we put all the pieces together, let’s register DaysEnumPropertyEditor.

To do so, we will need to use the WebDataBinder object which provides the registerCustomEditor() method.

First, let’s create a new method in the Spring controller:

    
        @InitBinder
        public void initBinder(WebDataBinder dataBinder) {
            dataBinder.registerCustomEditor(DaysEnum.class, new DaysEnumPropertyEditor());
        }
    

Please note that this new method is annotated with the @InitBinder and it’s not a handler method.

Conclusion

In this article, we explored different ways of mapping enums in Spring Boot in a case-insensitive manner.

We explained how to do this using built-in converters. Then, we showcased how to achieve the same objective using a custom formatter, converter and property editor.