2016-02-06 3 views
5

Ich bin ein api Endpunkt in einem MVC-6 WebAPI Aufruf:MVC 6 WebAPI Rückkehr HTML-Fehlerseite statt json Version von Ausnahmeobjekt

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 

Im Körper Ich habe Json serialisiert Testeinheit.

Ich habe einen Fehler in meinem Entity Controller-Code und die API gibt eine 500 Antwort "Server Error" Ich weiß, was der Fehler ist und es beheben wird, aber das Problem, das ich brauche, ist, dass die API zurückgibt HTML statt des serialisierten JSon-Ausnahmeobjekts - Json ist das, was ich erwarte: es ist das, was das alte Webapi zurückgeben würde. Ich habe den Code von einem alten Testprojekt portiert, von dem ich weiß, dass es funktioniert.

Warum gibt MVC 6 WebAPI HTML statt json zurück? Gibt es eine Konfiguration, die ich tun muss?

EDIT: Ich habe Accept: application/json zu den Headern wie von @danludwig vorgeschlagen, aber dies hat das Problem nicht gelöst, ich habe immer noch eine HTML-Fehlerseite zurück.

Ich sah meine StartUp.cs an und gefunden:

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

im ConfigureApp Verfahren. Ich habe mit app.UseDeveloperExceptionPage() getestet; auskommentiert. Dies verhinderte die Rückgabe der HTML-Fehlerseite im Antwortkörper der API, allerdings bekomme ich immer noch nicht das serialisierte json-Ausnahmeobjekt.

+1

'Accept: application/json' – danludwig

+0

leider nicht. Ich habe die Frage bearbeitet, um den neuen Header anzuzeigen, aber danke. – John

+0

eine Chance, dass 'JsonOutputFormatter' von Ihrem Code entfernt wird? –

Antwort

5

ExceptionHandlerMiddleware konfiguriert bei Verwendung von UseExceptionHandler("Home/Error") enthält keine Unterstützung für JSON. Es wird nur die Fehler-HTML-Seite zurückgeben. Das gleiche gilt für die Verwendung von UseDeveloperExceptionPage.

Soweit ich weiß, müssen Sie sich ein Stück Code hinzufügen, der Fehler behandelt und einen json zurückgibt.

  • Eine Option ist eine Ausnahme-Filter zu verwenden und es entweder global oder auf ausgewählte Controllern hinzufügen, obwohl dieser Ansatz nur Ausnahmen abdecken würde von den Reglern Methoden kommen. Zum Beispiel werden die folgenden Filter ein Json-Objekt zurück nur, wenn die Anforderung akzeptieren war application/json (Sonst wäre es die Ausnahme passieren lassen, durch die zum Beispiel durch die globale Fehlerseite behandelt werden könnte):

    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()); 
    }); 
    
  • Another Die Option besteht darin, eine eigene Exception-Handler-Middleware hinzuzufügen, die die app.UseExceptionHandler-Überladung verwendet, mit der Sie das Verhalten der alternativen Pipeline angeben können, die die Ausnahme verarbeitet. Ich habe schrieb schnell ein ähnliches Beispiel eine Inline-Middleware, die nur ein JSON-Objekt zurück, wenn die Anforderung war application/json akzeptieren:

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

Beide Ansätze lassen Sie andere Fehlerbehandlungsroutinen, die vielleicht zur Verfügung stellen HTML-Seiten für Nicht-Json-Anfragen (Eine andere Idee wäre, entweder eine JSON- oder eine HTML-Seite von Ihrem benutzerdefinierten Error-Handler zurückzugeben).

PS. Wenn Sie den zweiten Ansatz verwenden, möchten Sie diese Logik wahrscheinlich in eine eigene Middleware-Klasse einfügen und einen anderen Ansatz zum Generieren der JSON-Antwort verwenden.In diesem Fall nehmen Sie einen Blick auf, was JsonResultExecutor tut

+0

Dies ist eine gute Antwort. Ich habe davon Gebrauch gemacht und es funktioniert gut für Ausnahmen, die ich auf der Controller-Ebene werfe. Es scheint jedoch ein tieferes Problem in der ASP-Kern-Ausnahmebehandlung zu geben. Ich habe vergessen, den JSON-Formatierer für kreisförmige referenzierende Objekte zu konfigurieren. Das IActionResult wurde vom Controller zurückgegeben, aber innerhalb des asp-Kerns, als die Antwort generiert wurde, hat der JSON-Formatierer eine Ausnahme ausgelöst und der Client eine 502 Bad Gateway-Antwort erhalten! Es wäre wünschenswert gewesen, das serialisierte json-Ausnahmeobjekt zu erhalten. Bad Gateway macht hier keinen Sinn. Hast du das gesehen? Problemumgehung? – John

+0

Ihr Controller gibt also ein JSON-Ergebnis (keine Ausnahme) zurück, das bei der Verarbeitung zu einer Ausnahme führt. Ich denke, der erste Ansatz wird diesen nicht erfassen, haben Sie überprüft, ob der zweite in diesem Szenario ausgeführt wird? Übrigens habe ich die 502 gesehen, als ich einen dummen Fehler in der Inline-Middleware gemacht habe, den JSON manuell zu formatieren. Nicht sicher, warum das Framework diese als 502 behandelt! –

+0

Ich habe das Szenario 2 noch nicht getestet, werde es aber bald tun. Wirklich sind es die 502s, die mich ausflippen. Es wäre gut, wenn wir etwas wie: app.UseJsonExceptionResponses(); hoffentlich kann ich in den nächsten Tagen Szenario 2 betrachten. – John

1

ich einen günstigen Hack zu bekommen, was gefunden! Ich möchte das Hinzufügen der Methode Startup konfigurieren:

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. 
} 
Verwandte Themen