dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.27k stars 9.96k forks source link

Add proxying support to the Blazor dev server #24606

Open danroth27 opened 4 years ago

danroth27 commented 4 years ago

Add proxying support to the Blazor dev server similar to the functionality available from other frontend frameworks, particularly those based on webpack:

This functionality is useful when the Blazor WebAssembly app is being developed as a separate project from its backend API during development, but will be deployed to the same domain in production. A common example is a Blazor WebAssembly frontend with an Azure Functions backend.

javiercn commented 4 years ago

@danroth27 can we leverage YARP for this?

ghost commented 4 years ago

Thanks for contacting us. We're moving this issue to the Next sprint planning milestone for future evaluation / consideration. We will evaluate the request when we are planning the work for the next milestone. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

danroth27 commented 4 years ago

We have proxy middleware in the aspnet/asplabs repo: https://github.com/aspnet/AspLabs/tree/master/src/Proxy. I'm not sure if YARP is using that or not. @Tratcher?

We also have some proxying logic in SpaServices.Extensions that I believe @SteveSandersonMS wrote.

javiercn commented 4 years ago

I believe the main things here are to rewrite/forward requests and rewrite things like cookies and things like that?

Tratcher commented 4 years ago

@danroth27 YARP was "inspired-by" that lab, but the code isn't very similar anymore. The relevant part is here: https://github.com/microsoft/reverse-proxy/blob/master/src/ReverseProxy/Service/Proxy/HttpProxy.cs

danroth27 commented 4 years ago

I believe the main things here are to rewrite/forward requests and rewrite things like cookies and things like that?

That sounds right. The goal is to emulate single origin as much as is possible/reasonable.

Tratcher commented 4 years ago

Just be aware of all the things proxies break. https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-3.1

Things used for url generation, redirects, cookies, etc.:

Rewriting response headers is one thing, trying to rewrite anything in the content is a very different problem.

aaronpowell commented 4 years ago

A quick win could be to extend the DevServer to use the SpaServices.Extensions middleware as it already contains a proxy feature: https://github.com/dotnet/aspnetcore/tree/master/src/Middleware/SpaServices.Extensions/src/Proxying

To make it interoperable with the way many JavaScript projects work, it could leverage the BrowserSync config proxy structure - https://www.browsersync.io/docs/options#option-proxy

So have it that when the devserver starts up, if it finds a bs-config.json file, then add the middleware and parse the proxy information out of it to setup the middleware.

javiercn commented 4 years ago

I think that the spa services proxy works in combination with the different JS proxies. I think we would no take a dependency on node JS for something like that.

aaronpowell commented 4 years ago

It doesn't appear that it uses anything from Node, the code for the proxy implementation is in https://github.com/dotnet/aspnetcore/blob/master/src/Middleware/SpaServices.Extensions/src/Proxying/SpaProxy.cs and it added middleware to each request (https://github.com/dotnet/aspnetcore/blob/master/src/Middleware/SpaServices.Extensions/src/Proxying/SpaProxyingExtensions.cs#L77-L82) and uses HttpClient to forward the request (https://github.com/dotnet/aspnetcore/blob/master/src/Middleware/SpaServices.Extensions/src/Proxying/SpaProxy.cs#L76-L94).

It looks like prior to .NET 5 the extensions depends on SpaServices, which in turn depends on NodeServices, but if I understand the csproj correctly it's being removed in 5 RTM: (https://github.com/dotnet/aspnetcore/blob/master/src/Middleware/SpaServices.Extensions/src/Microsoft.AspNetCore.SpaServices.Extensions.csproj#L18-L23)

javiercn commented 4 years ago

@aaronpowell yes, but it is proxying that request to the angular/react/webpack proxy that is started by ng run and so on, which in turn are the ones that take care of proxying API requests and things like that.

aaronpowell commented 4 years ago

Isn't it proxying any request that can't be handled by the ASP.Net server to an endpoint that's running whatever the endpoint runs?

javiercn commented 4 years ago

Now I'm not sure we are talking about the same things. Proxying to a backend server in angular/react and other things like that are handled by nodejs middlware as Dan points out.

From what I've played with it, that middleware does more than what our current proxy middleware does (rewrites cookies, etc.) that's why I'm saying the SPA proxy is not enough, but I might be wrong.

@SteveSandersonMS will know better since he wrote the thing :).

aaronpowell commented 4 years ago

My understanding is that the goal is to have it so that if you hit http://localhost:5000/api/product and there isn't a Blazor route that matches that it should then pass through to whatever you've designated /api/product to map to in the proxy config (it could be a local Azure Functions, it could be an asp.net core server, it could be something running on another machine), emulating single origin as Dan mentions.

SteveSandersonMS commented 4 years ago

@aaronpowell Makes sense!

I guess we would need to extend the framework's MapFallback logic so that, if an incoming request does not have Accept: text/html, then the "fallback" behavior switches over to doing a proxied request instead of returning the usual fallback response (e.g., the index.html file).

Alternatively we'd create some other middleware the dev server plugs in before MapFallback, which does this proxying for all requests that don't have Accept: text/html, assuming that the static-files middleware or an MVC action etc. would already have handled the request if it matched a physical file or static web asset or known server-side endpoint.

As for the way the proxying is achieved, it's probably ideal to use YARP if we can, because that's the more modern and better maintained implementation. As per @Tratcher's comment, there are all kinds of caveats about what can actually be proxied and how it impacts behavior, but that's the same for all the other SPA tooling proxies too.

I'm not certain what aspects of request/response rewriting are involved (besides changing the Origin and rewriting CORS headers). Most likely there are lots of details that aren't obvious up front. We would want to research exactly what the other SPA frameworks do to get an initial estimate of the required feature set. It might end up being a quick and cheap feature, or a hugely expensive long tail of issues.

javiercn commented 4 years ago

Most of the FE frameworks use something along the lines of https://webpack.js.org/configuration/dev-server/#devserverproxy, which is why I suggested our SPA proxy wasn't enough. This is also based on my experimentation of switching the SPA proxy to use the native version.

Our SPA proxy, AFAIK just forwards the requests, but doesn't do any sort of transformation. Other FE frameworks allow you to specify things like "when hitting this url, this is the endpoint/url" you need to send the request to, and take care of the changes needed on the request and response to make that happen.

ghost commented 3 years ago

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

ghost commented 2 years ago

Thanks for contacting us.

We're moving this issue to the .NET 8 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

ghost commented 1 year ago

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

jbasinger commented 10 months ago

What if you allowed a string of URLs as a configuration option to the DevServer and a flag to use CORs with that list of strings?

Add a DevServerOptions class, and setup app.UseCors() with those options from the configuration. That way users can just setup a list of allowed servers in a blazor-devserversettings.json file in their project.