Required @QueryParam in JAX-RS (and what to do in their absence) Required @QueryParam in JAX-RS (and what to do in their absence) java java

Required @QueryParam in JAX-RS (and what to do in their absence)


Good question. Unfortunately (or maybe fortunately) there is no mechanism in JAX-RS to make any params mandatory. If a parameter is not supplied it's value will be NULL and your resource should deal with it accordingly. I would recommend to use WebApplicationException to inform your users:

@GET@Path("/some-path")public String read(@QueryParam("name") String name) {  if (name == null) {    throw new WebApplicationException(      Response.status(Response.Status.BAD_REQUEST)        .entity("name parameter is mandatory")        .build()    );  }  // continue with a normal flow}


You can use javax.validation annotations to enforce that the parameters are mandatory by annotating them with @javax.validation.constraints.NotNull. See an example for Jersey and one for RESTeasy.

So your method would simply become:

@GET@Path("/some-path")public String read(@NotNull @QueryParam("name") String name) {  String something =   // implementation  return something;}

Note that the exception gets then translated by the JAX-RS provider to some error code. It can usually be overridden by registering your own implementation of javax.ws.rs.ext.ExceptionMapper<javax.validation.ValidationException>.

This provides a centralized way to translate mandatory parameter to error responses and no code duplication is necessary.


I ran into the same problem and decided that I did not want a gazillion boilerplate null checks scattered across my REST code, so this this is what I decided to do:

  1. Create an annotation that causes an exception to be thrown when a required parameter is not specified.
  2. Handle the thrown exception the same way I handle all other exceptions thrown in my REST code.

For 1), i implemented the following annotation:

import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Required{    // This is just a marker annotation, so nothing in here.}

... and the following JAX-RS ContainerRequestFilter to enforce it:

import java.lang.reflect.Parameter;import javax.ws.rs.QueryParam;import javax.ws.rs.container.ContainerRequestContext;import javax.ws.rs.container.ContainerRequestFilter;import javax.ws.rs.container.ResourceInfo;import javax.ws.rs.core.Context;import javax.ws.rs.ext.Provider;@Providerpublic class RequiredParameterFilter implements ContainerRequestFilter{    @Context    private ResourceInfo resourceInfo;    @Override    public void filter(ContainerRequestContext requestContext)    {        // Loop through each parameter        for (Parameter parameter : resourceInfo.getResourceMethod().getParameters())        {            // Check is this parameter is a query parameter            QueryParam queryAnnotation = parameter.getAnnotation(QueryParam.class);            // ... and whether it is a required one            if (queryAnnotation != null && parameter.isAnnotationPresent(Required.class))            {                // ... and whether it was not specified                if (!requestContext.getUriInfo().getQueryParameters().containsKey(queryAnnotation.value()))                {                    // We pass the query variable name to the constructor so that the exception can generate a meaningful error message                    throw new YourCustomRuntimeException(queryAnnotation.value());                }            }        }    }}

You need to register the ContainerRequestFilter in the same way you would register your other @Provider classes with your JAX-RS library. Maybe RESTEasy does it for you automatically.

For 2), I handle all runtime exceptions using a generic JAX-RS ExceptionMapper:

import javax.ws.rs.core.Response;import javax.ws.rs.ext.ExceptionMapper;import javax.ws.rs.ext.Provider;@Providerpublic class MyExceptionMapper implements ExceptionMapper<RuntimeException>{    @Override    public Response toResponse(RuntimeException ex)    {        // In this example, we just return the .toString() of the exception.         // You might want to wrap this in a JSON structure if this is a JSON API, for example.        return Response            .status(Response.Status.BAD_REQUEST)            .entity(ex.toString())            .build();    }}

As before, remember to register the class with your JAX-RS library.