2013-01-22 32 views
7

Piszę aplikację asp.net 4.5 za pomocą nowych funkcji routingu. Mam stronę, która wyświetla pewne informacje na temat przedmiotu. W zdarzeniu Page_Load sprawdzam dane trasy (identyfikator produktu) i uprawnienia użytkownika, a jeśli coś jest nie tak (np. Identyfikator dotyczy usuniętego elementu) używam Response.RedirectToRoute, aby wysłać je do pakowania, z powrotem do strony głównej. Nie przechodź GO, nie zbieraj 200 $.Prawidłowy sposób na pomijanie wykonywania strony po Response.RedirectToRoute

To miało sens, dopóki nie próbowałem uzyskać dostępu do usuniętego elementu, a zamiast strony głównej otrzymałem stronę błędu. Zrobiłem trochę kopania i odkryłem, że nawet po użyciu RedirectToRoute (w przeciwieństwie do standardowej metody Redirect), pozostałej części kodu strony continues to execute, który co najmniej wydaje się nieużyteczny (ponieważ mam zamiar wyrzucić wyniki) i generuje błędy gdy niezbędne dane nie istnieją.

Zrobiłem trochę więcej SO górnictwa i odkryłem niesamowitą evil, która jest Response.End(). Robi to, czego potrzebuję, ale nawet MSDN page mówi, że jest to bękartowe dziecko starożytnego przeklętego języka i nie nadaje się do oglądania światła dziennego. Podstawowym zarzutem wydaje się być fakt, że Response.End zgłasza wyjątek, a to źle wpływa na wydajność. Nie jestem najbardziej doświadczonym programistą, więc nie rozumiem tego problemu w całości, ale mam problem z przekonaniem, że zgłoszenie wyjątku jest droższe niż załadowanie całej strony internetowej. Model workarounds wydaje się dość złożony i nadmiernie skomplikowany, jeśli chodzi o proste zadanie, zwłaszcza że większość stron wymaga pewnego rodzaju sprawdzenia poprawności.

Co powinienem zrobić w tej sytuacji? Użyj Response.End i błagaj o wybaczenie mojej bezczelności? Wymieszać jakiś brzydki sposób obejścia problemu? A może moje zdanie na temat problemu jest od początku błędne? Naprawdę chciałbym wiedzieć.

Aktualizacja: Teraz, gdy przemyślałem to nieco dłużej, zastanawiam się, czy mam złą perspektywę na problem. Być może natychmiastowe przekierowanie nie jest najlepszą odpowiedzią na wrażenia użytkownika. Czy lepiej byłoby owijać wszystkie kontrolki w panelu i używać czegoś takiego?

Private Sub Page_Init(sender As Object, e As EventArgs) Handles Me.Init 
    'Validation Code 
    If notValid Then 
     ControlsPanel.Visible = false 
     ErrorPanel.Visible = true 
    End If 
End Sub 

Odpowiedz

3

I można wychodzić na kończyny, nie odpowiadając na pytanie bezpośrednio, ale lubiłem widząc aktualizację odnośnie doświadczenia użytkownika. Wolę Twoje sugerowane podejście.

chciałbym dać błędu 410 dla identyfikatorów, które nie są ważne i przedłużyć go trochę z (tłumaczone z C#):

Protected Sub ItemDoesNotExist() 
'item does not exist, serve up error page 
ControlsPanel.Visible = False 
ErrorPanel.Visible = True 

'add meta tags for noindex 
Dim mymeta As New HtmlMeta() 
mymeta.Name = "robots" 
mymeta.Content = "noindex" 
Page.Header.Controls.Add(mymeta) 

'RESPOND WITH A 410 
Response.StatusCode = 410 
Response.Status = "410 Gone" 
Response.StatusDescription = "Gone" 
Response.TrySkipIisCustomErrors = True 
'important for IIS7, otherwise the Custom error page for 404 shows. 
Page.Title = "item gone" 
End Sub 
+0

Dzięki za obserwacji w górę. Nie zdawałem sobie sprawy, że możesz odpowiedzieć kodem błędu i nadal ładować stronę. Na pewno będę o tym pamiętać w przyszłości. –

9

RedirectToRoute jest rzeczywiście owija Response.Redirect przechodząc false do zakończenia żądania - stąd wniosek trwa. Możesz użyć HttpApplication.CompleteRequest jako natychmiastowego wywołania, aby zakończyć żądanie, aby następne zdarzenia aplikacji nie były wywoływane.

Response.End (i inna odmiana Redirect) wyrzuca ThreadAbortException przerwać wątek przetwarzania żądania, które jest naprawdę złe sposobem, aby zatrzymać przetwarzanie żądań. W świecie .NET przetwarzanie wyjątków jest zawsze uważane za drogie, ponieważ CLR musi następnie przeszukać stos aż do bloków przetwarzania wyjątków, utworzyć stos śledzenia itp. IMO, CompleteRequest został wprowadzony w .NET 1.1, aby uniknąć tego samego, co faktycznie polega na ustawieniu flagi w kodzie infrastruktury ASP.NET, aby pominąć dalsze przetwarzanie z wyjątkiem zdarzenia EndRequest.

Jeszcze innym (i lepszym) sposobem jest użycie numeru Server.Transfer i uniknięcie podróży w obie strony w celu ustawienia przekierowania wszystkich razem. Jedynym problemem jest to, że klient nie widział przekierowanego adresu URL na pasku adresu przeglądarki. Zazwyczaj wolę tę metodę.

EDIT
CompleteRequest nigdy nie działa w przypadku stron gdzie późniejsze wydarzenia strona będzie nadal powoływać ponieważ strona bycia obsługi, wszystkie jego wydarzenia dzieje się w obrębie jednego (i obecny) zdarzeń aplikacji ProcessRequest.Więc jedynym sposobem wydaje się być ustawienie flagi i sprawdzić tę flagę w zadajnikami takich jak Render, PreRender, RaisePostBackEvent itp

Z punktu widzenia technicznego, jest sens, aby mieć taką funkcjonalność w klasie bazowej stronę (czyli utrzymywanie flagę, oferując CompleteRequest metoda podklas i nadpisanie metod zdarzeń cyklu życia). Na przykład,

internal class PageBase: System.Web.UI.Page 
{ 
    bool _requestCompleted; 

    protected void CompleteRequest() 
    { 
     Context.ApplicationInstance.CompleteRequest(); 
     _requestCompleted = true; 
    } 

    protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl, 
    string eventArgument) 
    { 
     if (_requestCompleted) return; 
     base.RaisePostBackEvent(sourceControl, eventArgument); 
    } 

    protected internal override void Render(HtmlTextWriter writer) 
    { 
     if (_requestCompleted) return; 
     base.Render(writer); 
    } 

    protected internal override void OnPreRender(EventArgs e) 
    { 
     if (_requestCompleted) return; 
     base.OnPreRender(e); 
    } 

    ... and so on 
} 
+1

Dzięki za odpowiedź. Zauważyłem, że opcja "CompleteRequest" została zaproponowana jako lepsza opcja, ale nadal wykonuje cały kod na stronie. Różne elementy sterujące na stronie pobierają dane z tego elementu, a gdy nie istnieją, generują błędy, więc pojawia się strona błędu, a nie przekierowanie. Mógłbym przejrzeć i sprawdzić wiarygodność każdej metody, ale to wydaje się zbyteczne i niepotrzebne. –

+0

Uważam, że interesujące jest wspomnienie o 'Server.Transfer'. Chociaż doceniam korzyści wynikające z uniknięcia podróży w obie strony, nie wydaje mi się, aby ta metoda działała dobrze dla moich celów, ponieważ aktualny adres URL użytkownika jest nieprawidłowy. Czytałem również, że 'Server.Transfer' wywołuje' Response.End' i nie widzę przeciążenia funkcji, aby tego uniknąć. –

+2

@probackpacker, proszę przyjąć moje przeprosiny! 'CompleteRequest' wpływa na zdarzenia potoku aplikacji, ale bieżące zdarzenie obsługi byłoby kontynuowane. Nie byłoby innego sposobu niż użycie flagi do sprawdzenia na stronie - z punktu widzenia konserwacji możesz wyodrębnić ją do klasy strony głównej wielokrotnego użytku (zobacz moją edycję). – VinayC