2016-03-23 5 views
2

Ich bin neu zu Spring Boot, aber nach ein paar Stunden Lesen von Beiträgen und Blogs über Ausnahme handlig in Spring Boot REST, wo niemand etwas über die Behandlung solcher Ausnahme geschrieben von benutzerdefinierten Converter, I geschrieben beschlossen, hier zu schreiben.Ausnahmebehandlung in Spring Boot REST von benutzerdefinierten Konverter

Ich entwickle kleine REST-App basierend auf Spring Boot einfach von IntelliJ generiert. Exemplarische Verfahren sieht wie folgt aus

@RestController 
@RequestMapping("/resources") 
public class CVResourceService { 

    private final TechnologyRepository technologyRepository; 
    private final ProjectRepository projectRepository; 

    @Autowired 
    public CVResourceService(TechnologyRepository technologyRepository,  ProjectRepository projectRepository) { 
     this.technologyRepository = technologyRepository; 
     this.projectRepository = projectRepository; 
    } 

    @RequestMapping(value = "https://stackoverflow.com/users/{guid}/projects/langs/{lang}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) 
    @ResponseBody 
    public Collection getUserProjects(@PathVariable("guid") GUID userGUID,   @PathVariable("lang") Language language) { 
     return ProjectDTOAssembler.toDTOs(projectRepository.findOne(userGUID, language)); 
    } 
} 

Da beide guid und lang String sind und ich wollte diese Stücke von Informationen stark typisierte aus demselben Anfang waren, habe ich einfach Konverter für GUID und Language Typen und registriert es in Anwendungsklasse:

public final class GUIDConverter implements Converter{ 

    @Override 
    public GUID convert(String source) { 
     return GUID.fromString(source); 
    } 
} 

public class LanguageConverter implements Converter{ 

    @Override 
    public Language convert(String source) { 
     Language language = Language.of(source); 
     if (language == null) { throw new WrongLanguagePathVariableException(); } 

     return language; 
    } 
} 

GUID wirft Ausnahme von Verfahren Fabrik,

... 
public static GUID fromString(String string) { 
    String[] components = string.split("-"); 

    if (components.length != 5) 
     throw new IllegalArgumentException("Invalid GUID string: " + string); 

    return new GUID(string); 
} 
... 

Language null zurückgeben, dann werfe ich benutzerdefinierte Ausnahme vom Konverter. Registrierung in Anwendung:

@SpringBootApplication 
public class Application extends WebMvcConfigurerAdapter { 

    @Override 
    public void addFormatters(FormatterRegistry registry) { 
     registry.addConverter(new GUIDConverter()); 
     registry.addConverter(new LanguageConverter()); 
    } 

    public static void main(String[] args) { 
     SpringApplication.run(Application.class, args); 
    } 
} 

alle Art von Umgang mit Ausnahme Verwendung mit @ResponseStatus, @ControllerAdvice und @ExpectationHandler Ich konnte nicht Konvertern Ausnahmen in Controller-fangen neu zu schreiben (oder besser Karte) ‚Status‘, ‚Fehler‘, " Ausnahme "und" Nachricht "von JSON Fehlerantwort Originalfeld zu meinen Werten. Wahrscheinlich, weil die Ausnahmen vor dem Aufruf meiner REST-Methode ausgelöst werden. Ich habe auch eine Lösung mit ResponseEntityExceptionHandler versucht, aber es hat überhaupt nicht funktioniert.

Für die Anforderung http://localhost:8080/resources/users/620e643f-406f-4c69-3f4c-3f2c303f3f3f/projects/langs/end wo richtige Sprache en ist, Reaktion mit Ausnahme ist:

{ 
    "timestamp": 1458812172976, 
    "status": 400, 
    "error": "Bad Request", 
    "exception": "org.springframework.web.method.annotation.MethodArgumentTypeMismatchException", 
    "message": "Failed to convert value of type [java.lang.String] to required type [com.cybercom.cvdataapi.domain.Language]; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.PathVariable com.cybercom.cvdataapi.domain.Language] for value 'end'; nested exception is com.cybercom.cvdataapi.interfaces.rest.converter.WrongLanguagePathVariableException", 
    "path": "/resources/users/620e643f-406f-4c69-3f4c-3f2c303f3f3f/projects/langs/end" 

}

wo benutzerdefinierte Ausnahme in message Feld nur an der letzten Position ist aber natürlich Austausch sollte mit Meine benutzerdefinierte Nachricht. Und benutzerdefinierte Exception sollte in exception Feld sein, wo jetzt Spring Ausnahme ist. Es ist natürlich das Ziel, keine Ahnung, wie man es in diesem Zusammenhang erreichen kann.

Bitte lösen Sie mein Problem mit Abfangen von Ausnahmen von Konvertern und Mapping sie den Weg, wie es mit @ControllerAdvice und Ausnahmen von Controllern geworfen werden kann. Thx im Voraus.

+0

Bitte fügen Sie die Ausnahme auf Ihre Frage eine HttpServletRequest in Ihre Fehlerbehandlungsmethode injizieren – msparer

Antwort

1

Sie haben Recht - @ControllerAdvice usw. fängt nur Ausnahmen in Controller Methoden - und um ehrlich zu sein, ich denke, es braucht einige Gedanken, um die Fehlerbehandlung zu verstehen und eine konsistente Fehlerbehandlung Ansatz (ohne zu haben) Fehlerbehandlung an verschiedenen Stellen zu duplizieren).

Da in meinen Anwendungen muss ich zwangsläufig Fehler wie diese (oder benutzerdefinierte 404s usw.), die außerhalb des Controllers sind ich nur für die Anwendung breite Fehler Zuordnung gehen - Ich nehme an, Sie laufen die Anwendung als JAR gegeben Sie definiert haben die main() Methode in Ihrer Anwendung.

Meine Präferenz ist eine spezifische Fehlerkonfigurationsdatei haben, zur besseren Lesbarkeit, aber Sie können diese als nur eine normale Bohne in der Config definieren, wenn Sie wollen:

@Configuration 
class ErrorConfiguration implements EmbeddedServletContainerCustomizer { 

    /** 
    * Set error pages for specific error response codes 
    */ 
    @Override public void customize(ConfigurableEmbeddedServletContainer container) { 
     container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/errors/404")) 
    } 

} 

Sie können Seiten Kartenfehler auf der Grundlage spezifischer Ausnahmetypen sowie Http-Antwortcodes, so dass es ziemlich flexibel ist - aber was ich normalerweise tue, ist benutzerdefinierte Ausnahmen zu definieren und HTTP-Antwortcodes an die Ausnahmen anhängen - so kann ich die HttpStatus Codes in meine Fehlerkonfiguration und dann, wenn irgendwo in der Code, den ich möchte, zum Beispiel 404 eine Anfrage kann ich nur throw new PageNotFoundException() (aber das ist persönliche Vorliebe).

@ResponseStatus(value = HttpStatus.NOT_FOUND) 
class PageNotFoundException extends Exception { } 

Die Pfadzuordnung (im obigen Beispiel „/ errors/404“) bildet mit einem normalen Controller, was schön ist, wie es können Sie tun, jede Fehlerprotokollierung/Verarbeitung/E-Mail usw., die Sie tun möchten Bei einem bestimmten Fehler - der Nachteil ist, dass es sich bei einem Standard-Controller, der Ihre Fehler behandelt, möglicherweise um einen exponierten Endpunkt wie/errors/404 handelt - was nicht ideal ist (es gibt einige Optionen - die URL also weniger zu verdunkeln) wahrscheinlich entdeckt werden, oder mit etwas wie Apache, um direkten Zugriff auf diese Endpunkte usw. zu verhindern)

Ich habe kurz darüber geschrieben here - einschließlich Details, wie es funktioniert, wenn Sie Ihre App als WAR in einem traditionellen hostet tomcat server

0

Wenn Ihr Konverter eine Ausnahme auslöst, ist die tatsächliche Ausnahme, die von einer @ControllerAdvice Klasse (oder einer anderen Art von Ausnahmebehandler) abgeholt würde, ein MethodArgumentTypeMismatchException.

Wenn Sie Ihre Konverter so konfigurieren, dass sie stattdessen MethodArgumentTypeMismatchException übernehmen, sollte es funktionieren.

Beispiel:

@ControllerAdvice 
public class ExceptionConfiguration extends ResponseEntityExceptionHandler { 

    @ExceptionHandler(MethodArgumentTypeMismatchException.class) 
    public ResponseEntity<String> handleConverterErrors(MethodArgumentTypeMismatchException exception) { 
     Throwable cause = exception.getCause() // First cause is a ConversionException 
            .getCause(); // Second Cause is your custom exception or some other exception e.g. NullPointerException 
     if(cause.getClass() == UnauthorizedException.class) { 
      return ResponseEntity.status(401).body("Your session has expired"); 
     } 
     return ResponseEntity.badRequest().body("Bad Request: [" + cause.getMessage() + "]"); 
    } 
} 

Im obigen Fall, wenn meine benutzerdefinierte Ausnahme die Reaktion ausgelöst wurde aussehen würde:

Status: 401

Ihre Sitzung ist abgelaufen

Ansonsten für andere Ausnahmen wäre es

Status: 400

Bad Request: [Die Ausnahmemeldung]

Sie die Antwort anpassen können, wie Sie JSON mögen und zurück, wenn Sie es vorziehen.

Um die path auch erhalten Sie

@ExceptionHandler(MethodArgumentTypeMismatchException.class) 
public ResponseEntity<String> 
handleConverterErrors(HttpServletRequest req, MethodArgumentTypeMismatchException exception) { 
    String path = req.getPathInfo() 
    //... 
} 
Verwandte Themen