Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

ASP_NET_MVC_4_Framework_s_primerami_na_C_dlya_p

.pdf
Скачиваний:
29
Добавлен:
19.03.2016
Размер:
17.66 Mб
Скачать

Использование других возможностей фильтров

В предыдущих примерах мы разобрали все, что необходимо для эффективной работы с фильтрами. Далее мы продемонстрируем вам некоторые дополнительные возможности фильтрации MVC Framework, которые представляют некоторый интерес, хотя и не так широко используются.

Фильтрация без атрибутов

Обычно для использования фильтров мы применяем атрибуты, как это уже было показано в предыдущих разделах. Тем не менее, этому есть альтернатива - класс Controller реализует интерфейсы IAuthorizationFilter, IActionFilter, IResultFilter и IExceptionFilter. Он также предоставляет пустые виртуальные реализации всех методов OnXXX, с которыми вы уже также знакомы, например, OnAuthorization и OnException. В листинге 16-32 мы обновили контроллер Home, чтобы использовать эту функцию и создать класса контроллера, который будет самостоятельно анализировать свою производительность.

Листинг 16-32: Используем методы фильтров контроллера

using System;

using System.Collections.Generic; using System.Linq;

using System.Web; using System.Web.Mvc;

using Filters.Infrastructure; using System.Diagnostics; namespace Filters.Controllers

{

public class HomeController : Controller

{

private Stopwatch timer;

[Authorize(Users = "adam, steve, jacqui", Roles = "admin")] public string Index()

{

return "This is the Index action on the Home controller";

}

[HandleError(ExceptionType = typeof(ArgumentOutOfRangeException), View = "RangeError")]

public string RangeTest(int id)

{

if (id > 100)

{

return String.Format("The id value is: {0}", id);

}

else

{

throw new ArgumentOutOfRangeException("id", id, "");

}

}

public string FilterTest()

{

return "This is the FilterTest action";

}

protected override void OnActionExecuting(ActionExecutingContext filterContext)

{

timer = Stopwatch.StartNew();

}

protected override void OnResultExecuted(ResultExecutedContext filterContext)

{

timer.Stop();

filterContext.HttpContext.Response.Write(

411

string.Format("<div>Total elapsed time: {0}</div>", timer.Elapsed.TotalSeconds));

}

}

}

Мы удалили фильтры из метода действия FilterTest, потому что они больше не требуется - контроллер Home будет добавлятьть информацию о производительности к ответу для каждого метода действия.

На рисунке 16-11 показан эффект запуска программы и перехода по ссылке /Home/RangeTest/200, которая приведет нас к действию RangeTest, не вызывая исключения, которое мы создали для демонстрации фильтра HandleError.

Рисунок 16-11: Эффект реализации методов фильтра непосредственно в контроллере

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

Подсказка

В наших проектах мы предпочитаем использовать атрибуты: нам нравится разделять логику контроллеров и фильтров. Если вы хотите применить фильтр ко всем контроллерам, то можете использовать для этого глобальные фильтры.

Используем глобальные фильтры

Глобальные фильтры применяются ко всем методам действий во всех контроллерах приложения. Превратить обычный фильтр в глобальный можно с помощью метода RegisterGlobalFilters, определенного в файле App_Start/FilterConfig.cs. В листинге 16-33 показано, как сделать фильтр ProfileAll, который мы создали в листинге 16-30, глобальным.

Листинг 16-33: Создаем глобальный фильтр в файле FilterConfig.cs

using System.Web; using System.Web.Mvc;

using Filters.Infrastructure; namespace Filters

{

public class FilterConfig

412

{

public static void RegisterGlobalFilters(GlobalFilterCollection filters)

{

filters.Add(new HandleErrorAttribute()); filters.Add(new ProfileAllAttribute());

}

}

}

Метод RegisterGlobalFilters вызывается из метода Application_Start в файле Global.asax, что гарантирует регистрацию глобальных фильтров при запуске приложения.

Примечание

Первый оператор в методе RegisterGlobalFilters, создаваемом Visual Studio по

умолчанию, настраивает стандартные правила обработки исключений MVC. Он будет визуализировать представление /Views/Shared/Error.cshtml при

возникновении необработанного исключения. Во время разработки эти правила обработки исключений по умолчанию отключены. В секции "Создание фильтра исключения"далее в этой главе говорится о том, как ее включить в файле

Web.config.

Параметром для метода RegisterGlobalFilters является GlobalFilterCollection. Чтобы зарегистрировать глобальный фильтр, используйте метод Add, например:

filters.Add(new ProfileAllAttribute());

Обратите внимание, что к фильтру нужно обращаться, используя полное имя класса (ProfileAllAttribute), а не краткое, которое применяется при обращении к фильтру как к атрибуту (ProfileAll). Фильтр, зарегистрированный таким образом, будет применен к каждому действию метода.

Чтобы продемонстрировать работу глобальных фильтров, мы создали контроллер под названием Customer, как показано в листинге 16-34. Новый контроллер нам нужен потому, что мы хотим использовать код, к которому не применялись фильтры в предыдущих разделах.

Листинг 16-34: Контроллер Customer

using System.Web.Mvc; namespace Filters.Controllers

{

public class CustomerController : Controller

{

public string Index()

{

return "This is the Customer controller";

}

}

}

Это очень простой контроллер, в котором есть действие Index, возвращающее строку. На рисунке 16-12 показан эффект применения глобального фильтра, который мы увидели, запустив приложение и перейдя по ссылке /Customer.

Хотя мы не применяли фильтр непосредственно к контроллеру, глобальный фильтр добавляет информацию о производительности, как показано на рисунке.

413

Рисунок 16-12: Эффект применения глобального фильтра

Задаем порядок выполнения фильтров

Мы уже говорили о том, что фильтры выполняются в определенной последовательности, в зависимости от типа: фильтры авторизации, фильтры действий и затем фильтры результатов. Фильтры исключений выполняются на любом этапе, когда появляется необработанное исключение. Однако внутри типа порядок выполнения отдельных фильтров определяется вами.

В листинге 16-35 показан простой класс фильтра действий под названием SimpleMessageAttribute, который мы добавили в папку Infrastructure и на примере которого продемонстрируем, как задавать порядок выполнения фильтров.

Листинг 16-35: Простой фильтр действий

using System;

using System.Collections.Generic; using System.Linq;

using System.Web; using System.Web.Mvc;

namespace Filters.Infrastructure

{

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class SimpleMessageAttribute : FilterAttribute, IActionFilter

{

public string Message { get; set; }

public void OnActionExecuting(ActionExecutingContext filterContext)

{

filterContext.HttpContext.Response.Write( string.Format("<div>[Before Action: {0}]<div>", Message));

}

public void OnActionExecuted(ActionExecutedContext filterContext)

{

filterContext.HttpContext.Response.Write( string.Format("<div>[After Action: {0}]<div>", Message));

}

}

}

При вызове методов OnActionExecuting и OnActionExecuted этот фильтр записывает в ответ сообщение, часть которого задается с помощью свойства Message.

Мы можем применить несколько экземпляров этого фильтра к методу действия, как показано в листинге 16-36 (обратите внимание, что в атрибуте AttributeUsage мы устанавливаем свойству

AllowMultiple значение true).

414

Листинг 16-36: Применяем несколько фильтров к действию

using System.Web.Mvc;

using Filters.Infrastructure; namespace Filters.Controllers

{

public class CustomerController : Controller

{

[SimpleMessage(Message = "A")] [SimpleMessage(Message = "B")] public string Index()

{

return "This is the Customer controller";

}

}

}

Мы создали два фильтра с разными сообщениями: в первом – сообщение А, во втором - сообщение B. Мы могли бы использовать два различных фильтра, но такой подход позволит упростить наш пример. Когда вы запустите приложение и перейдете по ссылке /Customer, то увидите результат, показанный на рисунке 16-13.

Рисунок 16-13: Несколько фильтров в одном методе действия

Когда мы запустим этот пример, MVC Framework выполнит фильтр А перед фильтром B, но могло быть и наоборот – платформа не гарантирует какого-либо определенного порядка выполнения. В большинстве случаев порядок не имеет значения, но если он важен, используйте свойство Order, как показано в листинге 16-37.

Листинг 16-37: Используем свойство Order в фильтре

[SimpleMessage(Message = "A", Order = 2)] [SimpleMessage(Message = "B", Order = 1)] public ActionResult Index()

{

Response.Write("Action method is running"); return View();

}

Параметр Order принимает значение int, и MVC Framework выполняет фильтры в порядке его возрастания. В листинге мы назначили фильтру B наименьшее значение, поэтому он выполняется первым, как показано на рисунке 16-14.

415

Рисунок 16-14: Указываем порядок выполнения фильтров

Примечание

Обратите внимание, что методы OnActionExecuting выполняются в порядке, который определили мы, а методы OnActionExecuted - в обратном порядке. MVC Framework строит стек фильтров, когда выполняет их перед методом действия, а затем раскручивает этот стек, или возвращает его в исходное положение. Поведение для раскрутки стека нельзя изменить.

Если мы не указываем значение для свойства Order, по умолчанию ему присваивается значение 1. Это означает, что если вы смешиваете фильтры так, что одни имеют значения Order, а другие нет, то фильтры без значений будут выполняются в первую очередь, так как они имеют самые низкие значения Order.

Если несколько фильтров одного типа (например, фильтров действий) имеют одинаковое значение Order (скажем, 1), то MVC Framework определяет порядок выполнения на основании того, к чему применен фильтр. Глобальные фильтры выполняются в первую очередь, затем - фильтры, примененные к классу контроллера, затем - к методу действия.

Примечание

Для фильтров исключений применяется обратный порядок исполнения. Если фильтры исключений с одинаковым значением Order применяются к контроллеру и

методу действия, фильтр метода действия выполняется в первую очередь. Глобальный фильтр исключений с тем же значением Order будет выполнен

последним.

Использование встроенных фильтров

MVC Framework предоставляет некоторые встроенные фильтры, которые мы описали в таблице 16-9.

Таблица 16-9: Встроенные фильтры

 

Фильтр

Описание

 

 

RequireHttps

Разрешает использование протокола HTTPS для действий

 

416

Фильтр

Описание

 

 

OutputCache

Кэширует вывод из метода действия

 

 

ValidateInput и

Фильтры авторизации, связанные с безопасностью

ValidationAntiForgeryToken

 

 

 

AsyncTimeout и NoAsyncTimeout

Используются в асинхронных контроллерах

 

 

ChildActionOnlyAttribute

Фильтр авторизации, который поддерживает

вспомогательные методы Html.Action и Html.RenderAction

 

Большинство этих фильтров описаны в других частях книги. Тем не менее, два фильтра - RequireHttps и OutputCache – не относятся к чему-либо конкретно, так что мы разберем их применение здесь.

Используем фильтр RequireHttps

Фильтр RequireHttps разрешает использование протокола HTTPS для действий. Он перенаправляет браузер пользователя к тому же самому действию, но используя префикс протокола https://.

Вы можете переопределить метод HandleNonHttpsRequest и создать пользовательское поведение для обработки незащищенных запросов. Этот фильтр применяется только к запросам GET. Данные формы будут потеряны, если таким образом будет перенаправлен запрос POST.

Примечание

При использовании фильтра RequireHttps у вас могут возннуть проблемы с порядком выполнения фильтров, потому что RequireHttps - фильтр авторизации,

а не фильтр действия. Информация о порядке выполнения фильтров дана в разделе «Задаем порядок выполнения фильтров» ранее в этой главе.

Используем фильтр OutputCache

Фильтр OutputCache запускает кэширование вывода от метода действия таким образом, чтобы одно и то же содержание могло быть повторно использовано для обслуживания последующих запросов к одному URL. Кэширование вывода действия может значительно увеличить производительность, так как позволяет избежать многих отнимающих время этапов при обработке запроса (например, обращение к базе данных). Конечно, недостатком кэширования является то, что вы сможете предоставлять только один ответ на все запросы, что подходит не для всех методов действий.

Фильтр OutputCache использует механизм кэширования вывода из ядра платформы ASP.NET, и если вы когда-либо использовали кэширование в приложениях Web Forms, то параметры конфигурации будут вам знакомы. Фильтр OutputCache можно использовать для контроля кэширования на стороне клиента, изменяя значения, отправляемые в заголовке Cache-Control. В таблице 16-10 показаны параметры, которые можно установить для этого фильтра.

Таблица 16-10: Параметры для фильтра OutputCache

Параметр

 

Тип

 

Описание

 

 

 

 

 

Duration

 

int

 

Обязательный - определяет, как долго вывод

 

 

остается в кэше (в секундах).

 

 

 

 

 

 

 

 

 

VaryByParam

 

string (список,

 

Сообщает ASP.NET использовать новую запись

 

разделенный точкой с

 

кэша для каждой комбинации значений

 

 

 

 

 

 

417

Параметр

 

Тип

Описание

 

 

 

 

 

 

запятой)

Request.QueryString и Request.Form,

 

 

 

совпадающей с указанными именами. Значение по

 

 

 

умолчанию, none, означает «не изменять по

 

 

 

значениям строки запроса или формы». Другой

 

 

 

вариант, *, означает «изменять по значениям

 

 

 

строки запроса или формы». Если не указано иное,

 

 

 

используется значение none.

 

 

 

 

 

 

string (список,

Сообщает ASP.NET использовать новую запись

VaryByHeader

 

разделенный точкой с

кэша для каждой комбинации значений,

 

 

запятой)

отправленных в названиях заголовков HTTP.

 

 

 

 

 

 

 

Если указано, ASP.NET вызывает метод

 

 

 

GetVaryByCustomString в файле Global.asax,

 

 

 

передает это произвольное значение строки в

VaryByCustom

 

string

качестве параметра, что позволит вам

 

генерировать свой собственный ключ кэша.

 

 

 

 

 

 

Специальное значение browser позволяет

 

 

 

изменять кэш по названию и старшему номеру

 

 

 

версии браузера.

string (список, VaryByContentEncoding разделенный точкой с

запятой)

Location

 

OutputCacheLocation

 

 

 

NoStore

 

bool

 

 

 

CacheProfile string

SqlDependency string

Позволяет ASP.NET создать отдельный кэш для каждого закодированного содержимого (например, gzip и deflate), которое может быть запрошено браузером.

Указывает, где должен быть закэширован вывод. Он принимает одно из следующих значений : Server (только в памяти сервера), Client (только в браузере пользователя), Downstream (в браузере пользователя или любом промежуточном устройстве, поддерживающем кэширование HTTP, таком как прокси-сервер), ServerAndClient (объединение Server и Client), Any (объединение

Server и Downstream) либо None (кэширование не используется). Если не указано иное, параметр принимает значение по умолчанию Any.

Если содержит true, то ASP.NET отправит заголовок Cache-Control: no-store в браузер,

что укажет браузеру кэшировать страницу только в течение того времени, которое необходимо для ее отображения. Параметр используется только для защиты очень чувствительных данных.

Параметр указывает ASP.NET получить параметры кэша из раздела под названием

<outputCacheSettings> в файле Web.config.

Если указать пару база данных/имя, данные в кэше будут удалены автоматически при изменении соответствующих данных из базы. Для этого требуется функция SQL cache dependency, которую довольно сложно настроить. Более подробно о ней можно почитать на http://msdn.microsoft.com/enus/library/ms178604.aspx.

418

Одной из сильных сторон фильтра OutputCache является то, что его можно применять к дочерним действиям. Дочернее действие вызывается из представления с помощью вспомогательного метода Html.Action. Это позволяет нам выбирать, какие части ответа кэшируются и какие генерируются динамически. Мы обсудим дочерние действия подробно в главе 18, но в листинге 16-38 продемонстрируем работу с ними на примере простого контроллера под названием SelectiveCache.

Листинг 16-38: Контроллер SelectiveCache

using System;

using System.Web.Mvc; namespace Filters.Controllers

{

public class SelectiveCacheController : Controller

{

public ActionResult Index()

{

Response.Write("Action method is running: " + DateTime.Now); return View();

}

[OutputCache(Duration = 30)] public ActionResult ChildAction()

{

Response.Write("Child action method is running: " + DateTime.Now); return View();

}

}

}

Контроллер в листинге 16-38 определяет два метода действия:

Метод ChildAction, к которому применен фильтр OutputCache. Этот метод действия мы будем вызывать из представления.

Метод действия Index, который будет родительским действием.

Оба метода действий записывают время, которое потребовалось для их выполнения, в объект Response. В листинге 16-39 показано представление Index.cshtml (связанное с методом действия

Index).

Листинг 16-39: Представление, который вызывает кэшированное дочернее действие

@{

ViewBag.Title = "Index";

}

<h2>This is the main action view</h2> @Html.Action("ChildAction")

Как видите, в конце представления мы вызываем метод ChildAction. Представление для этого метода показано в листинге 16-40.

Листинг 16-40: Представление ChildAction.cshtml

@{

Layout = null;

}

<h4>This is the child action view</h4>

Запустите приложение и перейдите по ссылке /SelectiveCache. В первый раз вы увидите, что и родительское, и дочернее действие указывают в своих ответах одно и то же время. Если вы перезагрузите страницу (или перейдете по той же ссылке в другом браузере), то увидите, что время, указанное родительским действием, изменилось, но время дочернего действия осталось прежним.

419

Это говорит нам о том, что мы видим кэшированный вывод от первоначального вызова, как показано на рисунке 16-15.

Рисунок 16-15: Эффект кэширования вывода дочернего действия

Подсказка

Возможно, вам придется обновить страницу еще раз, прежде чем начнется кэширование - это объясняется способом компиляции представлений при первом запуске приложения MVC (который мы рассмотрим в главе 18).

Резюме

В этой главе мы рассмотрели фильтры как способ инкапсуляции логики, которая относится к сквозной функциональности. Мы изучили различные виды фильтров и их реализации. Мы научились применять фильтры в качестве атрибутов к контроллерам и методам действий, а так же в качестве глобальных фильтров. Фильтры расширяют логику обработки запроса, избавляя нас от необходимости включать дополнительную логику в методы действий. В следующей главе мы научимся изменять и расширять поведение MVC Framework для работы с контроллерами.

420

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]