AspNetCore.SpaYarp icon indicating copy to clipboard operation
AspNetCore.SpaYarp copied to clipboard

Make Proxied Request Path Configurable

Open DanielStout5 opened this issue 4 years ago • 8 comments
trafficstars

I haven't been able to fully test this yet (as per my other issue) but from reading the code, I think that the line the line endpoints.Map("/{**catch-all}" in IEndpointRouteBuilderExtensions would make it so the .Net application itself can't have a "catch all" handler - which is I think a relatively common requirement.

I need to be able to have only certain requests proxied - e.g. those starting with /dist/ and other paths specific to the hot reloading features of Webpack. That would let the .Net application handle all other requests, including not matched ones.

I'm thinking that MapSpaYarp should at very least accept the string pattern as a parameter

DanielStout5 avatar Oct 22 '21 18:10 DanielStout5

Thank you for the suggestion. I have not much time currently, but if you create a PR I will merge it.

Not sure if it's better to add a parameter to the MapSpaYarp method or if we should add a setting to the SpaDevelopmentServerOptions.

berhir avatar Oct 30 '21 09:10 berhir

My opinion would be that it'd be better to add it to MapSpaYarp, since it seems to me that ideally SpaDevelopmentServerOptions could stay in sync with the "real" version that's actually in aspnetcore.

As I mentioned in my other ticket, I've removed SpaYarp and have basically replicated most of what it does - here's how I am now configuring the endpoints:

                if (DebugUtils.IsDebug) // equivalent to #if DEBUG
                {
                    var prov = endpts.ServiceProvider;
                    var forward = prov.GetRequiredService<IHttpForwarder>();
                    var spaOpt = prov.GetRequiredService<IOptions<SpaDevelopmentServerOptions>>().Value;
                    var client = new HttpMessageInvoker(new SocketsHttpHandler()
                    {
                        UseProxy = false,
                        AllowAutoRedirect = false,
                        AutomaticDecompression = System.Net.DecompressionMethods.None,
                        UseCookies = false
                    });

                    var dest = new Uri(spaOpt.ServerUrl).GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped);

                    endpts.Map("/dist/{*path}", async ctx =>
                    {
                        var error = await forward.SendAsync(ctx, dest, client);
                        log.LogInformation("Forwarder error: {err}", error);
                    });
                }

Thanks for creating this library - even if I'm not using it directly now, it was how I learned about Yarp and showed me how to solve this issue :)

DanielStout5 avatar Nov 02 '21 12:11 DanielStout5

I've made a pull request that introduces SpaDevelopmentServerOptions.PublicPath option which corresponds to devServer.publicPath option of Webpack Dev Server. If .csproj file contains <SpaPublicPath>/dist</SpaPublicPath> only requests with the specified path are forwarded to Dev Server. If .csproj file contains an empty <SpaPublicPath></SpaPublicPath> or this option is omited, forwarding works from the root, as before. I didn't add any specific sample because MVC+SPA pattern is quite cumbersome and its structure is arguable, and it uses Webpack directly. But you can be sure that it works. If 'PublicPath' name is not suitable it can be renamed to something else.

buchatsky avatar Nov 19 '21 21:11 buchatsky

@buchatsky thank you for the PR, I merged it. I will make a few more tests and publish a new version to NuGet within the next few days.

berhir avatar Nov 26 '21 13:11 berhir

There may be some use cases when a single redirect path is not enough. For ex. when several SPAs are launched from different Razor views. So, semicolumn-delimited lists with wildcards would be more suitable

buchatsky avatar Dec 11 '21 13:12 buchatsky

@berhir @buchatsky does it make sense to allow custom yarp configuration file for full customization instead of the predefined routes? In the real world applications, we usually have more than one cluster and routes. ex: a signalr route, a spa route, and an api route)

example:

.AddReverseProxy()
      .LoadFromConfig(configuration.GetSection("ReverseProxy"));

litan1106 avatar Dec 15 '21 16:12 litan1106

Hello for me using <SpaPublicPath> option doesnt work properly in regards to detecting if ng server is running. If I put <SpaPublicPath>/spa</SpaPublicPath> and <SpaClientUrl>https://localhost:4300</SpaClientUrl> the IsSpaClientRunning doesnt detect succesfull the SPA as it is also lunched with /spa/ option for its routing to work: ng serve --port 4300 --serve-path=/spa/ If I put <SpaPublicPath>/spa</SpaPublicPath> and <SpaClientUrl>https://localhost:4300/spa</SpaClientUrl> IsSpaClientRunning detects it but forwarding is being done with https://localhost:4300/spa/spa/ and doesnt work.

I belive this + _options.PublicPath should be added to IsSpaClientRunning var response = await httpClient.GetAsync(_options.ClientUrl + _options.PublicPath, cancellationTokenSource.Token);

muntdan avatar Jun 02 '22 07:06 muntdan

@danmunteanuevo please see #17. a new version that fixes this issue will be published soon

berhir avatar Aug 07 '22 06:08 berhir