Pergunta

Estou aprendendo JAX-RS (aka, JSR-311) usando Jersey. Eu com sucesso criou um recurso de raiz e estou brincando com parâmetros:

@Path("/hello")
public class HelloWorldResource {

    @GET
    @Produces("text/html")
    public String get(
        @QueryParam("name") String name,
        @QueryParam("birthDate") Date birthDate) {

         // Return a greeting with the name and age
    }
}

Isso funciona muito bem, e lida com qualquer formato na localidade atual que é entendida pelo (String) construtor Date (como AAAA / mm / dd e dd / mm / aaaa). Mas se eu fornecer um valor que é inválido ou não entendeu, eu recebo uma resposta 404.

Por exemplo:

GET /hello?name=Mark&birthDate=X

404 Not Found

Como posso personalizar este comportamento? Talvez um código de resposta diferente (provavelmente "400 Bad Request")? E quanto registrando um erro? Talvez adicionar uma descrição do problema ( "mau formato de data") em um cabeçalho personalizado para solução de problemas ajuda? Ou retornar uma resposta de erro inteiro com detalhes, juntamente com um código de status 5xx?

Foi útil?

Solução

Existem várias abordagens para personalizar o comportamento de tratamento de erros com JAX-RS. Aqui estão três das maneiras mais fáceis.

A primeira abordagem é criar uma classe de exceção que se estende WebApplicationException.

Exemplo:

public class NotAuthorizedException extends WebApplicationException {
     public NotAuthorizedException(String message) {
         super(Response.status(Response.Status.UNAUTHORIZED)
             .entity(message).type(MediaType.TEXT_PLAIN).build());
     }
}

E para lançar este recém-criar exceção você simplesmente:

@Path("accounts/{accountId}/")
    public Item getItem(@PathParam("accountId") String accountId) {
       // An unauthorized user tries to enter
       throw new NotAuthorizedException("You Don't Have Permission");
}

Observe, você não precisa declarar a exceção em uma cláusula throws porque WebApplicationException é uma exceção de tempo de execução. Isso irá retornar uma resposta 401 para o cliente.

O segundo e mais fácil abordagem é simplesmente construir uma instância do WebApplicationException diretamente em seu código. Esta abordagem funciona, desde que você não tem que implementar suas próprias exceções de aplicação.

Exemplo:

@Path("accounts/{accountId}/")
public Item getItem(@PathParam("accountId") String accountId) {
   // An unauthorized user tries to enter
   throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}

Este código também retorna um 401 para o cliente.

É claro que este é apenas um exemplo simples. Você pode fazer a exceção muito mais complexo se necessário, e você pode gerar o que sempre http código de resposta que você precisa.

Uma outra abordagem é para embrulhar uma exceção existente, talvez uma ObjectNotFoundException com uma classe wrapper pequeno que implementa a interface ExceptionMapper anotada com uma anotação @Provider. Isto diz o JAX-RS tempo de execução, que se o Exception embrulhado é levantada, devolver o código de resposta definido no ExceptionMapper.

Outras dicas

@Provider
public class BadURIExceptionMapper implements ExceptionMapper<NotFoundException> {

public Response toResponse(NotFoundException exception){

    return Response.status(Response.Status.NOT_FOUND).
    entity(new ErrorResponse(exception.getClass().toString(),
                exception.getMessage()) ).
    build();
}
}

Criar acima classe. Isto irá lidar com 404 (NotFoundException) e aqui no método toResponse você pode dar sua resposta personalizada. Da mesma forma, existem ParamException etc. que você precisa mapear para fornecer respostas personalizadas.

Jersey lança uma com.sun.jersey.api.ParamException quando não consegue desempacotar os parâmetros para uma solução é criar um ExceptionMapper que lida com esses tipos de exceções:

@Provider
public class ParamExceptionMapper implements ExceptionMapper<ParamException> {
    @Override
    public Response toResponse(ParamException exception) {
        return Response.status(Status.BAD_REQUEST).entity(exception.getParameterName() + " incorrect type").build();
    }
}

Você também pode escrever uma classe reutilizável para variáveis ??QueryParam-anotados

public class DateParam {
  private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

  private Calendar date;

  public DateParam(String in) throws WebApplicationException {
    try {
      date = Calendar.getInstance();
      date.setTime(format.parse(in));
    }
    catch (ParseException exception) {
      throw new WebApplicationException(400);
    }
  }
  public Calendar getDate() {
    return date;
  }
  public String format() {
    return format.format(value.getTime());
  }
}

, em seguida, usá-lo como este:

private @QueryParam("from") DateParam startDateParam;
private @QueryParam("to") DateParam endDateParam;
// ...
startDateParam.getDate();

Embora a manipulação de erro é trivial neste caso (jogando uma resposta 400), usando esta classe permite que você fator-out parâmetro manipulação em geral que pode incluir registrando etc.

Uma solução óbvia: tomar em um String, convertido ao Data si mesmo. Dessa forma, você pode definir o formato que você quer, as exceções de captura e quer re-lançamento ou customize erro que está sendo enviada. Para análise, SimpleDateFormat deve funcionar bem.

Estou certo de que há maneiras para ligar manipuladores para tipos de dados também, mas talvez pouco de código simples é tudo que você precisa neste caso.

Eu também gosto StaxMan faria provavelmente implementar essa QueryParam como um string, então lidar com a conversão, rethrowing conforme necessário.

Se o comportamento local específico é o comportamento desejado e esperado, você usaria o seguinte para retornar o erro PEDIDO 400 BAD:

throw new WebApplicationException(Response.Status.BAD_REQUEST);

Veja o JavaDoc para javax.ws.rs.core.Response.Status para mais opções.

documentação @QueryParam diz

" O tipo T do parâmetro, campo anotado ou propriedade deve ou:

1) Seja um tipo primitivo
2) Ter um construtor que aceita um único Cadeia de argumento
3) Ter um método estático chamado valueOf ou fromString que aceita um único argumento String (ver, por exemplo, Integer.valueOf (String))
4) Ter uma implementação registada da javax.ws.rs.ext.ParamConverterProvider JAX-RS extensão SPI que retorna um exemplo javax.ws.rs.ext.ParamConverter capaz de um "a partir de " Conversão de cadeia para o tipo.
5) Seja List, Set ou SortedSet, onde satisfaz T 2, 3 ou 4 acima. O resultado coleção é somente leitura. "

Se você quiser controlar o que resposta vai para o usuário quando consulta parâmetro na forma de string não pode ser convertida para o tipo T, você pode jogar WebApplicationException. Dropwizard vem com seguintes * aulas Param que você pode usar para suas necessidades.

BooleanParam, DateTimeParam, IntParam, LongParam, LocalDateParam, NonEmptyStringParam, UUIDParam. Consulte https: // github.com/dropwizard/dropwizard/tree/master/dropwizard-jersey/src/main/java/io/dropwizard/jersey/params

Se precisar de Joda DateTime, basta usar Dropwizard DateTimeParam .

Se a lista acima não atender às suas necessidades, definir seu próprio estendendo AbstractParam. Substituir o método de análise. Se você precisa de controle sobre o erro corpo da resposta, método de erro de substituição.

Bom artigo de Coda Hale sobre isso é em http: // codahale. com / o que-fazer-jersey-interessante-parâmetro-classes /

import io.dropwizard.jersey.params.AbstractParam;

import java.util.Date;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

public class DateParam extends AbstractParam<Date> {

    public DateParam(String input) {
        super(input);
    }

    @Override
    protected Date parse(String input) throws Exception {
        return new Date(input);
    }

    @Override
    protected Response error(String input, Exception e) {
        // customize response body if you like here by specifying entity
        return Response.status(Status.BAD_REQUEST).build();
    }
}

Date (arg String) construtor é obsoleto. Gostaria de usar Java classes de 8 de data se você estiver em Java 8. Caso contrário joda tempo de data é recomendado.

Este é o comportamento correto na verdade. Jersey vai tentar encontrar um manipulador para sua entrada e vai tentar construir um objeto a partir da entrada fornecido. Neste caso, ele vai tentar criar um novo objeto Date com o valor X fornecido para o construtor. Desde que esta é uma data inválida, por convenção Jersey retornará 404.

O que você pode fazer é reescrita e colocar a data de nascimento como um String, em seguida, tentar analisar e, se você não consegue o que quer, você está livre para jogar qualquer exceção que você quer por qualquer dos mecanismos de mapeamento de exceção ( existem vários).

Assim como uma extensão para resposta @ Steven Lavine no caso de você deseja abrir a janela de login browser. Eu achei difícil de voltar adequadamente a Resposta ( MDN HTTP Authentication ) do filtro no caso de que o usuário ainda não foi autenticado

Isso me ajudou a construir a resposta ao login do navegador vigor, observe a modificação adicional dos cabeçalhos. Isto irá definir o código de status para 401 e definir o cabeçalho que faz com que o navegador para abrir o diálogo nome de usuário / senha.

// The extended Exception class
public class NotLoggedInException extends WebApplicationException {
  public NotLoggedInException(String message) {
    super(Response.status(Response.Status.UNAUTHORIZED)
      .entity(message)
      .type(MediaType.TEXT_PLAIN)
      .header("WWW-Authenticate", "Basic realm=SecuredApp").build()); 
  }
}

// Usage in the Filter
if(headers.get("Authorization") == null) { throw new NotLoggedInException("Not logged in"); }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top