79

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

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

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

4

4 回答 4

77

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

@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
}
于 2012-12-20T19:44:15.770 回答
68

您可以使用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;
}

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

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

于 2014-02-20T21:56:55.937 回答
18

我遇到了同样的问题,并决定不希望在我的 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.
}

...以及以下 JAX-RSContainerRequestFilter来执行它:

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

您需要以与在 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();
    }
}

和以前一样,请记住将类注册到您的 JAX-RS 库中。

于 2015-08-08T13:29:44.350 回答
2

可能最简单的方法是使用@Nonnullfromjavax.annotation来实现这一点。它使用起来非常简单,因为您只需在之前添加它,@QueryParam如下所示。

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


例子:

import javax.annotation.Nonnull;
...

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

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

{"timestamp":1536152114437,"status":500,"error":"Internal Server Error","exception":"java.lang.IllegalArgumentException","message":"com 的 @Nonnull 参数 'paramName' 的参数/example/YourClass.get 不能为 null","path":"/path/to/your-path"}

于 2018-09-05T13:11:17.610 回答