How do you 'intercept' all HTTP requests i

2019-04-10 08:55发布

问题:

I would like to be able to capture all HTTP requests and responses, and modify them, before they get to the rest of an EmberJs app. I would like to do this globally - across the entire app. I have not been able to find this digging through the API. How can this be done?

(Modification is to perform some conditional logic based in certain headers, or to add or modify certain headers).


In AngularJS, you can accomplish this using something like this:

App.factory('AppHttpInterceptor', function($q) {
    return {
        request: function(req) {
            //modify request
            return req;
        },
        response: function(res) {
            // modify response
            return res || $q.when(res);
        }
    };
});

App.config(function ($httpProvider) {
  $httpProvider.interceptors.push('AppHttpInterceptor');
});

回答1:

First, it's important to note that Angular and Ember are not built to serve the same purpose. They are both javascript frameworks, and that's about where the similarities end. Another big factor in the difference of async requests in the two framework is Angular's integration of promises into it's async services. Ember async services are not promise based, and therefore response interceptors are not possible (see my note below).

AngularJS provides $httpProvider as a configurable singleton that returns a configured instance of $http as a promise object. Angular focuses on services rather than methods (although it does have a few methods it gives you), and that's what sets Angular apart from other frameworks like Ember, which give you a structure to build services, but doesn't provide services in it's core.

Instead, with Ember, you'd have to build a service and service provider concept yourself. You could take something like cerebris/ember-rest, and extend it in such a way as to use properties you describe. This library provides an Ember.resource method, which you could use prototype to extend from there:

Ember.ResourceAdapter.extend({
    _prepareResourceRequest: function(params) {
      params.beforeSend = function (xhr, settings) {
       //set your xhr interceptors here
      }
    }
  });

Edit: Clarification on use of $ajax in ember and $http in Angular (promises vs callbacks)

The biggest difference on how angular makes response interceptors possible, is that async requests in angular are promises, whereas $ajax calls are not. Without getting too much into the weeds, you can assign variables to each step of a promise, making it available for mutation/handling along every step of the way, whereas with $ajax, you can only perform operations when the data has completely returned. With Promises, assign variables to represent states at any point during the lifecycle of the promise, and using that reference, you can mutate the promise as needed, at any point in time prior to the event of the promise fully resolving.

Thus why it's possible to do request interceptors with $ajaxPrefilter but there is no good way to do response interceptors, using the abstract configuration approach with $ajax. To truly do in Ember what AngularJS does with $http, you need to create a promise based async request/response service, as opposed to utilizing non-promise based xhr requests such as $ajax.

jQuery does provide an $ajaxSetup() method, which you might be able to set a dataFilter property to and define a handler function, however this is not recommended. With angular, the $httpProvider can be configured by module, and through de-coupling and Separation of Concerns, this can become truly powerful, allowing you to encapsulate and cascade http interceptor configurations with a lot of control. Making the same changes to your ajax settings will register themselves on the global jquery namespace, and can cause conflicts if you need to grow your app.

One video I found particularly englightening on this subject was from ng-conf 2014: Christion Lilley: Going Postal with Angular Promises

Edit 2: addressing Ember.RSVP

While Ember.RSVP is indeed a promise class useable in the framework, it does not have any methods available to perform resource requests. This means you have to manually assign an http request instance to an instance of RSVP.deferred such that it resolves your http request before returning the promise.

This allows you to do interceptors on both sides of each individual request, but does not provide a solution for configuring interceptors for all requests. You'd have to create another function or service for that, and extend RSVP with this function.