Требуется @QueryParam в JAX-RS (и что делать в их отсутствие)


Я развертываю компонент веб-служб в JBoss Application Server 7 С помощью RESTEasy JAX-RS реализация.

есть ли аннотация, доступная для объявления обязательной, обязательной @QueryParam параметры JAX-RS ? И, если нет, то это "стандартный" способ справиться с ситуациями, когда такие параметры отсутствуют?

мои методы веб-службы (ресурса) возвращают JSON-строковые результаты, когда это правильно вызывается со всеми обязательными аргументами, но я не уверен, что это лучший способ указать вызывающему, что требуемый параметр отсутствует.

4 61

4 ответа:

хороший вопрос. К сожалению (или, может быть, к счастью) в 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
}

можно использовать 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>.

Это обеспечивает централизованное способ перевода обязательного параметра в ответы на ошибки и отсутствие дублирования кода не требуется.

я столкнулся с той же проблемой и решил, что я не хочу gazillion шаблонных нулевых проверок, разбросанных по моему коду 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());
                }
            }
        }
    }
}

нужно прописать ContainerRequestFilter таким же образом можно регистрировать и другие @Provider классы с вашей библиотекой JAX-RS. Может быть, 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 библиотека.

вероятно, самый простой способ-использовать @Nonnull С javax.annotation для достижения этой цели. Это очень простой в использовании, как все, что вам нужно сделать, это добавить его перед @QueryParam как показано ниже.

Однако, имейте в виду, что это будет бросать IllegalArgumentException когда параметр равен нулю, поэтому ответ, который вы отправляете обратно, будет тем, что вы делаете для исключения. Если вы не перехватите его, это будет 500 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.ленг.IllegalArgumentException","message": "аргумент для параметра @Nonnull 'paramName' com/example/YourClass.get не должен быть null","path": "/path/to/your-path"}