2014-06-13 11 views
14

Sto sviluppando un'applicazione Web API 2 e attualmente sto cercando di formattare gli errori in modo uniforme (in modo che il consumatore sappia anche quale oggetto dati/struttura possono ispezionare per ottenere maggiori informazioni sugli errori). Questo è quello che ho finora:Risposte di errore uniformi e coerenti da API Web ASP.Net 2

{ 
    "Errors": 
    [ 
     { 
      "ErrorType":5003, 
      "Message":"Error summary here", 
      "DeveloperAction":"Some more detail for API consumers (in some cases)", 
      "HelpUrl":"link to the docs etc." 
     } 
    ] 
} 

Questo funziona bene per eccezioni generate dall'applicazione stessa (cioè controllori all'interno). Tuttavia, se l'utente richiede un URI errato (e ottiene un 404) o utilizza il verbo sbagliato (e ottiene un 405) ecc., Web Api 2 emette un messaggio di errore predefinito, ad es.

{ 
    Message: "No HTTP resource was found that matches the request URI 'http://localhost/abc'." 
} 

C'è un modo di intrappolare questi tipi di errori (404, 405 etc.) e formattazione di fuori nella risposta di errore nel primo esempio di cui sopra?

Finora ho provato:

  • personalizzato ExceptionAttribute inherting ExceptionFilterAttribute
  • personalizzato ControllerActionInvoker inherting ApiControllerActionInvoker
  • IExceptionHandler (nuovo errore globale funzione Trattamento da API Web 2,1)

Tuttavia nessuno di questi approcci è in grado di intercettare questo tipo di errori (404, 405 ecc.). Qualche idea su come/se questo può essere raggiunto?

... o, sto andando su questo nel modo sbagliato? Devo formattare solo le risposte di errore nel mio stile particolare per errori a livello di applicazione/utente e fare affidamento sulle risposte di errore predefinite per cose come 404?

+1

'Dovrei unico formato risposte di errore nel mio stile particolare per errori a livello di applicazione/utente e fare affidamento sulle risposte di errore predefinite per cose come 404? '... la mia opinione è sì –

+0

Sono sempre più incline a questo approccio. Grazie per le tue fantastiche risposte/commenti. –

risposta

8

È possibile eseguire l'override della classe astratta DelegatingHandler e intercettare la risposta al client. Questo ti darà la possibilità di restituire ciò che desideri.

Ecco alcune informazioni su di esso. http://msdn.microsoft.com/en-us/library/system.net.http.delegatinghandler(v=vs.118).aspx

Ecco un poster della pipeline Web Api che mostra ciò che può essere sostituito. http://www.asp.net/posters/web-api/asp.net-web-api-poster.pdf

creare una classe Handler come questo per ignorare la risposta

public class MessageHandler1 : DelegatingHandler 
{ 
    protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     Debug.WriteLine("Process request"); 
     // Call the inner handler. 
     var response = base.SendAsync(request, cancellationToken); 

     Debug.WriteLine("Process response"); 
     if (response.Result.StatusCode == HttpStatusCode.NotFound) 
     { 
      //Create new HttpResponseMessage message 
     } 
     ; 
     return response; 
    } 
} 

Nella classe WebApiConfig.cs aggiungere il gestore.

config.MessageHandlers.Add(new MessageHandler1()); 

UPDATE Come Kiran menziona nei commenti è possibile utilizzare l'OwinMiddleware per intercettare la risposta che va al client. Ciò funzionerebbe per MVC e Web Api in esecuzione su qualsiasi host.

Ecco un esempio di come ottenere la risposta e modificarla come va al client.

public class Startup 
{ 
    public void Configuration(IAppBuilder app) 
    { 
     app.Use(typeof(MyMiddleware)); 
    } 
} 

public class MyMiddleware : OwinMiddleware 
{ 
    public MyMiddleware(OwinMiddleware next) : base(next) { } 

    public override async Task Invoke(IOwinContext context) 
    { 
     await Next.Invoke(context); 
     if(context.Response.StatusCode== 404) 
     { 
      context.Response.StatusCode = 403; 
      context.Response.ReasonPhrase = "Blah"; 
     } 
    } 
} 
+0

Questo da solo non sarebbe sufficiente in quanto in alcuni casi le richieste non arriverebbero nemmeno nell'API Web a causa della mancata corrispondenza delle route API Web. (Questo problema è specifico per gli scenari ospitati da IIS). Questo problema potrebbe essere aggirato con una trappola di tutti i percorsi alla fine delle rotte dell'API Web che possono inviare un messaggio di errore 404 personalizzato ... altre possibili soluzioni è avere un OwinMiddleware che si trova prima nella pipeline e in questo modo hai tutto la tua logica in un solo posto (non ho giocato con questo me stesso ma immagino che questo dovrebbe funzionare) –

+0

Colperà il DelegatingHandler per tutte le chiamate API anche se il percorso non è corretto perché HttpRoutingDispatcher viene chiamato dopo il DelegatingHandlers. Hai ragione, se la richiesta non è una richiesta API, non colpirà questo gestore perché le chiamate non api vengono gestite dai gestori dello spazio dei nomi System.Web.Mvc. Esempio: http: // localhost/WebApplication1/api/invalid colpirà questo gestore ma http: // localhost/WebApplication1/invalid non colpirà questo gestore ... assumendo che tutte le chiamate API siano configurate per iniziare con http: // localhost/WebApplication1/api/ –

+0

Giusto per essere chiari ... la corrispondenza dei percorsi a 'HttpRoutingDispatcher' si verifica solo per scenari SelfHost e In-Memory ... –

2

che ho fatto nella stesso modo in cui @ Dan H menzionato

public class ApiGatewayHandler : DelegatingHandler 
{ 
    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     try 
     { 
      var response = await base.SendAsync(request, cancellationToken); 
      if (response.StatusCode == HttpStatusCode.NotFound) 
      { 
       var objectContent = response.Content as ObjectContent; 
       return await Task.FromResult(new ApiResult(HttpStatusCode.NotFound, VmsStatusCodes.RouteNotFound, "", objectContent == null ? null : objectContent.Value).Response()); 
      } 
      return response; 
     } 
     catch (System.Exception ex) 
     { 
      return await Task.FromResult(new ApiResult(HttpStatusCode.BadRequest, VmsStatusCodes.UnHandledError, ex.Message, "").Response()); 
     } 

    } 
} 

Aggiunto di routing come qui di seguito e ora colpisce il tentativo di cattura per URL non valido

config.Routes.MapHttpRoute(name: "DefaultApi",routeTemplate: "api/{controller}/{id}",defaults: new { id = RouteParameter.Optional }); 
     config.Routes.MapHttpRoute(name: "NotFound", routeTemplate: "api/{*paths}", defaults: new { controller = "ApiError", action = "NotFound" }); 
+0

ci sono dei problemi di rendimento? –