8

I have a spring boot controller, one of my parameters is an Enum. The enum has a string value. I want to pass as the parameter the value of the enum and the controller to give me the Enum. Can this be done?

 @RequestMapping(value = "/")
public MyResponse getResponse ( @RequestParam(value = "version") final ProjectVersion version ) {
   ...bla bla bla...
}

public enum ProjectVersion {
    VERSION_1 ("1.00")
    VERSION_2 ("2.00")

    private final String version;

    ProjectVersion ( String version ) {
        this.version = version;
    }

     @Override
     public String toString() {
        return this.version;
     }

}

I want to be able to make a request as follows:

http://myhost.com/mypath?version=1.00

And get in the controller the ProjectVersion.VERSION_1

Any ideas?

Panos
  • 7,227
  • 13
  • 60
  • 95
  • Yes, with custom serializer. See this, exactly yours case: http://stackoverflow.com/questions/7766791/serializing-enums-with-jackson – Alex Chernyshev Apr 06 '17 at 09:59
  • 2
    If you pass `VERSION_1` it will work, if you pass the internal value it won't then you would need to create a custom converter yourself. – M. Deinum Apr 06 '17 at 10:49

2 Answers2

10

This is not possible as is. You have to create your custom converter to convert from a String to ProjectVersion.

For example first defines the converter from a String to ProjectVersion:

public class ProjectVersionConverter implements ConditionalGenericConverter {

  @Override
  public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
    return targetType.getType().equals(ProjectVersion.class);
  }

  @Override
  public Set<ConvertiblePair> getConvertibleTypes() {
    return Collections.singleton(new ConvertiblePair(String.class , ProjectVersion.class));
  }

  @Override
  public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
    return ProjectVersion.findByVersion((String)source);
  }
}

Then register it:

@Configuration
public class CustomWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {

  @Override
  public void addFormatters(FormatterRegistry registry) {
    registry.addConverter(new ProjectVersionConverter());
  }
}

You can skip the registration if you define ProjectVersionConverter as a Spring bean. (This code has not be tested).

Marvo
  • 17,845
  • 8
  • 50
  • 74
Nicolas Labrot
  • 4,017
  • 25
  • 40
  • Using argument resolver is cleaner and better approach. – digz6666 Jan 04 '18 at 08:37
  • I do not agree. A converter should not bother that the value is passed as a request param or as a path variable. As long as the right annotation is used, with an agnostic converter, it should work. – Nicolas Labrot Jan 04 '18 at 12:38
2

Because you need to pass (for each request) the argument to the Controller method, the clean solution would be to use HandlerMethodArgumentResolver, so that Spring container can dynamically inject your ProjectVersion argument to the Controller method as shown below:

ProjectVersionArgumentResolver class:

public class ProjectVersionArgumentResolver implements 
                                    HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.getParameterType().equals(ProjectVersion.class);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter,
                   ModelAndViewContainer modelAndViewContainer,
                   NativeWebRequest nativeWebRequest,
                   WebDataBinderFactory webDataBinderFactory) throws Exception {

        return ProjectVersion.fromString(nativeWebRequest.getParameter("version"));
    }
}

Spring-boot ApplicationLauncher class:

public class MyProjectApplicationLauncher extends WebMvcConfigurerAdapter {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> 
                                           argumentResolvers) {
        //add the new resolver
        argumentResolvers.add(new MyMethodArgumentResolver());
    }

    public static void main(String[] args) {
        SpringApplication.run(MyProjectApplicationLauncher.class, args);
    }
} 

ProjectVersion class:

public enum ProjectVersion {

    //add your existing code

    //Add fromString method to convert string to enum 
    public static ProjectVersion fromString(String input) {
        for (ProjectVersion projectVersion : ProjectVersion.values()) {
          if (projectVersion.version.equals(input)) {
            return projectVersion;
          }
        }
        return null;
     }
}
Vasu
  • 21,832
  • 11
  • 51
  • 67
  • Better use methodParameter.getParameterName() instead of hardcoded string "version". So ProjectVersion.fromString(nativeWebRequest.getParameter(methodParameter.getParameterName())); – digz6666 Jan 04 '18 at 08:36