Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Jersey, can I combine QueryParams and FormParams into one value for a method?

I've got a function like:

@POST
@Path("/create")
@Produces({MediaType.APPLICATION_JSON})
public Playlist createPlaylist(@FormParam("name") String name)
{
    Playlist p = playlistDao.create();
    p.setName(name);
    playlistDao.insert(p);
    return p;
}

I want the "name" parameter to come from the form OR from the query parameter. If the user posts to /playlist/create/?name=bob then I want it to work. (This is mostly to aid with testing the API, but also for consuming it on different non-browser platforms.)

I'd be willing to subclass whatever makes the magic binding work... (@BothParam("name") String name) but would need some help to make that happen as I'm new to Jersey/Java Servlets.


Update: The next day...

I've solved this by implementing a ContainerRequestFilter that merges the form parameters into the query parameters. This isn't the best solution, but it does seem to work. I didn't have any luck merging anything into the form parameters.

Here's the code in case someone comes looking for it:

@Override
public ContainerRequest filter(ContainerRequest request) 
{
    MultivaluedMap<String, String> qParams = request.getQueryParameters();
    Form fParams = request.getFormParameters();
    for(String key : fParams.keySet())
    {
        String value = fParams.get(key).get(0);
        qParams.add(key, value);
    }
}

I would still appreciate knowing if there is a better way to do this so I'll leave this question open for now.

like image 618
Will Shaver Avatar asked Jan 04 '12 00:01

Will Shaver


1 Answers

One way you could do this is with an InjectableProvider.

First, you'd define a BothParam annotation:

@Target({ ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface BothParam { String value(); }

Then, define an InjectableProvider for it:

@Provider
public class BothParamProvider implements Injectable<String>, InjectableProvider<BothParam, Type> {    
    @Context private HttpContext httpContext;
    private String parameterName;

    public BothParamProvider(@Context HttpContext httpContext) {
        this.httpContext = httpContext;
    }

    public String getValue() {        
        if (httpContext.getRequest().getQueryParameters().containsKey(parameterName)) {
            return httpContext.getRequest().getQueryParameters().getFirst(parameterName);
        } else if(httpContext.getRequest().getFormParameters().containsKey(parameterName)) {
            return httpContext.getRequest().getFormParameters().getFirst(parameterName);
        }
        return null;
    }

    public ComponentScope getScope() {
        return ComponentScope.PerRequest;
    }

    public Injectable getInjectable(ComponentContext cc, BothParam a, Type c) {        
        parameterName = a.value();
        return this;
    }    
}

Note, this is not truly a coupling of QueryParam and FormParam. Targets annotated with either of those are injected in a much more sophisticated manner. However, if your needs are sufficiently limited, the method outlined above might work for you.

like image 105
maxenglander Avatar answered Sep 25 '22 15:09

maxenglander