http://metanit.com/sharp/mvc/
http://professorweb.ru/my/ASP_NET/mvc/level8/8_2.php
Оригинал
http://regfordev.blogspot.ru/2012/06/rest.html#.VYkzlvntlBc
REST сервисы и ASP.NET Web API
Самым актуальный способом создать REST сервис в стеке технологий Майкрософт на сегодняшний день является ASP.NET Web API. До того эта технология значилась как WCF Web API и больше по названию тяготела к WCF. Но уже тогда там использовались сходные походы как в ASP.NET MVC, включая роутинг (routing). До нее существовали такие вещи как WCF 4 REST, WCF REST Starter Kit 3.5. Их все еще можно встретить на старых проектах и stackoverflow пестрит вопросами о них. Но то что ASP.NET Web API используется на новых проектах, а некоторые старые конвертируются, чтобы его использовать – радует. Так как предшественники были хуже как в плане технологии (приходилось писать много boilerplating code), удобства использования так и документации.
В предыдущих постах были рассмотрены некоторые теоретические аспекты REST – теперь создадим простой REST сервис с помощью Web API и рассмотрим ключевые элементы такого сервиса.
Начать стоит с подключения NuGet packages (и/или установки ASP.NET MVC):
- Web API, в случае если хостимся в ASP.NET: AspNetWebApi
- Self-hosted Web API: AspNetWebApi.Selfhost
- HttpClient включая XML и JSON форматеры: System.Net.Http.Formatting
- JsonValue для навигации и манипуляции JSON: System.Json
Перед тем как создавать такой сервис необходимо также понимать что использовать Web API стоит если есть тесная связка с веб-клиентом, если сервис содержит логику выходящую за рамки CRUD операций. Если же у вас сервис по сути своей поставщик данных, т.е. операции в основном CRUD, то лучше использовать WCF Data Services, так как там много вещей из коробки генерится под базу - и CRUD операции и нормальная поддержка OData и IQuerable (в ASP.NET Web API она ограничена), которые позволяют делать запросы к сервису и данным с помощью Uri и специального OData синтаксиса.
Итак преступим. Для начала создадим новый проект ASP.NET MVC4:
Изображение 1
Естественно темплейт (шаблон) для MVC 4 нагенерит нам типичную структуру ASP.NET MVC проекта (файл ValuesController я уже успел переименовать на DocumentsController). Отличие в дополнительном контроллере для Web API. По умолчанию это ValuesController, естественно его необходимо переименовать.
Изображение 2
В нашем случае он стал DocumentsController. Из темплейта этот контроллер содержит операции заглушки для Get, Post, Put, Delete. В просто случае переопределим эти операции для DocumentsController и ресурса Document. Получится вот такой вот контроллер:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
| public class DocumentsController : ApiController { private IDocumentRepository documentRepository; public DocumentsController() { documentRepository = new DocumentRepository(); } public IEnumerable<Document> Get() { return documentRepository.GetDocuments(); } public Document Get( string id) { try { return documentRepository.GetDocument(id); } catch (DocumentNotFoundException) { throw new HttpResponseException( new HttpResponseMessage(HttpStatusCode.NotFound)); } } public void Post(Document document) { try { documentRepository.CreateDocument(document); } catch (DocumentNotFoundException) { throw new HttpResponseException( new HttpResponseMessage(HttpStatusCode.NotFound)); } } public void Put(Document document) { try { documentRepository.UpdateDocument(document); } catch (DocumentNotFoundException) { throw new HttpResponseException( new HttpResponseMessage(HttpStatusCode.NotFound)); } } public void Delete(Document document) { documentRepository.DeleteDocument(document); } } |
Это простой вариант, и здесь не используются фильтры для обработки сообщений или dependency resolvers. В свою очередь IDocumentRepository реализовано как простая заглушка и если дальше развивать тему с нормальным доступом к данным то реализацию можно подставить любую.
Теперь проверим операции. Это сделать можно используя Fiddler и правильно сформировав запрос. Например операция получения всех документов, используем адрес http://127.0.0.1:81/api/documents/. Используется стандартный роутинг из коробки:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute( "{resource}.axd/{*pathInfo}" ); routes.MapHttpRoute( name: "DefaultApi" , routeTemplate: "api/{controller}/{id}" , defaults: new { id = RouteParameter.Optional } ); routes.MapRoute( name: "Default" , url: "{controller}/{action}/{id}" , defaults: new { controller = "Home" , action = "Index" , id = UrlParameter.Optional } ); } |
Итак, запрос на http://127.0.0.1:81/api/documents/ должен вызвать метод IEnumerable<Document> Get() :
Так и есть, нам вернулся список в виде XML из двух элементов. Теперь попробуем content negotiation из коробки в действии. К тому же самому вызову добавим HTTP заголовок – Accept:application/json. Итак запрос:
Ответ ожидаем в Json:
Из коробки идут два стандартных форматера – XML и Json, но есть возможность добавлять свои.
Аналогичным образом будут работать остальные операции. Единственное попробуем еще запросить документ с недействительным идентификатором. Будем вызывать метод Document Get(string id) по адресуhttp://127.0.0.1:81/api/documents/9505a3b549b54881b3ed83fc19510534, где 9505a3b549b54881b3ed83fc19510534 – недействительный идентификатор, изменили последнюю цифру.
Ожидается ответ 404 NotFound. Результат запроса:
Вот таким вот образом можно создать и протестировать на работоспособность простенький REST сервис на базе ASP.NET Web API.
Основные концепты - ApiController
Так как мы имеем дело с REST сервисом. То из всего этого добра нас интересуют на начальном этапе контроллеры и роутинг. Контроллеры для Web API REST сервиса наследуются от от класса ApiController, который в свою очередь от интерфейса IHttpController. И ApiControllerнесет с собой много добра, кроме конечно того что она автоматом распознается и выполняется. Из всего этого добра самое интересное являются свойства Request и Configuration.Изображение 3
Основные концепты – Routing (Роутинг)
При вызове операций с контроллера важный момент играет routing. Именно routing позволяет подсистеме WebApi связать Uri адрес и конкретную операцию из контроллера. Причем есть несколько вариантов - либо операция-action помечается атрибутом, либо используется договоренность именовать операции с префиксом – Http Verb. Например, в методе PostDocument – именно префикс Post позволяет сказать Web Api что эта операция связанна с Uri и вызывается по соответствующему адресу с HTTP Verb – POST.Еще одним вариантом для того, чтобы помочь выделить среди методов контроллера операции, которые связанны с URL – использование атрибутов - HttpGet, HttpPut, HttpPost, или HttpDelete, каждый из них соответствует такому же HTTP Verb – GET, PUT, POST, DELETE. Для того, чтобы навесить на операцию больше чем один HTTP Verb, или операцию отличную от 4 базовых (GET, PUT, POST, DELETE), используется атрибут – AcceptVerbs. Использование атрибутов также дает возможность отказаться от конвенции именования методов, когда префиксом выступает HTTP Verb.
Изображение 4
А для того чтобы избежать мапинга (mapping) метода как action используется атрибут NonAction без параметров.
Есть еще способ роутинга, когда каждый мапинг делается по средством атрибутов на метод, а не глобальным роутингом через Global.asax.cs, но о нем позже, так как он не стандартный. Хотя на этапе WCF Web API использовался именно он.
Routing по-умолчанию в Web API устанавливается как в методе RegisterRoutes на изображении 5 ниже. При использовании такого routing необходимо придерживаться конвенции именования методов в контроллере, когда каждый метод начинается с HTTP Verb префикса.
Ну и естественно важная часть маппинга – routing в Global.asax.cs:
Изображение 5
Соответственно под роутинг "api/{controller}/{id}" подпадают URLs и примерные имена методов:
/api/contacts, GetAllContacts, PostNewContact /api/contacts/1, GetContact, PutUpdatedContact, DeleteContactМожно также сделать роутинг по имени action. Он не создается по-умолчанию темплейтом проекта. Например:
routes.MapHttpRoute( name: "ActionApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } );В случае с таким роутингом необходимо использовать атрибуты HttpGet, HttpPut, HttpPost, HttpDelete или AcceptVerbs чтобы указать на какие методы мапить {action}. В WCF WebAPI использовался роутинг с помощью атрибутов, его тоже можно прикрутить, но об этом отдельно.
Основные концепты - HttpResponseMessage, HttpRequestMessage
По сути это два спец класса которые используются достаточно часто. Они нужны для того чтобы иметь возможность оперировать запросом и ответом. HttpRequestMessage можно получить через свойство Request от ApiController (Изображение 6). Tак как Web API контроллеры всегда наследуются от ApiController, то его можно получить в середине любого из наших контроллеров. HttpRequestMessage позволяет нам управлять запросом, например извлекать из него данные из HTTP Body либо HTTP Headers которые нам нужны.Изображение 6
HttpResponseMessage можно создать, чтобы вернуть результат, либо просто Response код (Изображение 7), либо еще и с нагрузкой, запаковав в его свойство Content, нужный нам HttpContent, например для бинарных данных подойдет наследник от HttpContent – StreamContent. Из свойства Request можно вычитать бинарные данные документа, который пришел с клиента:
Изображение 7
Возврат ошибок - HttpResponseException
Вернуть ответ с ошибкой можно как с помощью HttpResponseMessage, указав код ошибки, так и с помощью специального класса HttpResponseException. Например, на изображении 7 на клиент возвращается ошибка InternalServerError = 500 с коротким описанием. Описание умеют читать далеко не все клиенты или клиентские библиотеки (были проблемы с iPad), в таком случае в тело сообщения с ошибкой можно писать объект более детально описывающий проблему, например свой кастомный объект с сообщением и кодом ошибки.Хостинг
Само собой разумеется, что Web API REST сервис может хоститься на IIS либо вместе с ASP.NET MVC клиентом либо раздельно. Также его можно легко захостить вместе с ASP.NET MVC Web Role в облаке на Windows Azure. Но интересно, что Web API также можно хостить у себя в приложении, в памяти. Это значительно расширяет круг сценариев, в которых Web API может использоваться. Например с self-hosted Web API можно легко делать интеграционные тесты, которые поднимут во время тестирования self-hosted Web API сервис.Например, на изображение 8 ниже, показано как поднимается с self-hosted Web API сервис для интеграционного теста в методе BecauseOf.
Изображение 8
Клиент
Клиентов к Web API REST может быть большое множество – есть куча библиотек под разные платформы для REST, можно обращаться к REST сервису c веб страницы по средством JavaScript и jQuery, можно использовать “старенький” класс WebClient для десктоп клиента. Вместе с Web API новым для .NET является также новый HttpClient, который очень легко использовать с десктоп клиента или тестового сценария (пример на изображении 8 метод should_make_tivial_get), и к тому же он изначально спроектирован асинхронным.
Выводы
ASP.NET Web API свежая и отличная технология для создания REST сервисов от Microsoft. До нее создавать сервисы было сложнее и требовалось больше boilerplating code. Среди интересных ее фишек – удобный content negotiation, возможность создавать self-hosted REST services, схожесть и хорошая интеграция с ASP.NET MVC. Основным концептами ASP.NET Web API являются широкие возможности хостинга (IIS в том числе на IaaS cloud services, self hosted, Windows Azure), роутинг, обработка request,response и ошибок, хорошая тестируемость как юнит тестами так и интеграционными тестами, content negotiation с удобным механизмом форматеров.
Ссылки по теме
http://www.asp.net/web-apiКуча материалов для старта. Видео туториалы и статьи.
http://webapibloggers.com/
Агрегатор блогов по Web API
No comments:
Post a Comment