Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can @QueryParam translate a parameter without value to boolean "false"?

Tags:

java

jax-rs

I'd like to use an URL like http://www.example.com/rest/foo?bar where the bar query parameter has no value and its presence alone should denote if a variable is true or false.

Currently the missing value is assumed to be "" (empty) and passed to new Boolean() which treats it as false.

Is there a more elegant way of defining it than declaring the parameter to be String and converting it myself?

Like e.g. a class javax.rs.BooleanFlag or similar?

like image 814
lathspell Avatar asked Oct 21 '15 11:10

lathspell


People also ask

Can a query parameter be a Boolean?

For query parameters that are defined as a boolean type in our documentation, the case-insensitive strings true and 1 will be handled as a true value, and the case-insensitive strings false and 0 will be handled as the false value. Other values will be defaulted to false , if provided.

What is the @QueryParam annotation?

Annotation Type QueryParamBinds the value(s) of a HTTP query parameter to a resource method parameter, resource class field, or resource class bean property. Values are URL decoded unless this is disabled using the Encoded annotation. A default value can be specified using the DefaultValue annotation.

Can query parameters be optional?

As query parameters are not a fixed part of a path, they can be optional and can have default values.

What is the difference between QueryParam and FormParam?

If you make request http://test.com?id=123, then id is a QueryParam (to be more precise this is GET request parameter), if you make POST request with a form inside body, then these form parameters (filled usually by user) are translated to FormParam-s.


3 Answers

Note: upon seeing Phoste's answer, I'd go with his/her solution. I'm leaving this answer up, as there is still some useful information here.

Is there a more elegant way of defining it than declaring the parameter to be String and converting it myself? Like e.g. a class javax.rs.BooleanFlag or similar?

No such type (BooleanFlag), If you look at the javadoc for @QueryParam, you'll see a list of options for how we can create a custom type to use a @QueryParam value (for the most part the same holds true for other @XxxParams also)

  • Have a constructor that accepts a single String argument
  • Have a static method named valueOf or fromString that accepts a single String argument (see, for example, Integer.valueOf(String))
  • Have a registered implementation of ParamConverterProvider JAX-RS extension SPI that returns a ParamConverter instance capable of a "from string" conversion for the type.

So from the first option, in theory, you should be able to do something like

public class Flag {

    private final boolean  isPresent;
    public Flag(String param) { isPresent = param != null; }
    public boolean isPresent() { return isPresent; }
}
@GET
public String get(@QueryParam("bar") Flag bar) {
    if (bar.isPresent()) {
        return "bar is present";
    } else {
        return "bar is not present";
    }
}

Now this works when the query flag is present. But when it's not, it acts like any other non-primitive type; it's null. So the call to bar.isPresent give an NPE. Tested with a fromString and valueOf with the same result. We could check if (bar == null), but that's no better that just using a String and checking if the String is null. It's not pretty.

So the last option is the ParamConverterProvider. Which does actually work. Below is the implementation.

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
import javax.ws.rs.ext.Provider;

@Provider
public class FlagParamConverterProvider implements ParamConverterProvider {

    @Override
    public <T> ParamConverter<T> getConverter(
            Class<T> rawType, Type genericType, Annotation[] annotations) {
        if (rawType != Flag.class) {
            return null;
        }

        return new ParamConverter<T>() {

            @Override
            public T fromString(String value) {
                return (T)new Flag(value);
            }

            @Override
            public String toString(T value) { return null; } 
        };
    }  
}

Just make sure the provider is registered. It's a pretty clean solution in my opinion.

like image 130
Paul Samsotha Avatar answered Nov 08 '22 18:11

Paul Samsotha


You could try the following:

@GET
@Path("/some-path")
public Response myMethod(@Context HttpServletRequest request) {

    boolean isParameterPresent = request.getParameterMap().contains("bar");

    ...
}

But the solutions shown in peeskillet's answer are the cleverest ways to achieve it.

like image 45
cassiomolin Avatar answered Nov 08 '22 18:11

cassiomolin


I know it's an old question but I had the same trouble.

To solve my problem, I used the Annotation @DefaultValue :

@GET
@Path("/path")
public Response myMethod(@DefaultValue("true") @QueryParam("foo") boolean foo) {
    if (foo) {
       ...
    }
}

So, when the request contains the parameter foo the boolean value will be false and if not it will be true. It shows the opposite of reality but if you're aware of it, it's quite simple to use.

like image 17
Phoste Avatar answered Nov 08 '22 18:11

Phoste