-
Notifications
You must be signed in to change notification settings - Fork 25.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Http interceptors and transformers #2684
Comments
This sort of approach makes a lot of sense to me, and generally just feels more composable and less configure-all-the-things. All for it. Might be worthwhile looking at doing the same sort of thing $http does, where the .then( returns the raw response, but has a couple of helper methods ( .success / .error ) which purely return the response data vs the whole response...
both read nicely for me. just comes down to edumacatin' everybody on the mechanics of Observables. |
If you wanted to do some sort of global transformers, you could have some globally registered function, but rather than just a mapping function, you could use a flatMapping function that gives you a stream of requests, and you output a stream of modified requests. i.e.: Config.requestIntercept = (requests) => {
return requests.flatMap(setHeaders);
}; this would allow either synchronous or asynchronous actions to occur to do things like set headers. Currently, in Angular 1, something like this would be impossible, AFAIK. |
Further thought on this: Presuming that each Config.requestIntercept = (requests) => requests.flatMap(req => req.timeout(5000)) |
I'd definitely like to see more "compose / wrap it yourself" APIs vs yet-another-thing-that's-angular2-specific-that-you-have-to-look-up-in-the-docs - the current API for interceptors is a bit of a nightmare (create a factory that you register with a provider in a config block) and as @jeffbcross said, mutable global state ftl... I often talk to a couple of different APIs from an app, and global interceptors make that more complex. |
@robwormald you're probably right. In fact, it might be better advice to tell people to compose over their requests themselves by wrapping the http service. In fact, I think I'd like to 👎 this feature in favor of advising people to wrap the http service and use that. |
... which is exactly what @jeffbcross had said to begin with. :P haha |
Ha, well I'd like to make sure we do due diligence, and explore ways we can make these things clear and simple, short of saying "go watch all of @Blesh's RxJS tutorials, then create some wrapper services, and you're done." I tend to lean away from the declarative, "everything-on-config" approach of Angular 1, but still think there may be helpers that could be added to reduce the bar of prerequisite Rx knowledge to do common things. |
is it heresy to suggest we provide an something like
I know I just said composition, and I prefer the wrap-it method, but it seems the choices are:
|
Perhaps helpers could come in the flavor of helpers to extend our wrap the service? I'm not sure what this looks like, just spitballing. |
I would prefer a general interception mechanism in Angular2 which is not only dedicated to an http service. This would allow a lot more possibilities. I entered an issue for that yesterday #2893 Wrapping a service would make sense if you do this for only one service, but this is not very productive if you need to do this for many services, e.g. adding automatic logging, exception handling etc. |
It's been a while since I looked at http in Angular 2.0. But after digging through the Angular source code, and doing a POC for my blog, my impression was that it follows some variation on this pattern: http://www.syntaxsuccess.com/viewarticle/angular-2.0-and-http Is this pattern still part of the vision? My initial stab at this was very simplistic and did not expose me to concepts like error handling, chaining or parallel requests. Surely there is a plan for this, but it's not 100% clear to me how it will look syntactically. Maybe not even conceptually :-) I find the current $q based solution to be very attractive when it comes to chaining, error handling and parallel requests using $q.all(..) etc. Another big advantage is that it aligns well with the promise standard know by most developers. I know rx.js pre-dates Angular, but I am worried that it's not as widely used as the promise model and might be seen as an Angular "odd man out" approach. Given the huge pivot of Angular 2.0 in general I think one should be allowed to expect a very compelling argument in favor of change before making changes to things that might not even be broken. Not saying such an argument can't be produced, but at the same time, I am very interested in hearing why Angular 2.0 decided to move away from $q and $http. I suspect there are some cool features built into rx since you decided to go in that direction. However, I think it's important to not forget that the 90% use case for http calls is most likely hitting some api and request some simple json data. My point is that there is a trade off between forcing everyone to reeducate themselves only to gain access to features that might already be solved well in the current, simple well known approach. That said, I am not against change, just want to learn more about the reasoning behind it. |
Hey Torgeir Thanks for the input - I'll write a more in depth response later, but have a look at this plunk and it might answer some of your initial questions : http://plnkr.co/edit/ul0NVGadmGi3v6nywGRw
|
Thanks @robwormald I will definitely take a look at that plunk! |
I'm glad to see this approach, when we use DI instead of global changes. To make this approach work with 3-party libraries too, we need to encourage them to require Http as dependency (if they need Http to work).
Please don't make this mistake again. $http.success/error is a very handy thing but it breaks the API signature - gives wrong expectation. For example, in first version of your component you return directly result of $http.get, client code written with .success/.error, and in later versions of component you find you need to return usual promise, without .success/.error functions - so you have 2 choices: breaking change or own implementation of .success/.error to your promise, just for few clients. So please follow observables API, without additions. |
The difference with the success/error idea in observable is that it's an operator - and adding operators is completely acceptable in Observable land (rxnext has a specific API for extending) - vs the terminator.
Note that you're calling .subscribe() in all cases there (equiv to .then). I do take your point tho. Thanks for the input, keep it coming! |
@jeffbcross one use case to consider might be the following: using $http interceptors in Angular 1 you can ensure that certain behaviour (e.g. redirect for authentication) is there by default, without developers having to remember to use a particular wrapper or shared service. Would this kind of default behaviour still be possible in Angular 2 using the wrapper approach? I am guessing you can tell DI to use the wrapper whenever Http is injected, right (do not know Angular 2 well enough yet)? |
I'm also looking for easy, angular1 like, solution. |
is there a out of the box solution for Interceptors on the roadmap for ng2 or do I have to solve the problem for my own? I've solved it with this Solution: https://squadwuschel.wordpress.com/2017/01/21/http-interception-in-angular-2/ |
you have to solve it yourself |
The problem with DIY solutions is that they are not going to be compatible with each other… |
Here is an excellent conversion of Ben Nadel's custom HTTP client. @witoldsz It seems like DIY solutions are not supposed to be compatible with each other for the most part because they are specific to the project. For example, security config for headers and tokens will differ across projects. However, there are somethings that could be common, such as some of the functions in the example I linked like URL path variable parsing, adding content type, and adding a xsrf cookie. Edit (11/27/16): I was able to use a modified version of Sam Storie's Typescript HTTP Client to great advantage. It is simpler than I first thought, especially for things like adding a HTTP Basic Auth header. |
@kentoj I tried creating a npm package to provide an interface for registering individual interceptors https://github.com/voliva/angular2-interceptors. The main idea is to have a structure that would support this kind of use case, and then you'd have interceptors specific for each project, although many of them could be shared. I'm looking forward to improve my solution, so any kind of feedback is welcome :) |
@MarkPieszak Hey Mark just wanted to bring to your attention that issue #80 doesn't exists, might be mainly because the angular/http repository doesn't exist (or is private?) currently. I beg you, please provide some kind of a simple solution and\or check what is going on with the http repository... I really really don't want to rewrite so much of the http class for such simple functionality... what do we have the DRY rule for?? |
It went private then I believe was merged into the main Angular repo. I believe (but I'm not sure) that at least the current goal is to leave it up to the user to override or implement Http and wrap things that way. But I do agree, it would be nice to keep things consistent between ng1&2 in this regard. What are you currently doing to achieve this? There's a lot of options online to choose from as well. |
@LiorArbel and @MarkPieszak At first glance it definitely seemed like a violation of DRY until I did my own implementation of an HTTP client. What it ended up being was a service for accessing my backend API gateway. Now that my first implementation is done I could see a valid use case for creating a custom HTTP client for every API gateway my application interacts with. Why? the authentication may be different, the content type may be different (XML or JSON), one may implement hypermedia (HATEOAS) and one may not, and so forth. Creating a global HTTP interceptor could make things harder for me if I interact with multiple APIs that have different requirements. This could be the 80/20 rule though. If 80% of angular 2 apps only communicate to one backend API then a global interceptor may not be so bad. The HTTP interceptor module, if created, seems like a module that could be added in and used conditionally, similar to how Spring Java uses the At the very least we should point from the official docs to an example or at least the idea of a custom HTTP client, the one I mentioned from Sam Storie earlier being a good candidate. |
@kentoj this is so much the "my point of view is better than yours", actually. You have some special setup for which the "global" HTTP interceptor would be less optimal than your own custom HTTP service wrapper. That is fine, but having an option to register/plug-into the official HTTP service would suit others just well, would allow for a 3rd party market of extensions (like it's now for AngularJS 1), and you could still choose your own wrapper solution. |
@witoldsz How do you propose to implement the option of registering an interceptor or plugging it in? Since, as the OP stated, Angular 2 has no "phases" like "config" and "run" then as Angular currently exists you would have to ensure the interceptor was registered before any HTTP calls were made. Or, you could do something like what @voliva did that ends up with injection of an interceptor-aware wrapper for HTTP:
Without adding something like "config" or "run" it looks like the best option for interceptors in Angular 2 HTTP is a wrapper, whether that be a custom HTTP client or an interceptor-aware extension to HTTP, which is a custom HTTP client. So either way, it looks like a custom HTTP client of some sorts is the way to go unless you propose adding some config phase to the angular 2 bootstrapper or dependency injector. |
@kentoj let me be a bit rude here... On the contrary, it would only help all those tons of people that wrapped the Http class and wrote tons of coupled code to add such a simple feature from angular 1 that involves about 10 lines of code. I mean, even auth0 ended up rewriting all the Http class' function signatures , |
@LiorArbel I appreciate the energy. You make a good point on coupling to the Http class's API. I make no argument against adding an interceptor. Furthermore, I would probably use the interceptor functionality if it existed. I must be coming across as against interceptors. I am not against them at all. They were the first thing that I looked for beyond simple HTTP in an angular app. When I first learned I would have to subclass HTTP to add my functionality I was very displeased because of the added complexity, coupling, and class inheritance. Avoid class-based inheritance except when I know for sure it is the best tool for the job seems to keep me out of inheritance hierarchy troubles. Regarding custom HTTP clients I only acknowledge the usefulness of a custom HTTP client and the value I got from having to write one myself (copy from Sam Storie and Ben Nadel :] ). Once I wrote my custom HTTP client I felt like I had more control and understanding of the underlying process. It feels like I can contain the leaks of the network and HTTP abstractions in one place instead of everywhere I need to make an HTTP call. Additionally, when I implement HATEOAS since HATEOAS is a protocol for defining domain-specific languages in the future I see myself handling that protocol in a custom HTTP client and not an interceptor since the scope for that protocol management goes beyond simple interception. Since some of my backend microservices may not yet be using HATEOAS I may need a custom HTTP client for each microservice depending on its maturity level. Even still, I can see myself using an HTTP interceptor for all requests for authentication since I expect authentication to be globally the same for one client application. Comparing the coupling of the custom HTTP client to an interceptor API makes it seem like using an interceptor would only be coupled to one method, the interceptor registration call. And yet, the claim I previously made still stands. How would we add the interceptor functionality to Angular 2 without adding bootstrap or config phases? Bootstrap and config phases could be the right answer. Spring does a similar thing when doing dependency injection for a Spring Java app. Maybe the right place to add bootstrap or config for Angular 2 is in the dependency injector. That may or may not be an option for Angular 2. Who is the creator of the injector? |
Although I found HTTP interceptors very useful in Angular 1, there are also some drawbacks for using global interceptors. For example, I used interceptors to manage the "login" status of the user, but! what happened when you're dealing with the login request itself? Should it "trap" using the global interceptor? I found that for complicated apps, there are several types of HTTP connections I want to handle and a global one break this approach. So, instead of manipulating the Http class itself, I have several "backend" classes that handle HTTP requests, each one responsible for its specific task (checking for login during request, handling JSON, etc.) Changing the HTTP client itself, limit your application to create different request types. |
They have ideas to introduce something like |
@DzmitryShylovich Thank you very much, it's nice to know. Would be happy if you could keep us updated. @kentoj Must we have an initialization\bootstrap\config\w.e. stage? What if the Http service would be a stateful service that holds interceptors that can be added at runtime? And then if we take for example the authentication token header case - only after the usser logged to your server you will then push the interceptor which handles the authentication token headers. |
@LiorArbel good point about coupling with Http methods! |
Interceptors will be available in ~4.1 |
@DzmitryShylovich It seems the proposal is obsolete. What's the status of it? |
Is there an update? The Doc from @DzmitryShylovich is "obsolete" |
@jeffbcross Could you please update on the status? The regression vs Angular 1.x of not having interceptors and transformers is breaking essential functionality we need to:
To be frank, I'm stunned that the framework still does not support this and that Http is marked experimental. This has been solved years ago in v1, and XHRs are so fundamental to a SPA. Currently the status of this feature is in limbo:
I don't know whether we are forced to make our own Http wrapper with this basic functionality and have to update all our dependencies. And then, when (if?) v4.1 (or a later version) supports it, we need to refactor our codebase again. At least having clarity in this matter would soften the hurt a bit, but this migration is starting to look really ugly. 😞 |
@bgever you can just create a custom |
@zoechi I think this discussion is more about how not to build a custom class but rather really only use Http interceptors. Which are two different use cases in my opinion. |
Whoever needs an elegant solution for interceptors, you can try ng-http-interceptors =) |
@BorntraegerMarc - spot on! Considering not an exact port of AngularJS to Angular 4+, the custom |
According to this:
and
So I guess something is being cooked internally at Google and they won't tell us about it until it's ready. ¯\_(ツ)_/¯ |
see #17143 |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
This issue is intended to define requirements for how the
Http
library should support common operations, like request and response transformations and error handling. The issue doesn't yet have a proposed design, but discussion, comments and suggestions are welcomed. Un-captured use cases are especially welcomed.History
Two widely-used feature of angular1 are request and response interceptors and request/response transformers. Interceptors and Transformers have different goals and constraints.
Some common use cases for interceptors:
Common use cases for transformers
Worth noting are some additional features provided by Angular 1 $http, which would otherwise be handled by transformers:
New Considerations for Angular 2’s Http Library
Angular 2 and Angular 2 Http have some technical differences and philosophical differences from Angular 1.
$http.defaults.transforms.push(...)
).Observable
instead of PromiseCurrent Options
The current library supports creating services that would provide shared interceptors and transformations. Here's an example of an Http-based service that would add custom headers, cache responses and log errors, based on the current RxJS implementation.
Questions to answer with final design:
The text was updated successfully, but these errors were encountered: