Domanda

Sto imparando JAX-RS (aka, JSR-311) utilizzando Jersey. Ho con successo creato una risorsa Radice e sto giocando in giro con i parametri:

@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
    }
}

Questa grande opera, e gestisce qualsiasi formato nella localizzazione corrente che è compresa dal costruttore Data (String) (come dd AAAA / mm / e gg / mm / aaaa). Ma se io fornisco un valore che non è valido o non capito, ricevo una risposta 404.

Ad esempio:

GET /hello?name=Mark&birthDate=X

404 Not Found

Come posso personalizzare questo comportamento? Forse un codice di risposta diversa (probabilmente "400 Bad Request")? Che dire di registrazione di un errore? Forse aggiungere una descrizione del problema ( "formato data cattivo") in un'intestazione personalizzata per aiutare la risoluzione dei problemi? O restituire un intero risposta errore con i dettagli, insieme a un codice di stato 5xx?

È stato utile?

Soluzione

Ci sono diversi approcci per personalizzare la gestione comportamento dell'errore con JAX-RS. Qui ci sono tre dei modi più facili.

Il primo approccio è quello di creare una classe di eccezione che si estende WebApplicationException.

Esempio:

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

E per lanciare questa nuova creare Eccezione è sufficiente:

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

Si noti, non è necessario dichiarare l'eccezione in una clausola throws perché WebApplicationException è un'eccezione di runtime. Ciò restituirà una risposta 401 al client.

Il secondo e più facile approccio è quello di costruire semplicemente un'istanza del WebApplicationException direttamente nel codice. Questo approccio funziona fino a quando non c'è bisogno di implementare le proprie eccezioni di applicazione.

Esempio:

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

Questo codice restituisce anche un 401 per il cliente.

Naturalmente, questo è solo un semplice esempio. È possibile effettuare l'eccezione molto più complesso, se necessario, e si può generare quale codice di risposta mai http è necessario.

Un altro approccio è quello di avvolgere un'eccezione esistente, forse un ObjectNotFoundException con una piccola classe wrapper che implementa l'interfaccia ExceptionMapper annotato con un'annotazione @Provider. Questo indica al runtime JAX-RS, che se l'eccezione avvolto viene sollevata, restituisce il codice di risposta definita nella ExceptionMapper.

Altri suggerimenti

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

Crea sopra di classe. Questo gestirà 404 (NotFoundException) e qui nel metodo toResponse si può dare la vostra risposta personalizzata. Allo stesso modo ci sono ParamException ecc, che si avrebbe bisogno di mappare per fornire risposte personalizzate.

Jersey lancia un com.sun.jersey.api.ParamException quando non riesce a unmarshall i parametri in modo una soluzione è quella di creare un ExceptionMapper che gestisce questi tipi di eccezioni:

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

Si potrebbe anche scrivere una classe riutilizzabile per le variabili QueryParam-annotati

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

quindi utilizzarlo in questo modo:

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

Anche se la gestione degli errori è banale in questo caso (lanciare una risposta 400), utilizzando questa classe consente di fattore-out gestione dei parametri, in generale, che potrebbero includere la registrazione ecc.

Una soluzione ovvia: prendere in una stringa, convertire in data da soli. In questo modo è possibile definire formato desiderato, le eccezioni di cattura e sia ri-lancio o personalizzare l'errore viene inviato. Per l'analisi, SimpleDateFormat dovrebbe funzionare bene.

Sono sicuro che ci sono modi per agganciare i gestori per i tipi di dati troppo, ma forse po 'di codice semplice è tutto ciò che serve in questo caso.

Anche a me piace StaxMan sarebbe probabilmente implementare che QueryParam come una stringa, quindi gestire la conversione, rethrowing se necessario.

Se il comportamento specifico locale è il comportamento desiderato e atteso, si dovrebbe utilizzare la seguente per restituire l'errore di richiesta BAD 400:

throw new WebApplicationException(Response.Status.BAD_REQUEST);

Vedere la JavaDoc per javax.ws.rs.core.Response.Status per ulteriori opzioni.

documentazione @QueryParam dice

  

" Il tipo T del parametro, campo o la proprietà annotata deve   o:

     

1) essere un tipo primitivo
  2) avere un costruttore che accetta un singolo   argomento String
  3) Avere un metodo statico denominato valueOf o fromstring   che accetta un singolo argomento stringa (vedi, per esempio,   Integer.valueOf (String))
  4) Avere un'implementazione registrato di   javax.ws.rs.ext.ParamConverterProvider JAX-RS estensione SPI che   restituisce un'istanza javax.ws.rs.ext.ParamConverter capace di un "da   stringa" la conversione per il tipo.
  5) Essere List, Set o   SortedSet, dove T soddisfa 2, 3 o 4 di cui sopra. Il risultato   la raccolta è di sola lettura. "

Se si desidera controllare quale risposta va all'utente quando il parametro di query in forma di stringa non può essere convertito in vostro tipo T, si può buttare WebApplicationException. Dropwizard viene fornito con i seguenti * classi Param che è possibile utilizzare per le vostre esigenze.

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

Se avete bisogno di Joda DateTime, basta usare Dropwizard DateTimeParam .

Se l'elenco non soddisfa le tue esigenze, definire il proprio estendendo AbstractParam. Override metodo parse. Se avete bisogno di controllare sull'errore corpo di risposta, il metodo di errore di sovrascrittura.

Un buon articolo da Coda Hale su questo è in http: // codahale. com /-jersey-interessante-parametro-classi what-fa /

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

Data costruttore (String arg) è deprecato. Vorrei usare Java classi 8 data se siete su Java 8. In caso contrario si consiglia di data e ora Joda.

Questo è il comportamento corretto in realtà. Jersey cercherà di trovare un gestore per il vostro contributo e cercherà di costruire un oggetto dall'input fornito. In questo caso si cercherà di creare un nuovo oggetto Date con il valore X fornito al costruttore. Poiché si tratta di una data non valida, per convenzione Jersey tornerà 404.

Che cosa si può fare è riscrivere e mettere la data di nascita come String, quindi provare a analizzare e, se non si ottiene ciò che si vuole, si è liberi di gettare ogni eccezione che si desidera da uno qualsiasi dei meccanismi di mappatura eccezione ( ci sono diversi).

Proprio come un'estensione @Steven Lavine risposta nel caso in cui si vuole aprire la finestra del browser di login. Ho trovato difficile per restituire correttamente la risposta ( MDN autenticazione HTTP ) dal filtro nel caso in cui l'utente non è stato ancora autenticato

Questo mi ha aiutato a costruire la risposta alla forza di accesso del browser, si noti la modifica ulteriore delle intestazioni. Questo imposterà il codice di stato a 401 e impostare l'intestazione che fa sì che il browser per aprire la finestra di dialogo nome utente / password.

// 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"); }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top