Saturday, March 12, 2016

Обзор книги "JavaScript: The Good Parts"

Сегодняшняя статья посвящена обзору замечательной книги по языку Java Script автора Дугласа Крокфорда JavaScript: The Good Parts
Мое знакомство с этой книгой началось после того, как я спросил у коллег по работе, что бы такого почитать по JavaScript, чтобы понять его более глубоко. И одним из источников, который мне порекомендовали, была данная книга. В этой книге есть как свои плюсы и минусы. Но на них я бы хотел остановиться более подробно.
Плюсы:
  • Небольшой объем книги
  • Достаточная глубина подачи большей части материала
  • Полезные рекомендации по языку
  • Интересные примеры

Минусы:
  • Навязывание автором своего мнения
  • Лишние главы, которые можно было опустить
  • Некоторые части книги объяснены поверхностно
  • Диаграммы
Давайте продвигаться от позитивного. Так почему же все-таки стоит почитать эту книгу? Первая причина  это достаточная известность автора данной книги, а также размер книги (всего около 150 страниц). Как разработчик .NET, я достаточно продолжительное время не встречал книг, которые могут при таком маленьком количестве страниц передать нужную информацию и заинтересовать в дальнейшем развитии себя в роли JS (JavaScript) разработчика. Но Дугласу Крокфорду это удалось. Кроме отличной книги, у него есть не менее замечательный курс на Plurasight, который носит такое же название, как и книга JavaScript the Good Parts. Если вам интересен язык JavaScript, рекомендую посмотреть этот курс. Тем более, не думаю, что 1287 человек, которые проголосовали за этот курс на Plurasight могли ошибиться о оценить его случайно на 4.5 рейтинга.
Вторая причина, которую я указал – глубина подачи материала. Часть материала описана просто замечательно, но некоторые моменты описаны очень поверхностно. Например, по книге вы не поймете отношение между объектами, прототипами и функциями. Слишком уж мало информации об этом дает автор. Но другие аспекты  как работает оператор new, как правильно использовать тот или иной подход, как работают сравнения – подано очень неплохо. Как видите, со второго пункта я плавно перешел в третий, а именно: почему стоит использовать синтаксис языка JS и какие подводные камни нас могут ожидать. Пример таких предупреждений и рекомендаций:
  • JavaScript arrays aren’t proper arrays with array-like performance, they are weird dictionaries;
  • variables have function not block scope;
  • unless declared inside a function variables have global scope;
  • there is a difference between the equality == and === (and similarly the inequality operators). The short one coerces and then compares, the longer one does not, and is thus preferred.

У автора же есть целых две главы, в которых он это описывает. Идут они как дополнения и называются Awful Parts и Bad Parts. Думаю, по названию более-менее понятно, что там описано.
Ну и последний пункт – это интересные примеры. Мне, например, нравятся книги, где приводится пример, а затем раскрывается, что в этом примере не так и почему стоит использовать другое описание. Также приводятся примеры с описанием того, к чему такой код может привести. Правда, таких примеров очень мало; в видеокурсе автора их намного больше, и раскрыты они намного глубже.
Негативные моменты книги
Теперь пора пройтись по негативным моментам, которые встречаются в книге. Их не так и много, но они, к сожалению, присутствуют. Например, рекомендация не использовать “i++”, а использовать вместо инкрементной операции такой подход: “i += 1”. По мнению автора, такой подход делает “код чище”. К сожалению, подход, который описывает автор, ведет к другой проблеме, которая известна в мире разработке как магические числа. Понимаю, что некоторые понятия, как классические паттерны, DDD паттерны по боку для JS девелоперов, но мне кажется, что подход с магическими числами  неудачный подход, тем более, для такого примитивного сценария. Еще небольшое смущение вызвала рекомендация автора по поводу void. А так в целом рекомендации больше позитивные, чем негативные, что подчеркивает огромный опыт автора в этой области.
Второй пункт, который я описал и который мне не понравился, – так-это то, что некоторые части книги, или даже целые главы, можно было просто убрать. Я считаю, что вместо регулярных выражений, которые написаны ну очень плохо, да и больше отталкивают от их использования, чем помогают, плюс примеры не несут по себе никакой информативности, лучше было бы больше внимания уделить ООП. А такие дополнения, как LSLint в дополнение C и JSON в дополнение E, можно спокойно выбросить. Дополнение С является рекламой разработки автора. А информация о JSON описана ну очень скудно, да и сам пример, мягко говоря, никакой.
Так как я и писал, некоторые моменты в книге плохо раскрыты. А именно: ООП как таковое. Лучше эту книгу в данном случае читать после какой-то другой книги в которой эти моменты рассмотрены намного лучше. Считаю, если бы эту книгу немного переделать и убрать лишнее, то у нас будет + 30-50 новых страниц, и объем книги будет 180-200 станиц, но зато с покрытием всех тем.
И последний негативный момент – огромное количество диаграмм.
Если честно, то меня очень смущает уместность этих диаграмм, так как люди не запоминают обычно их в таком большом количестве. Странно, что OReilly пропустили такое большое количество диаграмм. Мне понравился комментарий одного читателя о блоках-картинках в книге.
“A large portion of the book is occupied by flowcharts describing the syntax of the language. (E.g., one page is a flowchart describing how to format floating-point numbers.) If I were a finite-state automaton who wanted to know how to recognize floating-point numbers, this would be useful for me. Since I am a human being who can already read and write floating-point numbers, it's an inexplicable waste of space. I'm baffled that the author thought such diagrams served any purpose, and that O'Reilly allowed him to include them and assigned someone to lay them out for publication. If anyone can explain this to me I'd be grateful.”
Итоги
Хотелось бы кратко прорезюмировать свои впечатления от данной книги. Я бы ей поставил оценку 4 из 5-ти и рекомендовал бы к прочтению ввиду краткости и лаконичности подачи материала, а также за то, что автор дает дельные советы, чего и когда нужно избегать. А так как я начал читать эту книгу после просмотра курса этого автора на Plurasight, то она стала для меня отличным дополнением. Я обычно не многим книгам даю лестные отзывы, но эта, наверное, будет исключением. Вердикт – must read

Thursday, March 3, 2016

Project Rider. Первые впечатления от работы

Посчастливилось мне недавно поработать с новой IDE от JetBrains для работы с проектами на языке C# под названием Rider, так как JetBrains проводил возможность сделать подписку на их сайт, чтобы иметь возможность использовать предварительную версию для ознакомления, чем я и воспользовался. И вот буквально вчера я получил на почту ссылку на исходники и смог попробовать использовать Rider от первого лица, так сказать.
Давайте рассмотрим, что же собой представляет Rider. Первым делом после установки инсталлера у нас будет предложено выбрать по умолчанию скин для нашей IDE, с которой мы будем работать. Выбор их небольшой, точнее, их всего два.
Не скажу, что это плохо, но первая схема мне бы напомнила работу с Java, как в еще одной среде для разработки от JetBrains IntelliJ IDEA. А так как я не Java разработчик то выбор стал очевиден, и я сразу выбрал себе схему “Dracula”.
На следующем шаге вы можете выбрать, какую keymap scheme использовать для вашых приложений.
Здесь нет ничего особенного, и те, кто работал с Visual Studio, уже видели это окно для решарпера.
И на последнем степе у вас будет окно, в котором вам скажут, что вы счастливый обладатель этой уникальной IDE.
Если честно, то все создание проектов сделано в стиле визарда. То есть, по сути первое окно, которое вы увидите в реальном проекте, вы сможете выбрать существующий проект или создавать новый, а также настроить Version Control, если вам это нужно.
Создадим по классике простое приложение “Hello World”, тем более, если вы нажмете New project, то выбор у вас будет небольшой.
Хотя небольшой я, конечно, загнул, учитывая тот факт, что на выбор у вас будет только пять темлейтов. А если быть вообще честным, то выбор последних двух я вообще не понимаю. Я еще могу понять использовать DNX в ASP.NET Core, но создавать консольные приложения не нахожу особого смысла. 
Мы же пробуем создать простое консольное приложение, поэтому второй пункт нам подходит.
На следующем степе мы можем задать имя нашего проекта, что мы можем увидеть на рисунке выше. Странно, почему JetBrains захотели объединить это с предыдущим шагом для удобства, напрашивается только одно предположение – “Чтобы не было так, как сделано в Visual Studio”.
На следующем окне мы можем увидеть наш созданный проект.
Сам дизайн неплохой. Единственный минус  так это разбираться в корреляции MS Build.
Интересно, чем руководствовались дизайнеры JetBrains в плане удобства выбора.
Если вы работаете с Mono, например, то можете выбрать настройку моно, как показано на рисунке ниже.
А затем в следующем окне указать к нему путь.
Поскольку мы перешли в настройки Райдера, то я вам хочу сказать, что в плане кастомизации его можно настроить, как душе угодно. Например, на скриншоте ниже показано, как можно настроить быстрые клавиши ко всем меню.
Но предлагаю не останавливаться долго на настройке, а перейти в сам редактор, открыть наш файл Program.cs и изменить его следующим образом:
using System.Collections.Generic;
using static System.Console;

namespace ConsoleApplication
{
 
class Program
  {
   
static void Main(string[] args)
    {
      WriteLine(
"Hello Rider");
      ReadKey();
    }
  }
}

Теперь можно запустить наш пример комбинацией клавиш Ctrl + F5 или нажать на кнопку запуска в самом крайнем правом углу. Если честно, то JetBrains в этом плане сделали самый удобный вариант запуска вручную, нежели любой другой, который я когда-то видел. На самом деле было бы намного удобнее вообще спрятать ее с панели, чтобы не мешала. (Это был небольшой сарказм.) Давайте запустим наш проект, чтобы посмотреть на то, что получилось.
Как вы можете заметить, что результат выполнения выводится в свое консольное окно. В Райдере есть свой аналог Watch с Visual Studio. Я не описал одну важную вещь, ради которой эта среда разработки делалась, а именно – то, что, по сути, у вас в среду разработки встроенный Resharper, по аналогии, как это сделала Microsoft и добавила Roslyn как background анализатор с Visual Studio 2015. Но к сожалению, Rider пока не может составить конкуренции VS. Разве что в ближайшем будущем все изменится.
Кстати, достаточно интересно выглядит подсветка синтаксиса в Rider.
Лично мне нравится желтый цвет на сером фоне.
Давайте сразу не отходя от кассы попробуем создать DNX Console App, закрыв при этом текущий проект. Сделаем его по аналогии с предыдущим и выведем тоже какое-то приветствие на экран.
using System.Collections.Generic;
using static System.Console;

namespace DNXConsoleApp1
{
 
public class Program
  {
   
public static void Main(string[] args)
    {
      WriteLine(
"Hello DNX Rider");
      Read();
    }
  }
}
После запуска приложения получим результат на экране.
А затем я решил немного усложнить задачу и попробовать скормить Rider одно из моих ASP.NET Core приложений; и проверить, как с этим справится Rider.
Сначала мне пришлось ждать несколько минут, чтобы он загрузил мой проект. Как видите погрузка проекта выглядит не очень, если честно. Но я так и не смог собрать свой проект в Rider. Затем я решил прост проверить, сколько же памяти съедает Rider, по сравнению с той же студией. Я был очень удивлен.
Rider на нескольких проверенных проектах съедал вдвое больше памяти, чем VS2015. Может сложится впечатление, что я расхваливаю здесь студию, но на самом деле я просто провожу замеры с той средой, которой постоянно пользуюсь.
Скорость загрузки тоже оставляет желать лучшего. Наверное, потому что внутри Rider ядро составляет Java. Не знаю, плохо это или хорошо, но как минимум, на скорости загрузки влияет.
Без багов тоже не обошлось. Ниже на скриншоте представлен один баг из семи отловленных на простых приложениях.
Как видите – Java во всей красе.
Часто во время работы можно увидеть падения ошибки в IDE.
Правда, есть плюс, что сама среда при этом не отваливается.
Еще один дефект порадовал: зависшая иконка загрузки в правом углу.
Так как эта иконка никогда не пропадала, то, наверное, это просто By Design.
Итоги
В итогах постараюсь ответить на ответ одного из разработчиков JetBrains на мой вопрос “Реально не понимаю, почему такое восхищение новой платной IDE, если есть бесплатная альтернатива?”, на что получил ответ “Потому что это же IDE, а не стиральные порошки.”. Так вот, если уже сравнивать IDE, то нужно хотя бы со стиральными машинками сравнивать. Где на одной чаше весов – стиральная машинка, которая работает отлично, имеет кучу плюшек, и которую дают вам бесплатно для собственного использования (VS Community), с другой стороны  такая же машинка с меньшим количеством функций, но которая может работать в другой среде (Linux, Mac OS) но за 1000$ сверху. Так вот, выбор очевиден в пользу машинки №1. 
Если взять сегмент корпоративной разработки и поставить цену меньше, чем та же VS 2015 Professional, которая стоит сейчас 45$ в месяц, а учитывая появления конкурента на рынке, думаю, цена еще упадет. Такова работа рынка. По поводу кросплатформенности, то мне сложно представить ребят, которые сидят на линуксе или маке и пишут программы под .NET. Хотя уже это больше холивар. Поэтому скажу заключающее слово о том, как это вижу я. Sales компании JetBrains продали среду, которой, к сожалению, с ее функционалом пока не с чем конкурировать. Хотя презентовали ее красиво, с этим не прикопаться. Мне нравятся продукты JetBrains, и я считаю, что выпуск Rider только поднимет конкуренцию на рынке ПО, но к сожалению, пока все очень сыро и, по сути, не за что отдавать деньги. Увы, но та же VS Community с Resharper будет уделывать Rider по всем фронтам, при этом я уверен на 90%, что цена будет намного ниже, чем нужно будет заплатить за саму среду разработки Rider

Friday, February 26, 2016

Using filters in MVC and Web API controllers

Сегодня мы погрузимся в мир изучения работы фильтров в ASP.NET MVC 5. Мы рассмотрим виды фильтров, создание своих фильтров и добавление кастомных фильтров для Web API (ApiController). 
Для начала создадим новое ASP.NET Web Application приложение и назовем его UsingFilterSample.
Затем выберем шаблон Web API.
После того, как мы создали наше приложение, вернемся к теории. По умолчанию в ASP.NET MVC 5 выделяют 5 типов фильтров, которые приведены в таблице ниже.
Как видите, единственным фильтром, у которого нет встроенной реализации, является IAuthenticationFilter, для всех остальных есть реализация по умолчанию.
Давайте рассмотрим методы, которые предоставляют нам эти фильтры.
IAuthentificationFilter: OnAuthentication и OnAuthenticationChallenge
IAuthorizationFilter: OnAuthorization
IExceptionFilter: OnException
Возьмем существующие классы для реализации наших фильтров и на каждый из них реализуем свою реализацию, в которую для начала добавим простое логирование. Чтобы приступить к реализации, остановимся детальнее на классе ActionFilterAttribute. Этот класс уже реализует фильтры IResultFilter и IActionFilter. Функция их описана выше в таблице, а теперь давайте рассмотрим на практике их использование. Для этого на каждый тип фильтров мы создадим свой вариант, в котором просто будем логировать наше действие. Добавим новую папку Filters, если такой папки у вас еще нет. Затем будем по порядку добавлять нужные нам фильтры.
IAuthenticationFilter
Первым делом добавим новый класс AuthenticationCustomFilter, в который добавим следующую логику.
public class AuthenticationCustomFilter : FilterAttribute, IAuthenticationFilter
{
    public void OnAuthentication(AuthenticationContext filterContext)
    {
        IPrincipal user = filterContext.HttpContext.User;
        Debug.WriteLine("OnAuthentication: Controller: {2}, Action Name: {0}, User Name: {1}", filterContext.ActionDescriptor.ActionName,
            user.Identity.Name,
            filterContext.ActionDescriptor.ControllerDescriptor.ControllerName);
    }

    public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
    {
           
    }
}
Так как мы тестируем сейчас MVC фильтры, то в каждый добавленный нами класс добавляем следующие namespace:
using System.Web.Mvc;
using System.Web.Mvc.Filters;
В приведенном выше примере мы попросту имплементировали интерфейс IAuthenticationFilter, задачей которого стоит проверка аутентификации пользователя. В нашем же примере, поскольку мы работаем под анонимным пользователем, не используя какой-либо авторизации и аутентификации, там будет всегда пусто.
AuthorizeAttribute
Затем в эту же папку добавим новый класс AuthorizationCustomFilter, задачей которого будет показ того, как работает авторизация. Ниже приведен код этого класса.
public class AuthorizationCustomFilter : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        IPrincipal user = filterContext.HttpContext.User;
        Debug.WriteLine("OnAuthorization: Controller: {2}, Action Name: {0}, User Name: {1}", filterContext.ActionDescriptor.ActionName,
            user.Identity.Name,
            filterContext.ActionDescriptor.ControllerDescriptor.ControllerName);
    }
}
Здесь мы просто логируем пользователя, который к нам залогинился. Зачастую приведенный выше класс AuthorizeAttribute используют без дополнительного написания кода. Например, как показано ниже.
[Authorize(Users = "Joe, Mary", Roles = "Admin, Manager")]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewBag.Title = "Home Page";

        return View();
    }
}
Пример чисто синтетический и не несет особой смысловой нагрузки, поэтому не стоит его копировать.
ActionFilterAttribute
Класс ActionFilterAttribute представляет собой имплементацию интерфейсов IResultFilter и IActionFilter. Для того чтобы посмотреть, как можно использовать фильтры действий и результатов, добавим в папку Filters новый класс, который назовем ActionCustomFilter.
public class ActionCustomFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        Debug.WriteLine("OnActionExecuting: Controller: {0}, Action Name: {1}", filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
            filterContext.ActionDescriptor.ActionName);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        Debug.WriteLine("OnActionExecuted: Controller: {0}, Action Name: {1}", filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
            filterContext.ActionDescriptor.ActionName);
    }

    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        Debug.WriteLine("OnResultExecuting: Controller: {0}, Action Name: {1}", filterContext.RouteData.Values["controller"],
            filterContext.RouteData.Values["action"]);
    }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        Debug.WriteLine("OnResultExecuted: Controller: {0}, Action Name: {1}", filterContext.RouteData.Values["controller"],
            filterContext.RouteData.Values["action"]);
    }
}
Здесь мы тоже ничего особенного не делали кроме логирования.
HandleErrorAttribute
Нам осталось напоследок добавить реализацию интерфейса IExceptionFilter, который реализован в классе HandleErrorAttribute, реализацию которого мы и рассмотрим ниже. Для этого мы в нашу папку Filters добавим новый класс CustomExceptionFilter.
public class CustomExceptionFilter : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        Debug.WriteLine("OnException: Controller: {0}, Action Name: {1}, Message: {2}", filterContext.RouteData.Values["controller"],
            filterContext.RouteData.Values["action"],
            filterContext.Exception.Message);
    }
}
Для того чтобы проверить, как работает наш только что созданный фильтр, нужно в наш HomeController добавить следующий метод:
public ActionResult Home()
{
    throw new Exception("Test Custom exceptions");
}
Configuring Filters in ASP.NET MVC
Существуют разные уровни использования фильтров, и сейчас мы по ним пройдемся.
Action Level
Это самый низкий уровень фильтров, которые можно применить, и применяется он на уровне метода. Ниже представлен пример такого метода.
[ActionCustomFilter]
public ActionResult Index()
{
    ViewBag.Title = "Home Page";

    return View();
}
Controller Level
Этот тип фильтров применяется на уровне контроллера. Отличие от предыдущего заключается в том, что под влияние попадают все методы, которые описаны в контроллере.
[Authorize(Users = "Joe, Mary", Roles = "Admin, Manager")]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewBag.Title = "Home Page";

        return View();
    }
}
Global Level
Регистрация глобальных фильтров, которые действуют на уровне всего проекта. Регистрируются они в Global.asax файле в методе
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
Ниже представлена полная реализация, которая у меня вышла для Global.asax.
public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }
}
Поскольку для теста предлагаю зарегистрировать все наши фильтры глобально, давайте перейдем в метод RegisterGlobalFilters и изменим его следующим образом:
public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new CustomExceptionFilter());
        filters.Add(new ActionCustomFilter());
        filters.Add(new AuthorizationCustomFilter());
        filters.Add(new AuthenticationCustomFilter());
    }
}
Теперь давайте запустим наш проект и проверим, что все работает.
А затем в адресной строке браузера введем Home/Home, чтобы посмотреть на наш фильтр обработки ошибок.

Web API Filters
Давайте немного пробежимся по Web API фильтрам. Судя по названию, это фильтры, которые применимы для Web API контроллеров и методов, описанных в них. Все эти фильтры лежат в пространстве имен System.Web.Http.Filters. Если сравнивать Web API фильтры с фильтрами MVC, то у нас остаются практически те же фильтры с той же областью, как и фильтры за исключением того, что в среде Web API фильтров нет интерфейса IResultFilter; вместо него всю эту роль выполняет IActionFilter.
Теперь рассмотрим, как регистрировать глобальные фильтры для Web API. Регистрируются они в файле Global.asax в методе WebApiConfig.Register. Ниже в примере показан пример добавления глобальных фильтров для web api контроллеров.
public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        // Configure Web API to use only bearer token authentication.
        config.SuppressDefaultHostAuthentication();
        config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
    }
}
Для того чтобы посмотреть, как работает использование фильтров на уровне методов, откроем файл AccountController и найдем там метод GetUserInfo.
// GET api/Account/UserInfo
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("UserInfo")]
public UserInfoViewModel GetUserInfo()
{
    ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);

    return new UserInfoViewModel
    {
        Email = User.Identity.GetUserName(),
        HasRegistered = externalLogin == null,
        LoginProvider = externalLogin != null ? externalLogin.LoginProvider : null
    };
}
В этом же классе мы сможем увидеть, как работают фильтры на уровне контроллера.
[Authorize]
[RoutePrefix("api/Account")]
public class AccountController : ApiController
{
Чтобы более продуктивно подойти к изучению фильтров для ApiController, рассмотрим более практическое применение этих фильтров. Для этого мы в нашу папку Filters добавим новый класс ValidateModelAttribute, реализация которого приведена ниже.
using System.Net;
using System.Net.Http;
using System.Web.Http.Filters;
using System.Web.Http.Controllers;

namespace UsingFilterSample.Filters
{
    public class ValidateModelAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (actionContext.ModelState.IsValid == false)
            {
                actionContext.Response = actionContext.Request.CreateErrorResponse(
                    HttpStatusCode.BadRequest, actionContext.ModelState);
            }
        }
    }
}
Этот фильтр позволит добавить валидацию для ваших моделей данных. Как видите, достаточно неплохое применение ActionFilterAttribute. Теперь разберемся, как можно использовать кастомную обработку ошибок.  Для этого добавим новый класс DatabaseExceptionFilterAttribute в папку Filters. Ниже представлен код его реализации.
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Validation;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web.Http.Filters;

namespace UsingFilterSample.Filters
{
    public class DatabaseExceptionFilterAttribute : ExceptionFilterAttribute
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            var e = context.Exception;
            HttpResponseMessage message = null;
            if (e is DbUpdateException)
            {
                var ue = e as DbUpdateException;
                var errorMessage = e.InnerException?.InnerException?.Message ?? ue.InnerException?.Message ?? ue.Message;
                message = context.Request.CreateErrorResponse(HttpStatusCode.BadRequest, errorMessage);
            }
            else if (e is DbEntityValidationException)
            {
                var ev = e as DbEntityValidationException;
                var sb = new StringBuilder();
                foreach (var validationError in
                    ev.EntityValidationErrors.SelectMany(entityValidationError => entityValidationError.ValidationErrors)
                    )
                {
                    sb.AppendLine(string.Format("Property:{0}, ErrorMesage:{1}", validationError.PropertyName, validationError.ErrorMessage));
                }
                message = context.Request.CreateErrorResponse(HttpStatusCode.BadRequest, sb.ToString());
            }
            else
            {
                base.OnException(context);
                return;
            }

            context.Response = message;

            base.OnException(context);
        }
    }
}
Польза данного класса состоит в том, что мы можем расширить ответ, который присылаем клиенту, и клиент может понять, почему ему не удалось сохранить введенные им данные в базу данных. Ниже приведен кусок кода, как это может быть отражено на клиенте через вспомогательные окна или логирование с помощью AngularJS.
$scope.save = function () {
    $http.post('api/Clients', $scope.client).success(function () {
        console.log("Client saved succesfully");
    }).error(function (result) {
        $scope.errors = {};
        errorHelper.validateForms($scope.clientForm, $scope.errors, result.modelState, toastr);
        toastr.error(result.message);
    });
};
Если у нас произошла какая-то ошибка, то мы ее отображаем через message без необходимости проверять тип ошибки и получать необходимое свойство. Например, для DbUpdateException нам пришлось бы детальную ошибку смотреть в InnerException на клиенте, а так фильтры нам помогли упростить нашу задачу.
После проделанной работы давайте зарегистрируем наши фильтры глобально в WebApiConfig.Register, как показано ниже.
config.Filters.Add(new ValidateModelAttribute());
config.Filters.Add(new DatabaseExceptionFilterAttribute());
Полную реализацию можно увидеть ниже.
public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        // Configure Web API to use only bearer token authentication.
        config.SuppressDefaultHostAuthentication();
        config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
        config.Filters.Add(new ValidateModelAttribute());
        config.Filters.Add(new DatabaseExceptionFilterAttribute());

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

Итоги
Надеюсь, небольшая справка по использованию фильтров в ваших ASP.NET приложениях будет полезна для работы, и вы сможете разобраться, как применять те или иные фильтры, в зависимости от ситуации.

Исходники: Filters Sample