在JAX-RS中需要@QueryParam(以及在他们缺席时该怎么做)

时间:2012-12-20 08:48:57

标签: java jax-rs

我使用 RESTEasy JAX-RS 实现将Web服务组件部署到 JBoss Application Server 7

是否有注释可用于在 JAX-RS 中声明必需的强制 @QueryParam 参数?而且,如果没有,那么处理缺少这些参数的情况的“标准”方法是什么?

我的Web服务(资源)方法在使用所有必需参数正确调用时返回JSON字符串化结果,但我不确定向调用者指示缺少必需参数的最佳方法是什么。

4 个答案:

答案 0 :(得分:66)

好问题。不幸的是(或者幸运的是)JAX-RS中没有任何机制可以强制使用任何参数。如果未提供参数,则其值为NULL,您的资源应相应地处理它。我建议您使用WebApplicationException通知您的用户:

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

答案 1 :(得分:54)

您可以使用javax.validation注释通过使用@javax.validation.constraints.NotNull对其进行注释来强制执行参数。请参阅an example for Jerseyone for RESTeasy

所以你的方法就变成了:

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

请注意,JAX-RS提供程序会将异常转换为某些错误代码。通常可以通过注册您自己的javax.ws.rs.ext.ExceptionMapper<javax.validation.ValidationException>实现来覆盖它。

这提供了一种将强制参数转换为错误响应的集中方式,并且不需要代码重复。

答案 2 :(得分:15)

我遇到了同样的问题并决定我不想在我的REST代码中分散大量的样板空检查,所以这就是我决定做的事情:

  1. 创建一个注释,当未指定所需参数时,该注释会引发异常。
  2. 处理抛出的异常与处理REST代码中抛出的所有其他异常的方式相同。
  3. 对于 1),我实现了以下注释:

    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.
    }
    

    ...以及以下JAX-RS ContainerRequestFilter强制执行它:

    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());
                    }
                }
            }
        }
    }
    

    您需要注册ContainerRequestFilter,就像使用JAX-RS库注册其他@Provider类一样。也许RESTEasy会自动为您完成。

    对于 2),我使用通用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<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();
        }
    }
    

    和以前一样,记得用JAX-RS库注册该类。

答案 3 :(得分:1)

最简单的方法可能是使用@Nonnull中的javax.annotation来实现。使用起来非常简单,只需要做的就是在@QueryParam之前添加它,如下所示。

但是,请记住,当参数为null时,它将抛出IllegalArgumentException,因此您发回的响应将是您为异常所做的任何事情。如果您不截取它,即使发送回正确的内容是500 Server Error,它也将是400 Bad Request。您可以拦截IllegalArgumentException并对其进行处理以返回正确的响应。


示例:

import javax.annotation.Nonnull;
...

    @GET
    @Path("/your-path")
    public Response get(@Nonnull @QueryParam("paramName") String paramName) {
        ... 
    }

返回给调用者的默认错误消息如下:

  

{“时间戳”:1536152114437,“状态”:500,“错误”:“内部服务器错误”,“异常”:“ java.lang.IllegalArgumentException”,“消息”:“ @ Nonnull参数'paramName的参数com / example / YourClass.get的'不能为空“,” path“:” / path / to / your-path“}