cscan
cscan

Reputation: 3840

Spring default value of RequestParam equal to a method call

I'm trying to add a request parameter with a default value - however I'd like that default value to be the logged in user's name.

I have a method getUsername() which returns the current user's name but I can't set the value of an annotation to a method call (or a class attribute). Here's my method:

@RequestMapping(method = RequestMethod.GET)
public @ResponseBody ResponseEntity<WebUser> getUser(
        @RequestParam(value = "username", defaultValue = getUsername()) String username) {
    return ResponseEntity.ok(service.getUser(getUsername(), username.toLowerCase()));
}

I can make the RequestParam not required and populate it if null - but this doesn't feel very elegant (or spring-ish). Is there another way to accomplish this?

Upvotes: 2

Views: 4634

Answers (2)

cscan
cscan

Reputation: 3840

As suggested by fateddy, the easiest way to do this is by implementing a HandlerMethodArgumentResolver.

public class UsernameHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.getParameterType().equals(Username.class);
    }
    @Override
    public Object resolveArgument(MethodParameter methodParameter,
                                  ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest,
                                  WebDataBinderFactory webDataBinderFactory) throws Exception {
        String username = nativeWebRequest.getParameter("username");
        if (username == null && nativeWebRequest.getUserPrincipal() != null) {
            username = nativeWebRequest.getUserPrincipal().getName();
        }
        return new Username(username);
    }
}

This requires a simple username class:

public class Username {
    private String username;
    public Username(String username) {
        this.username = username;
    }
    public String getValue() {
        return this.username;
    }
}

as well as an annotation

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserRequest {}

In order to get this configured properly this requires a very minor change to the WebMvcConfigurerAdapter:

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
    argumentResolvers.add(new UsernameHandlerMethodArgumentResolver());
}

And that's it. Good to go. Now we can be simply drop the argument into a controller endpoint:

@RequestMapping(method = RequestMethod.GET)
public @ResponseBody ResponseEntity<WebUser> getUser(@UserRequest Username username) {
    return ResponseEntity.ok(service.getUser(username, username.toLowerCase()));
}

Upvotes: 2

dReAmEr
dReAmEr

Reputation: 7196

I am not sure whether you can call method in annotation or not,but you can get user name value just before the @RequestMapping call execution by something like below.

I think this can be achieved using @ModelAttribute.

@ModelAttribute("username")
public String getUserName() {
    String name = "XYZ";
    return name;
}

And in your @RequestMapping do like below.

@RequestMapping(method = RequestMethod.GET)
public @ResponseBody ResponseEntity<WebUser> getUser(
        @ModelAttribute("username") String username) {
    return ResponseEntity.ok(service.getUser(username, username.toLowerCase()));
}

Further reading

Upvotes: 0

Related Questions