AngularJS + PHP. Заставляем $http-сервис веcти себя как jQuery.ajax()

от автора

Новички в Ангуляре часто путаются из-за того, что быстрые функции $http-сервиса (напр., $http.post()) не взаимозаменяемы с эквивалентными функциями Джиквери (напр., jQuery.post()), не смотря на то, что соответствующие руководства описывают их использование схожим образом. То есть, если код в Джиквери до этого имел вид:

(function($) {   jQuery.post('/endpoint', { foo: 'bar' }).success(function(response)   {     // ...   }); })(jQuery); 

То можно обнаружить, что следующий точно такой же не работает из коробки в Ангуляре:

var MainCtrl = function($scope, $http) {   $http.post('/endpoint', { foo: 'bar' }).success(function(response)   {     // ...   }); }; 

Проблема, с которой можно столкнуться, в том, что сервер не может получить параметры { foo: 'bar' } из запроса Ангуляра.

Различие в том, как Джиквери и Ангуляр сериализуют и передают данные. В основном, проблема заключается в языке, на котором написана серверная часть и который просто не понимает передачу Ангуляра с настройками по-умолчанию — чертовски обидно, потому что Ангуляр, конечно, не делает ничего плохого. По умолчанию, Джиквери передает данные с использованием Content-Type: x-www-form-urlencoded и привычной строки foo=bar&baz=moe. Ангуляр, однако, передает данные с использованием Content-Type: application/json и { "foo": "bar", "baz": "moe" } строки JSON, которую, к сожалению, некоторые серверные языки — особенно РНР — изначально не преобразуют в объект.

К счастью, разработчики Ангуляра позаботились о поддержке такого метода $http-сервисом, чтобы установить x-www-form-urlencoded на всех наших передач. Существует множество решений, предлагаемых на форумах и StackOverflow, но они не идеальны, поскольку требуют либо изменить код сервера, либо схему использования $http. Поэтому представляю вам наилучшее возможное решение, которое не требует изменений ни серверного, ни клиентского кода, а предлагает внести некоторые незначительные изменения в работу $http в настройках модуля приложения:

// Корневой модуль приложения ... angular.module('MyModule', [], function($httpProvider) {   // Используем x-www-form-urlencoded Content-Type   $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';     // Переопределяем дефолтный transformRequest в $http-сервисе   $httpProvider.defaults.transformRequest = [function(data)   {     /**      * рабочая лошадка; преобразует объект в x-www-form-urlencoded строку.      * @param {Object} obj      * @return {String}      */      var param = function(obj)     {       var query = '';       var name, value, fullSubName, subValue, innerObj, i;              for(name in obj)       {         value = obj[name];                  if(value instanceof Array)         {           for(i=0; i<value.length; ++i)           {             subValue = value[i];             fullSubName = name + '[' + i + ']';             innerObj = {};             innerObj[fullSubName] = subValue;             query += param(innerObj) + '&';           }         }         else if(value instanceof Object)         {           for(subName in value)           {             subValue = value[subName];             fullSubName = name + '[' + subName + ']';             innerObj = {};             innerObj[fullSubName] = subValue;             query += param(innerObj) + '&';           }         }         else if(value !== undefined && value !== null)         {           query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';         }       }              return query.length ? query.substr(0, query.length - 1) : query;     };          return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;   }]; }); 

Примечание: Большой фрагмент выше необходимо всегда использовать с указанием функции param(). НЕ используйте вместо этого jQuery.param(); что вызовет хаос, когда попытаетесь воспользоваться $resource Ангуляра, поскольку jQuery.param() будет отклонять каждый, переданный ему, метод класса $resource! (Это особенность JQuery из-за которой свойства объекта для параметризации, содержащие функции, вызываются и возвращаемое ими значение используется в качестве параметризованного, но в случае с Ангуляром это может быть губительно, так как мы обычно передаем «реальные» экземпляры объектов с методами и т.д.)

Просто используйте приведенный выше фрагмент и всё будет в порядке!

Теперь можно двигаться дальше, используя $http.post() и другие подобные методы, предполагая, что существующий серверный код ожидает x-www-form-urlencoded данные. Вот несколько примеров конечного результата для обыденного, законченного кода (пример того, на что вы надеялись и мечтали):

HTML-шаблон

<div ng-app="MyModule" ng-controller="MainCtrl">   <p ng-show="loading">Loading...</p>   <p ng-hide="loading">Response: {{response}}</p> </div> 

Клиентский код (AngularJS)

var MainCtrl = function($scope, $http) {   $scope.loading = true;   $http.post('/endpoint', { foo: 'bar' }).success(function(response)   {     $scope.response = response;     $scope.loading = false;   }); }; 

Серверный код (PHP)

< ? header('Content-Type: application/json');   // Та-дам! $_POST теперь нормальный; PHP без проблем // преобразует запросы Ангуляра в объекты echo json_encode($_POST); ?> 

Другие примечания
Напрашивается вопрос, можно ли всё-таки в PHP прочитать JSON-запрос Ангуляра? Ну, конечно, если читать входной поток PHP и декодировать JSON:

< ? $params = json_decode(file_get_contents('php://input'));  // или так, если нужно преобразовать в PHP массив (прим. переводчика) $params = json_decode(trim(file_get_contents('php://input')), true); ?> 

Очевидный недостаток в том, что код немного менее интуитивен (мы привыкли к $_POST, в конце концов), и если серверные обработчики уже написаны с использованием $_POST, то придется переписывать серверный код. Если используется хороший фреймворк, вероятно, можно осуществить глобальные изменения, так, чтобы обработчик входа прозрачно определял JSON запросы, но я отвлекся.

ссылка на оригинал статьи http://habrahabr.ru/post/181009/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *