2014-03-26 2 views
5

Ich vermeide die Standard-ASP.NET-Ansatz der Umleitung auf Fehler (wie viele Leute). Sauberer AJAX-Code und SEO gehören zu den Gründen.HttpContext.Current.Items gelöscht über responseMode = "ExecuteURL"?

Allerdings verwende ich die folgende Methode, um es zu tun, und es scheint, dass ich HttpContext.Current.Items in der Übertragung verlieren kann?

<httpErrors errorMode="Custom" existingResponse="Replace"> 
    <remove statusCode="401" /> 
    <remove statusCode="403" /> 
    <remove statusCode="404" /> 
    <remove statusCode="500" /> 
    <error statusCode="401" responseMode="ExecuteURL" path="/Account/SignIn" /> 
    <error statusCode="403" responseMode="ExecuteURL" path="/Site/Forbidden" /> 
    <error statusCode="404" responseMode="ExecuteURL" path="/Site/NotFound" /> 
    <error statusCode="500" responseMode="ExecuteURL" path="/Site/Error" /> 
</httpErrors> 

Ich nahm an, es erfolgt nur eine Server.Transfer() unter die Decke, die ich Items verstehen bewahrt. (Siehe: Scope of HttpContext.Current.Items und http://weblog.west-wind.com/posts/2010/Jan/20/HttpContextItems-and-ServerTransferExecute)

Aber ich bin auch etwas in Items vor dem „ExecuteURL“ Erfassen und Abrufen/es nach der Übertragung ausgibt (oder was auch immer es ist), und es scheint zu verschwinden. Ich habe es in der Items Sammlung gesehen, sehe ich die Count Erhöhung auf 5, und wenn der Wert abgerufen wird, gibt es nur 2 Elemente in der Sammlung.

Was ist los?


Wenn Sie mehr darüber verstehen möchten, was ich mache und eine alternative Implementierung empfehlen, ich bin dafür offen. Ich benutze dies, um die ELMAH-Fehler-ID in ein ViewModel auf eine Weise zu übertragen, die frei von Rennbedingungen ist. (. Dh eine gemeinsame Abhilfe für diesen, die ich ersetzt habe, ist die jüngsten Fehler nur Anzeige) Hier ist mein Code:

Global.asax

protected void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args) { 
    ElmahSupplement.CurrentId = args.Entry.Id; 
} 

void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs e) { 
    if (ElmahSupplement.IsNotFound(e.Exception)) { 
     ElmahSupplement.LogNotFound((e.Context as HttpContext).Request); 
     e.Dismiss(); 
    } 
} 

SiteController.cs

public virtual ActionResult Error() { 
    Response.StatusCode = 500; 
    return View(MVC.Site.Views.Error, ElmahSupplement.CurrentId); 
} 

ElmahSupplement.cs

public class ElmahSupplement { 
    // TODO: This is a rather fragile way to access this info 
    private static readonly Guid contextId = new Guid("A41A67AA-8966-4205-B6C1-14128A653F21"); 

    public static string CurrentId { 
     get { 
      return 
       // Elmah 1.2 will fail to log when enumerating form values that raise RequestValidationException (angle brackets) 
       // https://code.google.com/p/elmah/issues/detail?id=217 
       // So this id could technically be empty here 
       (HttpContext.Current.Items[contextId] as string); 
     } 
     set { 
      HttpContext.Current.Items[contextId] = value; 
     } 
    } 

    public static void LogNotFound(HttpRequest request) { 
     var context = RepositoryProxy.Context; 
     context.NotFoundErrors.Add(new NotFoundError { 
      RecordedOn = DateTime.UtcNow, 
      Url = request.Url.ToString(), 
      ClientAddress = request.UserHostAddress, 
      Referrer = request.UrlReferrer == null ? "" : request.UrlReferrer.ToString() 
     }); 
     context.SaveChanges(); 
    } 

    public static bool IsNotFound(Exception e) { 
     HttpException he = e as HttpException; 
     return he != null && he.GetHttpCode() == 404; 
    } 
} 
+1

Ihre Annahme ist falsch, ASP.NET MVC verwendet nicht länger Server.Transfer und verhält sich daher nicht wie erwartet. Und Server.Transfer funktioniert auch nicht mit ASP.NET MVC, da MVC in einer asynchronen Pipeline neu erstellt wird. –

+0

Akash, du scheinst zwei Kommentare zu machen, aber ich verstehe, dass sie dasselbe sagen, also habe ich vielleicht falsch verstanden? Auch, obwohl ich Ihnen glaube, verstehe ich das Problem mit Server.Transfer nicht wirklich. Wird das nicht außerhalb des MVC-Handlers ausgeführt? Ich denke, vor allem, wenn Sie einen Vorschlag haben, wie Sie ansprechen, machen Sie es bitte. 18 Stunden bis die +50 verloren ist. :( – shannon

Antwort

0

Ich habe eine Spur verfolgt und folgendes festgestellt. Einige sind lose abgeleitet.

Das CustomErrorModule (im IIS-Modulstapel) empfängt die SEND_RESPONSE-Benachrichtigung.

Der HttpStatus ist 500, daher klont er den Kontext, legt eine neue URL (entsprechend der übereinstimmenden benutzerdefinierten Fehlerregel) fest und führt die Anforderung für diesen Kontext aus (siehe ExecuteRequest).

Der Zweck HttpContext.Items pro Dokumentation ist:

Ruft einen Schlüssel/Wert-Sammlung, die verwendet werden kann, um Daten zwischen einer IHttpModule-Schnittstelle und einer Schnittstelle IHttpHandler während einer HTTP-Anforderung zu organisieren und zu teilen.

Betrachtet man diese Funktionsdefinition kritisch, gibt es natürlich nur "HTTP-Anfrage". Es scheint jedoch wahrscheinlich, dass das Items Wörterbuch selbst ein Element in einem Wörterbuch ist, das im HttpContext codiert ist. Dies ist ein eindeutiger (geklonter) Verweis in dieser ausführenden untergeordneten Anforderung. Die Ablaufverfolgung zeigt die vollständige Pipeline (alle Module, z. B. doppelte Authentifizierung), die für diese ExecuteURL ausgeführt wird, so dass dieser isolierte Kontext natürlich erforderlich ist.

Von nicht verwaltetem Code ist es trivial GetParentContext. Aus verwaltetem Code ist diese Hierarchie jedoch nicht verfügbar. Also, ich bin ohne eine Möglichkeit, das Original Items zu erhalten.

Als alternative Lösung könnte es sinnvoll sein, eine Variable Global.asax zu verwenden, da meine Tests die untergeordnete Anfrage zeigten, die eine ApplicationInstance teilt, aber ich bin nicht sicher Client-Zugriff auf diese ist notwendigerweise sequenziell.

Ein anderer, möglicherweise besserer Ansatz wäre, die erneute Ausführung der gesamten Pipeline zu vermeiden; niemals den MVC-Handler verlassen (z. B. Controller.OnException und TransferToAction). Dies verhindert jedoch die Implementierung einer Single-Point-of-Truth-Konfiguration für die Fehlerseiten, da Fehler auch außerhalb von MVCs Aufmerksamkeit auftreten können.

1

Wie erläutert here, generiert die ExecuteURL zwei Anforderungen: der erste löst die Ausnahme und der zweite die Fehlerantwort aus.

Da Context.Items zwischen Anforderungen gelöscht wird, wird immer die zweite Anforderung vom Code generiert, daher der Unterschied zwischen den Elementen.

Probieren Sie die Sugestion im Post aus: Verwenden Sie stattdessen system.web> customErrors mit redirectMode = "ResponseRewrite".

+0

Ich fürchte, dass nicht beantwortet, warum Kontext Clearing ist. Wenn es tatsächlich eine zweite 'virtuelle' Client-Anfrage materialisiert, was ist der Mechanismus? So habe ich immer noch nicht genug Informationen, um es zu lösen. Ich akzeptiere immer noch die Antwort, wenn die vorgeschlagene Problemumgehung anwendbar ist, aber es funktioniert nicht mit MVC. Danke für den Link (+1). Ich werde einen Blick auf die Spur werfen. – shannon

+0

Ich sollte klarstellen, dass seit einem Major Funktion des Kontexts 'Items' ist es, Informationen zwischen Handlern und Modulen zu teilen, eine zweite Anfrage müsste ganz am Anfang des Prozesses beginnen, um diese Information (' HttpContext') natürlich zu verlieren. Gleichzeitig muss die IP antworten – shannon

+0

Aus meinen Tests scheint es, dass ExecuteURL von CustomErrorModule eine untergeordnete Anfrage generiert, die auf einem geklonten HttpContext ausgeführt wird.Es wird die gesamte Pipeline ausgeführt, die vom vorherigen Kontext umschlossen wird.Da dieser untergeordnete Kontext jedoch in unmanag geklont wird ed code erhält es kein Duplikat von 'Items'. – shannon

Verwandte Themen