JAX-RS 中必需的@QueryParam (以及缺少它们时应该做什么)

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

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

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

113210 次浏览

问得好。不幸的是(或者可能幸运的是) 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
}

可以使用 javax.validation注释强制使用参数,方法是使用 @javax.validation.constraints.NotNull注释参数。参见 新泽西的榜样一个是 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>实现来覆盖它。

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

我遇到了同样的问题,决定不要在 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-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());
}
}
}
}
}

您需要像在 JAX-RS 库中注册其他 @Provider类一样注册 ContainerRequestFilter。也许 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 库中注册该类。

也许最简单的方法是使用 javax.annotation中的 @Nonnull来实现这一点。它的使用非常简单,因为您所要做的就是在 @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”,“消息”: “ com/example/YourClass.get 的@Nonnull 参数‘ paramName’的参数不能为 null”,“路径”: “/path/to/your-path”}