2014-06-24 9 views
12

Ich benutze Jersey 2.10 mit Jackson Serialisierung/Deserialisierung Feature in meiner REST API.Jersey Exception Mapper funktioniert nicht, wenn Jackson Deserialisierung fehlschlägt

Meine Idee ist es, meine REST-API immer eine Standard-JSON-Fehlerantwort zurückgeben. Dafür habe ich ExceptionMapper-Klassen, die richtige json-Fehlerantworten für jede Ausnahme erstellen, die in der Jersey-Anwendung ausgelöst wird. Ich habe auch eine jsp, die die gleiche Art von JSON-Antwort erzeugt, die ich als Fehlerseite in der web.xml registriert, die alle Fehler abdeckt, die kommen könnten, bevor Jersey geladen wird.

Aber es gibt einen Fall, in dem weder meine Ausnahme Mapper noch mein Json Herstellung jsp arbeitet, ist, dass, wenn eine schlechte Formte json auf einen POST REST-Endpunkt senden, die nur die folgende Meldung zurück:

HTTP/1.1 400 Bad Request 
Server: Apache-Coyote/1.1 
Access-Control-Allow-Origin: * 
Access-Control-Allow-Methods: GET, POST, DELETE, PUT 
Content-Type: text/plain 
Content-Length: 210 
Date: Tue, 24 Jun 2014 22:14:11 GMT 
Connection: close 

Can not deserialize instance of com.example.rest.User[] out of START_OBJECT token 
at [Source: [email protected]; line: 1, column: 1] 

Wie kann ich Jersey dazu bringen, stattdessen meine benutzerdefinierte Fehlerantwort zurückzugeben?

UPDATE:

Basierend auf der Antwort von @Lucasz, ich mehr Forschung getan und festgestellt, dass es zwei Ausnahme-Mapper in der Verpackung com.fasterxml.jackson.jaxrs.base definiert (https://github.com/FasterXML/jackson-jaxrs-providers/tree/master/base/src/main/java/com/fasterxml/jackson/jaxrs/base) JsonMappingExceptionMapper und JsonParseExceptionMapper scheint meine benutzerdefinierten Mapper zu überschatten.

Wie kann ich diese Mapper abmelden?

Dies ist, wie ich zur Zeit die Mapper bin Registrierung:

import javax.ws.rs.core.Response; 
import javax.ws.rs.core.Response.Status; 
import javax.ws.rs.ext.ExceptionMapper; 
import javax.ws.rs.ext.Provider; 

import com.fasterxml.jackson.core.JsonProcessingException; 

@Provider 
public class JsonProcessingExceptionMapper implements ExceptionMapper<JsonProcessingException>{ 

     public static class Error { 
      public String key; 
      public String message; 
     } 

     @Override 
     public Response toResponse(JsonProcessingException exception) { 
      Error error = new Error(); 
      error.key = "bad-json"; 
      error.message = exception.getMessage(); 
      return Response.status(Status.BAD_REQUEST).entity(error).build(); 
     } 
} 

und es funktionierte:

@ApplicationPath("/") 
public class MyApp extends ResourceConfig{ 
    public SyntheticAPIApp() { 
     packages("com.example.resource", "com.example.mapper"); 
     register(org.glassfish.jersey.jackson.JacksonFeature.class); 
    } 
} 
+0

Normalerweise löst ein ungültiger JSON eine 500 interne Serverfehlerantwort aus. Es sieht so aus, als ob ein Ausnahme-Mapper die JsonParseExcpetion bearbeitet und in eine 400-Antwort mit dem aus der Ausnahmebedingungsnachricht entnommenen Text konvertiert hat. –

+0

Aber ich habe keine Ausnahme Mapper, die eine Text/plain Antwort erzeugt, scheint es, dass jemand diese Ausnahme abfängt, weil ich es nicht einmal in den Protokollen sehen – raspacorp

Antwort

23

ich es mit einer Ausnahme-Mapper wie unten getestet.


Update: geändert JsonParseException zu JsonProcessingException (allgemein)


Update2: Um die unerwünschten Mapper ersetzen

Registrierung zu vermeiden
register(org.glassfish.jersey.jackson.JacksonFeature.class); 

mit

Schauen Sie sich den Quellcode von JacksonFeature an und Sie werden verstehen, was passiert.

+0

Welche Version von Jersey und Jackson Provider verwenden Sie? Ich benutze Jersey 2.10 mit Jersey-Media-Json-Jackson-Bibliothek. Meine Methode empfängt ein Array von User-Objekten als Eingabe, zum Beispiel akzeptiert sie [{"name": "John", ...}], wird aber wie erwartet mit {"name": "John", ...} fehlschlagen. , erhält Ihre Methode auch ein Array? – raspacorp

+0

@raspacorp Ich habe es mit Trikot 2.9.1 und Jackson-Jaxrs-Json-Provider 2.3.3 getestet –

+0

@raspacorp Ich habe versucht, ein einzelnes Objekt an eine Methode zu senden, die eine Liste akzeptiert und mein Mapper hat nicht funktioniert, aber der Grund war das Eine andere Ausnahme wurde ausgelöst (JsonMappingException). Ich habe meinen Mapper so geändert, dass er JsonProcessingException akzeptiert (Basisklasse für JsonParseException und JsonMappingException) und dann ordnungsgemäß funktioniert. –

1

Ich hatte das gleiche Problem, und die vorherige Antwort führte mich zu der Lösung, aber gab mir keine aktuelle Jersey (2.22). Zuerst musste ich die org.glassfish.jersey.spi.ExtendedExceptionMapper wie beschrieben in https://jersey.java.net/documentation/latest/representations.html verwenden.

Darüber hinaus prüft Jersey eine Ausnahme Mapper, die so nah wie möglich an die ausgelöste Ausnahme ist (von org.glassfish.jersey.internal.ExceptionMapperFactory):

for (final ExceptionMapperType mapperType : exceptionMapperTypes) { 
     final int d = distance(type, mapperType.exceptionType); 
     if (d >= 0 && d <= minDistance) { 
      final ExceptionMapper<T> candidate = mapperType.mapper.getService(); 

      if (isPreferredCandidate(exceptionInstance, candidate, d == minDistance)) { 
       mapper = candidate; 
       minDistance = d; 
       if (d == 0) { 
        // slight optimization: if the distance is 0, it is already the best case, so we can exit 
        return mapper; 
       } 
      } 
     } 
    } 

deshalb ich genau die Ausnahme zur Karte benötigt und nicht eine allgemeine Ausnahme .

Am Ende sieht mein Provider wie folgt:

@Provider 
public final class JsonParseExceptionExceptionHandler implements ExtendedExceptionMapper<JsonParseException> { 
    @Override 
    public Response toResponse(final JsonParseException exception) { 
     exception.printStackTrace(); 
     return Response.status(Response.Status.BAD_REQUEST).entity("JSON nicht in korrektem Format.").build(); 
    } 

    @Override 
    public boolean isMappable(final JsonParseException arg0) { 
     return true; 
    } 
} 
+0

Dies funktioniert unvorhersehbar, wie in http://stackoverflow.com/questions/15989212/overriding-included- angegeben. provider-in-jersey Die Lösung ist, Brücke Jersey-Media-json-jackson loszuwerden und 'com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider.class' manuell zu registrieren, wie jeder andere Anbieter. – genobis

1

Ich habe "jackson-jaxrs-json-Provider 2.8.8" und JAX-RS 2,0

Anwendung Klasse - brauchen Sie Ihre ExceptionMapper Implementierungsklasse registrieren:

@ApplicationPath("pathApplication") 
public class ApplicationConfiguration extends Application{ 


    @Override 
    public Set<Class<?>> getClasses() { 

     Set<Class<?>> resources = new HashSet<>(); 
     resources.add(YourJAXRSClass.class); 
     resources.add(JsonJacksonEM.class); //ExceptionMapper class implementation 
     //others resources that you need... 
     return resources; 

    } 


} 

ExceptionMapper Klasse Umsetzung:

@Provider 
public class JsonJacksonEM implements ExceptionMapper<JsonParseException>{ 


    @Override 
    public Response toResponse(JsonParseException exception) { 
     //you can return a Response in the way that you want! 
     return Response.ok(new YourObject()).build(); 
    } 


} 
0

Ich hatte das gleiche Problem und lösen das ExceptionMapper überschrieben. Perfekt! Eine zusätzliche Sache, die ich tun musste und nicht 100% ig verstand, war, wie man den JacksonProvider für meine Anwendung außer Kraft setzt (ich weiß nicht, ob es mit Jerseys Version zusammenhing, die ich verwendete - 2.19). Hier ist mein web.xml-Teil, der ihn überschreibt:

<init-param> 
    <param-name>jersey.config.server.provider.classnames</param-name> 
     <param-value> 
      com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider 
     </param-value> 
</init-param> 
Verwandte Themen