Правильный способ пропустить выполнение страницы после ответа.RedirectToRoute


Я пишу анекдот. asp.net 4.5 приложение, использующее новые функции маршрутизации. У меня есть страница, на которой отображается некоторая информация об элементе. В событии Page_Load я проверяю данные маршрута (идентификатор элемента) и разрешения пользователя, и если что-то не так (например, идентификатор для удаленного элемента), я использую Response.RedirectToRoute, чтобы отправить их обратно на домашнюю страницу. Не проходят идти, не собирают $200.

Это имело смысл, пока я не попытался получить доступ к удаленному элементу и вместо домашней страницы я получил ошибку страница. Я немного покопался и обнаружил, что даже после использования RedirectToRoute (в отличие от стандартного метода Redirect) остальная часть кода страницы продолжает выполняться, что по крайней мере кажется расточительным (поскольку я просто собираюсь выбросить результаты) и выдает ошибки, когда необходимые данные не существуют.

Я сделал еще немного так майнинг и обнаружил невероятное зло то есть Response.End(). Он делает то, что мне нужно, но даже страница MSDN говорит мне, что Response.End это незаконнорожденное дитя древнего проклятого языка, и оно не годится для того, чтобы видеть дневной свет. Главным возражением, по-видимому, является сам факт такого ответа.End создает исключение, и это плохо сказывается на производительности. Я не самый опытный разработчик, поэтому я не совсем понимаю проблему, но мне трудно поверить, что создание исключения стоит дороже, чем загрузка всей веб-страницы. Обходные пути кажутся довольно сложными и чрезмерными для такой простой задачи, тем более что большинство страниц требуют какой-то проверки подлинности.

Что я должен делать в этой ситуации? Использовать Response.End и просить прощения за мою дерзость? Сколотить какой-нибудь уродливый обходной путь? Или мой взгляд на проблему изначально неверен? Мне бы очень хотелось это знать.

Обновление: теперь, когда я подумал об этом немного больше, я задаюсь вопросом, есть ли у меня неправильный взгляд на проблему. Возможно, немедленное перенаправление-не лучший ответ для пользовательского опыта. Был бы я лучше? убрать все элементы управления в панель и использовать что-то вроде этого?

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
2 7

2 ответа:

Я, возможно, иду на крайность, не отвечая на вопрос напрямую, но мне понравилось видеть ваше обновление относительно пользовательского опыта. Я предпочитаю предложенный вами подход.

Я хотел бы дать ошибку 410 для идентификаторов, которые не являются допустимыми и расширить его немного с (переведено с 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

RedirectToRoute на самом деле является ответом wraps .Перенаправление передача false для завершения запроса-следовательно, запрос продолжается. Вы можете использовать HttpApplication.CompleteRequest как немедленный вызов для завершения запроса, чтобы не вызывались следующие события приложения.

Response.End (и другие варианты перенаправления) бросает ThreadAbortException, чтобы прервать поток обработки запроса, который на самом деле является плохим способом остановить обработку запроса. В .Чистый мир, исключение обработка всегда считается дорогостоящей, потому что CLR затем нужно искать в стеке все блоки обработки исключений, создавать трассировку стека и т. д. IMO, CompleteRequest был введен в .NET 1.1, чтобы избежать того же, что на самом деле зависит от установки флага в ASP.NET код инфраструктуры для пропуска дальнейшей обработки, кроме события EndRequest.

Еще один (и лучший) способ-использовать сервер.Перенесите и избегайте кругового перемещения клиента для установки перенаправления все вместе. Единственная проблема заключается в том, что клиент не будет видеть перенаправленный URL-адрес в адресной строке браузера. Я обычно предпочитаю этот метод.

Править
CompleteRequest никогда не будет работать в случае страницы, где последующие события страницы будут по-прежнему вызываться, потому что страница является обработчиком, все ее события происходят в пределах одного (и текущего) события приложения ProcessRequest. Таким образом, единственный способ, кажется, установить флаг и проверить этот флаг в переопределениях, таких как Render, PreRender, RaisePostBackEvent и т.д.

С точки зрения технического обслуживания, это имеет смысл иметь такую функциональность в базовом классе страницы (т. е. поддерживать флаг, предлагать метод CompleteRequest подклассам и переопределять методы событий жизненного цикла). Например,

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
}