Ver Fonte

Fix URL building for SPA proxying (#43407)

Márton Varsányi há 3 anos atrás
pai
commit
03ddd91eff

+ 2 - 2
src/Middleware/Spa/SpaServices.Extensions/src/Microsoft.AspNetCore.SpaServices.Extensions.csproj

@@ -11,9 +11,9 @@
     <Reference Include="Microsoft.AspNetCore.WebSockets" />
     <Reference Include="Microsoft.Extensions.FileProviders.Physical" />
   </ItemGroup>
-
+  
   <ItemGroup>
-    <InternalsVisibleTo Include="Microsoft.AspNetCode.SpaServices.Extensions.Tests" />
+    <InternalsVisibleTo Include="Microsoft.AspNetCore.SpaServices.Extensions.Tests" />
   </ItemGroup>
 
 </Project>

+ 4 - 3
src/Middleware/Spa/SpaServices.Extensions/src/Proxying/SpaProxy.cs

@@ -68,9 +68,10 @@ internal static class SpaProxy
         // when proxying to Angular CLI middleware: we won't know what port it's listening
         // on until it finishes starting up.
         var baseUri = await baseUriTask;
-        var targetUri = new Uri(
-            baseUri,
-            context.Request.Path + context.Request.QueryString);
+        var baseUriAsString = baseUri.ToString();
+        var targetUri = new Uri((baseUriAsString.EndsWith("/", StringComparison.OrdinalIgnoreCase) ? baseUriAsString[..^1] : baseUriAsString)
+            + context.Request.Path
+            + context.Request.QueryString);
 
         try
         {

+ 2 - 0
src/Middleware/Spa/SpaServices.Extensions/test/Microsoft.AspNetCore.SpaServices.Extensions.Tests.csproj

@@ -12,6 +12,8 @@
     <Reference Include="Microsoft.AspNetCore.TestHost" />
     <Reference Include="Microsoft.Extensions.DiagnosticAdapter" />
     <Reference Include="Microsoft.Extensions.Hosting" />
+    <Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
+    <Reference Include="System.Net.Http" />
     <Content Include="js\**\*" />
   </ItemGroup>
 

+ 61 - 0
src/Middleware/Spa/SpaServices.Extensions/test/SpaProxyTests.cs

@@ -0,0 +1,61 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.SpaServices.Extensions.Proxy;
+using Microsoft.AspNetCore.Http;
+using Moq;
+using Xunit;
+using System.Threading.Tasks;
+using System;
+using System.Net.Http;
+using System.Net;
+using System.Threading;
+using Moq.Protected;
+
+namespace Microsoft.AspNetCore.SpaServices.Extensions.Tests;
+
+public class SpaProxyTests
+{
+    private static (HttpContext, HttpClient) GetHttpContextAndClient(string path, string queryString, Action<HttpRequestMessage> callback)
+    {
+        var messageHandler = new Mock<HttpMessageHandler>();
+        messageHandler.Protected()
+            .Setup<Task<HttpResponseMessage>>("SendAsync",
+                                        ItExpr.IsAny<HttpRequestMessage>(),
+                                        ItExpr.IsAny<CancellationToken>())
+            .Callback<HttpRequestMessage, CancellationToken>((req, c) => callback(req))
+            .ReturnsAsync(new HttpResponseMessage
+            {
+                StatusCode = HttpStatusCode.OK,
+                Content = new StringContent("Test")
+            });
+
+        var context = new DefaultHttpContext();
+        context.Request.Path = path;
+        context.Request.QueryString = new QueryString(queryString);
+        context.Request.Method = "GET";
+
+        return (context, new HttpClient(messageHandler.Object));
+    }
+
+    [Theory]
+    [InlineData("http://localhost:3000", "", "", "http://localhost:3000/")]
+    [InlineData("http://localhost:3000", "", "?a=b", "http://localhost:3000/?a=b")]
+    [InlineData("http://localhost:3000/", "", "?a=b", "http://localhost:3000/?a=b")]
+    [InlineData("http://localhost:3000", "/test", "?a=b", "http://localhost:3000/test?a=b")]
+    [InlineData("http://localhost:3000/", "/test", "?a=b", "http://localhost:3000/test?a=b")]
+    [InlineData("http://localhost:3000/spa", "/test", "?a=b", "http://localhost:3000/spa/test?a=b")]
+    [InlineData("http://localhost:3000/spa/", "/test", "?a=b", "http://localhost:3000/spa/test?a=b")]
+    [InlineData("http://localhost:3000", "///test", "?a=b", "http://localhost:3000///test?a=b")]
+    [InlineData("http://localhost:3000/", "///test", "?a=b", "http://localhost:3000///test?a=b")]
+    [InlineData("http://localhost:3000/spa", "///test", "?a=b", "http://localhost:3000/spa///test?a=b")]
+    [InlineData("http://localhost:3000/spa/", "///test", "?a=b", "http://localhost:3000/spa///test?a=b")]
+    public async Task PerformProxyRequest_TestUrlCombination(string baseUrl, string path, string queryString, string expected)
+    {
+        HttpRequestMessage forwardedRequestMessage = null;
+        var (context, httpClient) = GetHttpContextAndClient(path, queryString, (req) => forwardedRequestMessage = req);
+        var baseUriTask = Task.FromResult(new Uri(baseUrl));
+        var res = await SpaProxy.PerformProxyRequest(context, httpClient, baseUriTask, CancellationToken.None, true);
+        Assert.Equal(expected, forwardedRequestMessage.RequestUri.ToString());
+    }
+}