В соответствии со Сводом правил по REST API Марка Массе, есть четыре архетипа ресурса:
- Документ — представление ресурса.
- Коллекция — набор данных на сервере.
- Хранилище — набор данных на клиенте.
- Контроллер — выполняемое действие.
Документ, Коллекция и Хранилище представляют ресурсы. Контроллеры, с другой стороны, отвечают за изменение ресурсов. Можно использовать глаголы в HTTP для обозначения действия над ресурсом, но для заурядных CRUD-операций это не всегда имеет смысл. Возьмем в качестве примера следующие ресурсы:
- /messages
- /messages/4
Имея Коллекцию сообщений (первый ресурс) и отдельное сообщение (второй ресурса), как сообщить об очистке всех сообщений? Или, как переместить данное сообщение в архив? CRUD не вполне отвечает таким требованиям. Но контроллеры отлично подходят для операций подобного типа:
- /messages/clear-all
- /messages/4/archive
Здесь мы используем Контроллеры «clear-all» и «archive» для изменения Коллекции и представления Документа, соответственно.
Начав использовать контроллеры для наших ресурсов, мы пришли к схеме URL, имеющей большую вариативность. $resource из Ангуляра позволяет так делать, но придется использовать несколько «умных» особенностей чтобы настроить связывание данных.
Исходя из приведенной выше концепции работы с сообщениями, в контексте Ангуляра мы должны были бы определить наш ресурс, используя следующий шаблон:
- /messages/:id/:controller
Проблема в том, что, в настоящее время, возникает небольшая неоднозначность вокруг первого URL-параметра. Является ли «:id» ссылкой на, основывающийся на Коллекции, Контроллер? Или это ссылка ID Документа?
Чтобы устранить эту неопределенность, Ангуляр позволяет определить несколько параметров в одной и той же части URL шаблона:
- /messages/:listController:id/:docController
Обратите внимание, что вторая часть шаблона URL содержит два параметра:
- :listController
- :id
Пока использую только один из них, в то время, как Ангуляр построит RESTful-ресурс должным образом. Чтобы продемонстрировать это, написал демку, в которой определяется, а затем задействуется ресурс сообщения как было показано выше:
<!doctype html> <html ng-app="Demo"> <head> <meta charset="utf-8" /> <title> Использование RESTful контроллеров для ресурсов AngularJS</title> <!-- Т. к. работает с ресурсами, то должны загрузить и AngularJS и ngResource модули. --> <script type="text/javascript" src="../angular-1.0.2/angular.js"></script> <script type="text/javascript" src="../angular-1.0.2/angular-resource.js"></script> <script type="text/javascript"> // Говорим Ангуляру загрузить ngResource перед загрузкой // основного модуля приложения. var app = angular.module( "Demo", [ "ngResource" ] ); // Запускаем, когда app готово. app.run( function( $resource ) { // При определении ресурса, мы получаем несколько действий // из коробки, таких как как get() и query(), основанных на // стандартных командах HTTP. Но можем также использовать RESTful // контроллер для изменения состояния ресурса с помощью // действия, которое выходит за рамки обычных операций CRUD. var messages = $resource( "./api.cfm/messages/:listController:id/:docController", { id: "@id", listController: "@listController", docController: "@docController" }, { clear: { method: "POST", params: { listController: "clear-all" } }, archive: { method: "POST", params: { docController: "archive" } } } ); // Теперь наш ресурс определен, давайте вызывать // его с различными настройками. // GET без ID. messages.query(); // POST с контроллером списка. messages.clear(); // GET с ID. messages.get( { id: 4 } ); // POST с контроллером документа. messages.archive( { id: 8 } ); } ); </script> </head> <body> <!-- Намеренно оставлена пустой. --> </body> </html>
Обратите внимание, что мой ангуляровский ресурс рассматривается двумя различными контроллерами:
- :ListController – действует на коллекцию сообщений.
- :DocController – действует на конкретное сообщение.
Когда выше задействовались методы ресурса, то, в конечном итоге, было сделано четыре запроса по следующим адресам:
- GET …/api.cfm/messages
- POST …/api.cfm/messages/clear-all
- GET …/api.cfm/messages/4
- POST …/api.cfm/messages/8/archive
Как можно увидеть Ангуляр правильно определил URL-адреса в соответствии с REST. Кайф!
Если интересно, вот файл API теста:
<!--- Получение сырого ресурсного пути, который был запрошен. ---> <cfset resourcePath = cgi.path_info /> <!--- ПРИМЕЧАНИЕ: Кажется, что ColdFusion 10 подвис, пока я не сделал запрос для получения тела POST. Не знаю, почему. ---> <cfif ( cgi.request_method neq "GET" )> <cfset requestBody = getHTTPRequestData().content /> </cfif> <!--- Определяем тип запроса, который пришел, на основе шаблона запрошенного ресурсного пути. ---> <cfif reFind( "^/messages$", resourcePath )> <cfset response = "GET without ID." /> <cfelseif reFind( "^/messages/clear-all$", resourcePath )> <cfset response = "POST with clear-all Controller." /> <cfelseif reFind( "^/messages/\d+$", resourcePath )> <cfset response = "GET with ID." /> <cfelseif reFind( "^/messages/\d+/archive+$", resourcePath )> <cfset response = "POST with archive controller" /> <cfelse> <cfset response = "Hmm, couldn't match resource." /> </cfif> <!--- Преобразуем ответ в JSON. Ангуляр будет знать, как его распарсить. ---> <cfset serializedResponse = serializeJSON( response ) /> <!--- Добавляем заголовок для отладки пути. ---> <cfheader name="X-Debug-Path" value="#cgi.path_info#" /> <!--- Возврат потока ответов обратно к клиенту. ---> <cfcontent type="application/json" variable="#charsetDecode( serializedResponse, 'utf-8' )#" />
По некоторым причинам, ColdFusion 10 зависал при POST запросах, пока я не получал доступ к телу запроса (через функцию getHttpRequestData()). Такое происходило только, если тело запроса содержало JSON (JavaScript Object Notation) данные. Когда оно было пустым, запрос не вис. Не разбираюсь досконально в обработке запросов и не знаю, имеет ли это смысл, однако, замечу, что ColdFusion 9 такого не вытворяет.
ссылка на оригинал статьи http://habrahabr.ru/post/180941/
Добавить комментарий