From a8f7e9cfde82ed7eaba3a868d8acafdf57f2d76f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rouven=20We=C3=9Fling?= Date: Mon, 15 Jun 2015 00:14:20 +0200 Subject: [PATCH] feat($httpProvider): add 'useLegacyPromiseExtensions' configuration The legacy methods, `success` and `error`, have been deprecated. Set this to `false` to cause `$http` to throw an error if these methods are used in the application. For now it defaults to `true`. In a future release we will remove these methods altogether. DEPRECATION NOTICE: The legacy methods 'success' and 'error' on promises returned by $http are now deprecated. Closes #12112 Closes #10508 --- docs/content/error/$http/legacy.ngdoc | 45 +++++++++ src/ng/http.js | 130 ++++++++++++++++---------- test/ng/httpSpec.js | 30 ++++++ 3 files changed, 155 insertions(+), 50 deletions(-) create mode 100644 docs/content/error/$http/legacy.ngdoc diff --git a/docs/content/error/$http/legacy.ngdoc b/docs/content/error/$http/legacy.ngdoc new file mode 100644 index 000000000000..1ff4282fc607 --- /dev/null +++ b/docs/content/error/$http/legacy.ngdoc @@ -0,0 +1,45 @@ +@ngdoc error +@name $http:legacy +@fullName The `success` and `error` methods on the promise returned from `$http` have been disabled. +@description + +This error occurs when the legacy promise extensions (`success` and `error`) +{@link $httpProvider#useLegacyPromiseExtensions legacy `$http` promise extensions} have been disabled. + +To resolve this error, either turn on the legacy extensions by adding +`$httpProvider.useLegacyPromiseExtensions(true);` to your application's configuration; or refactor you +use of `$http` to use `.then()` rather than `.success()` and `.error()`. + +For example if you code looked like this: + +```js +// Simple GET request example : +$http.get('/someUrl'). + success(function(data, status, headers, config) { + // This callback will be called asynchronously + // when the response is available + }). + error(function(data, status, headers, config) { + // called asynchronously if an error occurs + // or server returns response with an error status. + }); +``` + +then you would change it to look like: + +```js +// Simple GET request example : +$http.get('/someUrl'). + then(function(response) { + // (The response object contains the data, status, headers and config properties) + // This callback will be called asynchronously + // when the response is available. + }, function(response) { + // called asynchronously if an error occurs + // or server returns response with an error status. + }); +``` + +For more information, see the +{@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} +documentation. diff --git a/src/ng/http.js b/src/ng/http.js index 8ef571290655..456d42d875b1 100644 --- a/src/ng/http.js +++ b/src/ng/http.js @@ -8,6 +8,12 @@ var JSON_ENDS = { '{': /}$/ }; var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/; +var $httpMinErr = minErr('$http'); +var $httpMinErrLegacyFn = function(method) { + return function() { + throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method); + }; +}; function serializeValue(v) { if (isObject(v)) { @@ -330,6 +336,30 @@ function $HttpProvider() { return useApplyAsync; }; + var useLegacyPromse = true; + /** + * @ngdoc method + * @name $httpProvider#useLegacyPromiseExtensions + * @description + * + * Configure `$http` service to return promises without the shorthand methods `success` and `error`. + * This should be used to make sure that applications work without these methods. + * + * Defaults to false. If no value is specified, returns the current configured value. + * + * @param {boolean=} value If true, `$http` will return a normal promise without the `success` and `error` methods. + * + * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining. + * otherwise, returns the current configured value. + **/ + this.useLegacyPromiseExtensions = function(value) { + if (isDefined(value)) { + useLegacyPromse = !!value; + return this; + } + return useLegacyPromse; + }; + /** * @ngdoc property * @name $httpProvider#interceptors @@ -396,17 +426,15 @@ function $HttpProvider() { * * ## General usage * The `$http` service is a function which takes a single argument — a configuration object — - * that is used to generate an HTTP request and returns a {@link ng.$q promise} - * with two $http specific methods: `success` and `error`. + * that is used to generate an HTTP request and returns a {@link ng.$q promise}. * * ```js * // Simple GET request example : * $http.get('/someUrl'). - * success(function(data, status, headers, config) { + * then(function(response) { * // this callback will be called asynchronously * // when the response is available - * }). - * error(function(data, status, headers, config) { + * }, function(response) { * // called asynchronously if an error occurs * // or server returns response with an error status. * }); @@ -415,21 +443,23 @@ function $HttpProvider() { * ```js * // Simple POST request example (passing data) : * $http.post('/someUrl', {msg:'hello word!'}). - * success(function(data, status, headers, config) { + * then(function(response) { * // this callback will be called asynchronously * // when the response is available - * }). - * error(function(data, status, headers, config) { + * }, function(response) { * // called asynchronously if an error occurs * // or server returns response with an error status. * }); * ``` * + * The response object has these properties: * - * Since the returned value of calling the $http function is a `promise`, you can also use - * the `then` method to register callbacks, and these callbacks will receive a single argument – - * an object representing the response. See the API signature and type info below for more - * details. + * - **data** – `{string|Object}` – The response body transformed with the transform + * functions. + * - **status** – `{number}` – HTTP status code of the response. + * - **headers** – `{function([headerName])}` – Header getter function. + * - **config** – `{Object}` – The configuration object that was used to generate the request. + * - **statusText** – `{string}` – HTTP status text of the response. * * A response status code between 200 and 299 is considered a success status and * will result in the success callback being called. Note that if the response is a redirect, @@ -453,8 +483,8 @@ function $HttpProvider() { * request data must be passed in for POST/PUT requests. * * ```js - * $http.get('/someUrl').success(successCallback); - * $http.post('/someUrl', data).success(successCallback); + * $http.get('/someUrl').then(successCallback); + * $http.post('/someUrl', data).then(successCallback); * ``` * * Complete list of shortcut methods: @@ -468,6 +498,14 @@ function $HttpProvider() { * - {@link ng.$http#patch $http.patch} * * + * ## Deprecation Notice + *
+ * The `$http` legacy promise methods `success` and `error` have been deprecated. + * Use the standard `then` method instead. + * If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to + * `false` then these methods will throw {@link $http:legacy `$http/legacy`} error. + *
+ * * ## Setting HTTP Headers * * The $http service will automatically add certain HTTP headers to all requests. These defaults @@ -511,7 +549,7 @@ function $HttpProvider() { * data: { test: 'test' } * } * - * $http(req).success(function(){...}).error(function(){...}); + * $http(req).then(function(){...}, function(){...}); * ``` * * ## Transforming Requests and Responses @@ -743,7 +781,6 @@ function $HttpProvider() { * In order to prevent collisions in environments where multiple Angular apps share the * same domain or subdomain, we recommend that each application uses unique cookie name. * - * * @param {object} config Object describing the request to be made and how it should be * processed. The object has following properties: * @@ -788,20 +825,9 @@ function $HttpProvider() { * - **responseType** - `{string}` - see * [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype). * - * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the - * standard `then` method and two http specific methods: `success` and `error`. The `then` - * method takes two arguments a success and an error callback which will be called with a - * response object. The `success` and `error` methods take a single argument - a function that - * will be called when the request succeeds or fails respectively. The arguments passed into - * these functions are destructured representation of the response object passed into the - * `then` method. The response object has these properties: + * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object + * when the request succeeds or fails. * - * - **data** – `{string|Object}` – The response body transformed with the transform - * functions. - * - **status** – `{number}` – HTTP status code of the response. - * - **headers** – `{function([headerName])}` – Header getter function. - * - **config** – `{Object}` – The configuration object that was used to generate the request. - * - **statusText** – `{string}` – HTTP status text of the response. * * @property {Array.} pendingRequests Array of config objects for currently pending * requests. This is primarily meant to be used for debugging purposes. @@ -843,13 +869,12 @@ function $HttpProvider() { $scope.response = null; $http({method: $scope.method, url: $scope.url, cache: $templateCache}). - success(function(data, status) { - $scope.status = status; - $scope.data = data; - }). - error(function(data, status) { - $scope.data = data || "Request failed"; - $scope.status = status; + then(function(response) { + $scope.status = response.status; + $scope.data = response.data; + }, function(response) { + $scope.data = response.data || "Request failed"; + $scope.status = response.status; }); }; @@ -954,23 +979,28 @@ function $HttpProvider() { promise = promise.then(thenFn, rejectFn); } - promise.success = function(fn) { - assertArgFn(fn, 'fn'); + if (useLegacyPromse) { + promise.success = function(fn) { + assertArgFn(fn, 'fn'); - promise.then(function(response) { - fn(response.data, response.status, response.headers, config); - }); - return promise; - }; + promise.then(function(response) { + fn(response.data, response.status, response.headers, config); + }); + return promise; + }; - promise.error = function(fn) { - assertArgFn(fn, 'fn'); + promise.error = function(fn) { + assertArgFn(fn, 'fn'); - promise.then(null, function(response) { - fn(response.data, response.status, response.headers, config); - }); - return promise; - }; + promise.then(null, function(response) { + fn(response.data, response.status, response.headers, config); + }); + return promise; + }; + } else { + promise.success = $httpMinErrLegacyFn('success'); + promise.error = $httpMinErrLegacyFn('error'); + } return promise; diff --git a/test/ng/httpSpec.js b/test/ng/httpSpec.js index 99f6d17b74c2..70e0d71cbae3 100644 --- a/test/ng/httpSpec.js +++ b/test/ng/httpSpec.js @@ -1975,6 +1975,36 @@ describe('$http with $applyAsync', function() { }); }); +describe('$http without useLegacyPromiseExtensions', function() { + var $httpBackend, $http; + beforeEach(module(function($httpProvider) { + $httpProvider.useLegacyPromiseExtensions(false); + }, provideLog)); + + beforeEach(inject(['$httpBackend', '$http', '$rootScope', function($hb, $h, $rs) { + $httpBackend = $hb; + $http = $h; + }])); + + it('should throw when the success or error methods are called if useLegacyPromiseExtensions is false', function() { + $httpBackend.expect('GET', '/url').respond(''); + var promise = $http({url: '/url'}); + + function callSucess() { + promise.success(); + } + + function callError() { + promise.error(); + } + + expect(callSucess).toThrowMinErr( + '$http', 'legacy', 'The method `success` on the promise returned from `$http` has been disabled.'); + expect(callError).toThrowMinErr( + '$http', 'legacy', 'The method `error` on the promise returned from `$http` has been disabled.'); + }); +}); + describe('$http param serializers', function() { var defSer, jqrSer;