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

后端 未结 4 2092
不知归路
不知归路 2020-12-02 10:51

I deploy a web-services component to JBoss Application Server 7 using the RESTEasy JAX-RS implementation.

Is there an annotation av

4条回答
  •  醉酒成梦
    2020-12-02 11:39

    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)
    @Documented
    public @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;
    
    @Provider
    public 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;
    
    @Provider
    public class MyExceptionMapper implements ExceptionMapper
    {
        @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.

提交回复
热议问题