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

Mar*_*tus 70 java jax-rs

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

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

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

yeg*_*256 68

好问题.不幸的是(或者幸运的是)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
}
Run Code Online (Sandbox Code Playgroud)

  • [JAX-RS 1.0的文档](https://wikis.oracle.com/display/Jersey/Overview+of+JAX-RS+1.0+Features)表示它并不总是为空.它将是"List,Set或SortedSet的空集合,其他对象类型为null,以及原始类型的Java定义的默认值". (14认同)
  • 还建议不要使用HttpURLConnection.HTTP_BAD_REQUEST,而是使用javax.ws.rs.core.Response.Status.BAD_REQUEST来保持方法的预期参数. (12认同)
  • **来自遥远未来的注意事项:**有一个[`BadRequestException`](https://docs.oracle.com/javaee/7/api/javax/ws/rs/BadRequestException.html)可以抛出它或多或少地执行上面的代码,但也允许您以编程方式专门捕获此异常. (6认同)
  • `String`不是原始类型,因此它对于其他对象类型是"null" (3认同)

Gio*_*tta 59

您可以使用javax.validation注释通过使用注释强制参数是必需的@javax.validation.constraints.NotNull.查看Jersey的示例RESTeasy 的示例.

所以你的方法将变成:

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

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

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

  • 这种方法的一个问题是错误消息没有指定缺席参数的名称,例如"arg1可能不为空".幸运的是,Bean Validation规范引入了接口javax.validation.ParameterNameProvider.对于JAX-RS,我们可以使用注释QueryParam和PathParam来获取名称(因为反射不允许获取参数名称).可以在此处找到一个示例:http://stackoverflow.com/q/22496527/998772 (12认同)
  • 我正在尝试做类似的事情,但即使我从 URL 中省略此查询参数,@NotNull 也没有检测到它。我在这里开始了一个线程[链接](http://stackoverflow.com/questions/25737837/notnull-annotation-is-not-checking-null-queryparameter-in-jersey-rest-resource) (2认同)

Zer*_*ro3 17

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

  1. 创建一个注释,该注释会在未指定必需参数时引发异常.
  2. 处理抛出的异常与处理REST代码中抛出的所有其他异常的方式相同.

对于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.
}
Run Code Online (Sandbox Code Playgroud)

......以及以下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());
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您需要以与使用JAX-RS库ContainerRequestFilter注册其他@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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这是否提供了@ javax.validation.constraints.NotNull不会提供的功能? (2认同)
  • @MichaelHaefele保留参数名称,这对于显示有意义的错误消息很有用。如果使用`NotNull`注释,则会丢失参数名称,这是不幸的。那就是让我写自己的注释的问题。但也请参阅/sf/ask/977778301/?noredirect=1#comment35934905_21920512。自从我编写这段代码以来,事情可能已经改变。 (2认同)