2016-02-06 11 views
5

che io chiamo un endpoint api in un MVC 6 WebAPI:MVC 6 WebAPI pagina di errore di ritorno html invece della versione JSON dell'oggetto eccezione

POST http://localhost:57287/mytestapi/testentity/ HTTP/1.1 
Accept: application/json 
X-APIKey: 00000000-0000-0000-0000-000000000000 
Content-Type: application/json; charset=utf-8 
Host: localhost:57287 
Content-Length: 1837 
Expect: 100-continue 
Connection: Keep-Alive 

Nel corpo ho JSON entità prova serializzato.

Ho un errore nel mio codice controller entità e l'API restituisce una risposta di 500 'Errore server' so che il bug è un errore risolverlo, tuttavia il problema con cui ho bisogno di aiuto è che l'API sta tornando HTML al posto dell'eccezione json serializzata - Json è quello che mi aspetto: è ciò che il vecchio webapi restituirebbe. Ho portato il codice da un vecchio progetto di test che so funziona.

Quindi perché MVC 6 WebAPI restituisce html anziché json? C'è qualche configurazione che devo fare?

MODIFICA: Ho aggiunto Accetta: application/json alle intestazioni come suggerito da @danludwig, tuttavia questo non ha risolto il problema, ho ancora ricevuto una pagina di errore html.

ho guardato i miei StartUp.cs ed ho trovato:

if (env.IsDevelopment()) 
{ 
    //app.UseBrowserLink(); 
    app.UseDeveloperExceptionPage(); 
} 
else 
{ 
    app.UseExceptionHandler("/Home/Error"); 
} 

nel metodo ConfigureApp. Ho provato con app.UseDeveloperExceptionPage(); commentata. Ciò ha impedito il ritorno della pagina di errore html nel corpo della risposta API, tuttavia non sto ancora ottenendo l'oggetto eccezione serializzato json.

+1

'Accept: application/json' purtroppo non – danludwig

+0

. Ho modificato la domanda per mostrare la nuova intestazione, grazie però. – John

+0

c'è la possibilità che 'JsonOutputFormatter' venga rimosso dal tuo codice? –

risposta

5

Il ExceptionHandlerMiddleware configurato quando si utilizza UseExceptionHandler("Home/Error") non include alcun supporto per JSON. Restituirà solo la pagina html di errore. Lo stesso si può dire quando si utilizza UseDeveloperExceptionPage.

Per quanto ne so, è necessario aggiungersi un pezzo di codice che gestirà gli errori e restituirà un json.

  • Una possibilità è quella di utilizzare un filtro eccezione e inserirlo globalmente o nei controller selezionati, sebbene questo approccio comprenderebbe solo eccezioni provenienti dai metodi di azione del controller. Per esempio il seguente filtro restituirà un oggetto json solo quando la richiesta di accettare era application/json (Altrimenti sarebbe far passare l'eccezione attraverso il quale ad esempio può essere gestita dalla pagina di errore globale):

    public class CustomJSONExceptionFilter : ExceptionFilterAttribute 
    { 
    
        public override void OnException(ExceptionContext context) 
        { 
         if (context.HttpContext.Request.GetTypedHeaders().Accept.Any(header => header.MediaType == "application/json")) 
         { 
          var jsonResult = new JsonResult(new { error = context.Exception.Message }); 
          jsonResult.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError; 
          context.Result = jsonResult; 
         } 
        } 
    } 
    
    services.AddMvc(opts => 
    { 
        //Here it is being added globally. 
        //Could be used as attribute on selected controllers instead 
        opts.Filters.Add(new CustomJSONExceptionFilter()); 
    }); 
    
  • Un'altra l'opzione è di aggiungere il proprio middleware del gestore di eccezioni utilizzando l'overload app.UseExceptionHandler che consente di specificare il comportamento della pipeline alternativa che elaborerà l'eccezione. Ho subito scritto un esempio simile utilizzando un middleware in linea, che restituirà solo un oggetto JSON quando la richiesta accetti era application/json:

    if (env.IsDevelopment()) 
    { 
        app.UseDeveloperExceptionPage(); 
    } 
    else 
    { 
        app.UseExceptionHandler("/Home/Error");    
    } 
    
    app.UseExceptionHandler(appBuilder => 
    { 
        appBuilder.Use(async (context, next) => 
        { 
         var excHandler = context.Features.Get<IExceptionHandlerFeature>();      
         if (context.Request.GetTypedHeaders().Accept.Any(header => header.MediaType == "application/json")) 
         { 
          var jsonString = string.Format("{{\"error\":\"{0}\"}}", excHandler.Error.Message); 
          context.Response.ContentType = new MediaTypeHeaderValue("application/json").ToString(); 
          await context.Response.WriteAsync(jsonString, Encoding.UTF8); 
         } 
         else 
         {       
          //I haven't figured out a better way of signally ExceptionHandlerMiddleware that we can't handle the exception 
          //But this will do the trick of letting the other error handlers to intervene 
          //as the ExceptionHandlerMiddleware class will swallow this exception and rethrow the original one 
          throw excHandler.Error; 
         } 
        }); 
    }); 
    

Entrambi gli approcci vi permetterà di avere altri gestori di errori che forse forniscono pagine html per richieste non json (Un'altra idea sarebbe quella di restituire un json o una pagina html dal tuo gestore di errori personalizzato).

PS. Se si utilizza il secondo approccio, è molto probabile che si desideri inserire tale logica nella propria classe middleware e utilizzare un approccio diverso per generare la risposta JSON.In tal caso, dare un'occhiata a ciò che fa JsonResultExecutor

+0

Questa è una buona risposta. Ho fatto uso di questo e funziona bene per le eccezioni che lancio a livello di controller. Tuttavia sembra esserci un problema più profondo nella gestione delle eccezioni di base di asp. Ho dimenticato di configurare JSON formatter per gli oggetti di riferimento circolare, The IActionResult è stato restituito dal controller ma all'interno di asp core quando è stata generata la risposta, il formatter di JSON ha generato un'eccezione e il client ha ricevuto una risposta di 502 Bad Gateway! Ottenere l'oggetto di eccezione serializzato json sarebbe stato preferito. Bad Gateway non ha senso qui. Hai visto questo? Soluzione? – John

+0

Quindi il tuo controller restituisce un risultato JSON (nessuna eccezione ancora) che quando viene elaborato produce un'eccezione. Immagino che il primo approccio non catturerà questo, hai controllato se il secondo è stato eseguito in questo scenario? A proposito, ho visto la 502 quando ho fatto uno stupido errore nel middleware inline manualmente formattando il json. Non sono sicuro del motivo per cui il framework tratta questi 502! –

+0

Non ho ancora testato lo scenario 2, ma lo farò presto. In realtà sono gli anni 50 che mi fanno impazzire. Sarebbe bello se avessimo qualcosa di simile: app.UseJsonExceptionResponses(); speriamo di poter guardare lo scenario 2 nei prossimi due giorni. – John

1

ho trovato un trucco a buon mercato per ottenere ciò che voglio aggiungendo questo al metodo di avvio Configurazione:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 
    { 
     // Simple error page to avoid a repo dependency. 
     app.Use(async (context, next) => 
     { 
      try 
      { 
       await next(); 
      } 
      catch (Exception ex) 
      { 
       if (context.Response.HasStarted) 
       { 
        throw; 
       } 
       context.Response.StatusCode = 500; 
       context.Response.ContentType = "application/json"; 
       var json = JToken.FromObject(ex); 
       await context.Response.WriteAsync(json.ToString()); 
      } 
     }); 
//Rest of configure method omitted for brevity. 
} 
Problemi correlati