[英]Creating a proxy to another web api with Asp.net core
我正在開發一個 ASP.Net Core Web 應用程序,我需要為另一個(外部)Web 服務創建一種“身份驗證代理”。
我所說的身份驗證代理的意思是,我將通過我的 Web 應用程序的特定路徑接收請求,並且必須檢查這些請求的標頭以獲取我之前發布的身份驗證令牌,然后使用相同的請求字符串/內容到外部 Web API,我的應用程序將通過 HTTP 基本身份驗證進行身份驗證。
這是偽代碼的整個過程
/extapi
並在 HTTP 標頭中添加 auth-token這是我現在所擁有的。 它似乎工作正常,但我想知道這是否真的應該這樣做,或者是否沒有更優雅或更好的解決方案? 從長遠來看,該解決方案會為擴展應用程序帶來問題嗎?
[HttpGet]
public async Task GetStatement()
{
//TODO check for token presence and reject if issue
var queryString = Request.QueryString;
var response = await _httpClient.GetAsync(queryString.Value);
var content = await response.Content.ReadAsStringAsync();
Response.StatusCode = (int)response.StatusCode;
Response.ContentType = response.Content.Headers.ContentType.ToString();
Response.ContentLength = response.Content.Headers.ContentLength;
await Response.WriteAsync(content);
}
[HttpPost]
public async Task PostStatement()
{
using (var streamContent = new StreamContent(Request.Body))
{
//TODO check for token presence and reject if issue
var response = await _httpClient.PostAsync(string.Empty, streamContent);
var content = await response.Content.ReadAsStringAsync();
Response.StatusCode = (int)response.StatusCode;
Response.ContentType = response.Content.Headers.ContentType?.ToString();
Response.ContentLength = response.Content.Headers.ContentLength;
await Response.WriteAsync(content);
}
}
_httpClient
是一個HttpClient
類實例化別的地方,是一個獨立的,並與BaseAddress
的http://someexternalapp.com/api/
此外,是否有比手動進行令牌創建/令牌檢查更簡單的方法?
如果有人感興趣,我采用了 Microsoft.AspNetCore.Proxy 代碼,並使用中間件使其更好一些。
在這里查看: https : //github.com/twitchax/AspNetCore.Proxy 。 NuGet 在這里: https : //www.nuget.org/packages/AspNetCore.Proxy/ 。 微軟存檔了這篇文章中提到的另一個,我計划回應這個項目的任何問題。
基本上,它允許您在采用帶有 args 的路由並計算代理地址的方法上使用屬性,從而使反向代理另一個 Web 服務器變得更加容易。
[ProxyRoute("api/searchgoogle/{query}")]
public static Task<string> SearchGoogleProxy(string query)
{
// Get the proxied address.
return Task.FromResult($"https://www.google.com/search?q={query}");
}
我最終實現了一個受Asp.Net 的 GitHub 項目啟發的代理中間件。
它基本上實現了一個中間件,它讀取收到的請求,從中創建一個副本並將其發送回配置的服務,讀取服務的響應並將其發送回調用者。
這篇文章討論了在 C# 或 ASP.NET Core 中編寫一個簡單的 HTTP 代理邏輯。 並允許您的項目將請求代理到任何其他 URL。 這不是為您的 ASP.NET Core 項目部署代理服務器。
在項目的任何位置添加以下代碼。
public static HttpRequestMessage CreateProxyHttpRequest(this HttpContext context, Uri uri)
{
var request = context.Request;
var requestMessage = new HttpRequestMessage();
var requestMethod = request.Method;
if (!HttpMethods.IsGet(requestMethod) &&
!HttpMethods.IsHead(requestMethod) &&
!HttpMethods.IsDelete(requestMethod) &&
!HttpMethods.IsTrace(requestMethod))
{
var streamContent = new StreamContent(request.Body);
requestMessage.Content = streamContent;
}
// Copy the request headers
foreach (var header in request.Headers)
{
if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()) && requestMessage.Content != null)
{
requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
}
}
requestMessage.Headers.Host = uri.Authority;
requestMessage.RequestUri = uri;
requestMessage.Method = new HttpMethod(request.Method);
return requestMessage;
}
此方法隱蔽用戶將HttpContext.Request
發送到可重用的HttpRequestMessage
。 因此,您可以將此消息發送到目標服務器。
在您的目標服務器響應之后,您需要將響應的HttpResponseMessage
復制到HttpContext.Response
以便用戶的瀏覽器獲取它。
public static async Task CopyProxyHttpResponse(this HttpContext context, HttpResponseMessage responseMessage)
{
if (responseMessage == null)
{
throw new ArgumentNullException(nameof(responseMessage));
}
var response = context.Response;
response.StatusCode = (int)responseMessage.StatusCode;
foreach (var header in responseMessage.Headers)
{
response.Headers[header.Key] = header.Value.ToArray();
}
foreach (var header in responseMessage.Content.Headers)
{
response.Headers[header.Key] = header.Value.ToArray();
}
// SendAsync removes chunking from the response. This removes the header so it doesn't expect a chunked response.
response.Headers.Remove("transfer-encoding");
using (var responseStream = await responseMessage.Content.ReadAsStreamAsync())
{
await responseStream.CopyToAsync(response.Body, _streamCopyBufferSize, context.RequestAborted);
}
}
現在准備工作已經完成。 回到我們的控制器:
private readonly HttpClient _client;
public YourController()
{
_client = new HttpClient(new HttpClientHandler()
{
AllowAutoRedirect = false
});
}
public async Task<IActionResult> Rewrite()
{
var request = HttpContext.CreateProxyHttpRequest(new Uri("https://www.google.com"));
var response = await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, HttpContext.RequestAborted);
await HttpContext.CopyProxyHttpResponse(response);
return Ok();
}
並嘗試訪問它。 它將被代理到 google.com
一個不錯的反向代理中間件實現也可以在這里找到: https : //auth0.com/blog/building-a-reverse-proxy-in-dot-net-core/
請注意,我在這里替換了這一行
requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
和
requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToString());
在我的情況下,如果沒有我的修改,則不會添加原始標頭(例如,帶有不記名令牌的授權標頭)。
我很幸運地使用了twitchax 的 AspNetCore.Proxy NuGet 包,但無法使用twitchax 的回答中顯示的ProxyRoute
方法使其工作。 (可能很容易成為我的錯誤。)
相反,我在 Statup.cs Configure() 方法中定義了類似於下面的代碼的映射。
app.UseProxy("api/someexternalapp-proxy/{arg1}", async (args) =>
{
string url = "https://someexternalapp.com/" + args["arg1"];
return await Task.FromResult<string>(url);
});
依靠 James Lawruk 的回答https://stackoverflow.com/a/54149906/6596451使 twitchax Proxy 屬性正常工作,我也收到了 404 錯誤,直到我在 ProxyRoute 屬性中指定了完整路由。 我在一個單獨的控制器中有我的靜態路由,而控制器路由的相對路徑不起作用。
這有效:
public class ProxyController : Controller
{
[ProxyRoute("api/Proxy/{name}")]
public static Task<string> Get(string name)
{
return Task.FromResult($"http://www.google.com/");
}
}
這不會:
[Route("api/[controller]")]
public class ProxyController : Controller
{
[ProxyRoute("{name}")]
public static Task<string> Get(string name)
{
return Task.FromResult($"http://www.google.com/");
}
}
希望這可以幫助某人!
這是ASP.NET Core 代理庫的基本實現:
這不會實現授權,但對於使用 ASP.NET Core 尋找簡單反向代理的人來說可能很有用。 我們僅將其用於開發階段。
using System;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
namespace Sample.Proxy
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(options =>
{
options.AddDebug();
options.AddConsole(console =>
{
console.IncludeScopes = true;
});
});
services.AddProxy(options =>
{
options.MessageHandler = new HttpClientHandler
{
AllowAutoRedirect = false,
UseCookies = true
};
options.PrepareRequest = (originalRequest, message) =>
{
var host = GetHeaderValue(originalRequest, "X-Forwarded-Host") ?? originalRequest.Host.Host;
var port = GetHeaderValue(originalRequest, "X-Forwarded-Port") ?? originalRequest.Host.Port.Value.ToString(CultureInfo.InvariantCulture);
var prefix = GetHeaderValue(originalRequest, "X-Forwarded-Prefix") ?? originalRequest.PathBase;
message.Headers.Add("X-Forwarded-Host", host);
if (!string.IsNullOrWhiteSpace(port)) message.Headers.Add("X-Forwarded-Port", port);
if (!string.IsNullOrWhiteSpace(prefix)) message.Headers.Add("X-Forwarded-Prefix", prefix);
return Task.FromResult(0);
};
});
}
private static string GetHeaderValue(HttpRequest request, string headerName)
{
return request.Headers.TryGetValue(headerName, out StringValues list) ? list.FirstOrDefault() : null;
}
public void Configure(IApplicationBuilder app)
{
app.UseWebSockets()
.Map("/api", api => api.RunProxy(new Uri("http://localhost:8833")))
.Map("/image", api => api.RunProxy(new Uri("http://localhost:8844")))
.Map("/admin", api => api.RunProxy(new Uri("http://localhost:8822")))
.RunProxy(new Uri("http://localhost:8811"));
}
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}
Twitchax 的回答似乎是目前最好的解決方案。 在對此進行研究時,我發現 Microsoft 正在開發一種更強大的解決方案,該解決方案適合 OP 試圖解決的確切問題。
回購: https : //github.com/microsoft/reverse-proxy
預覽版 1 的文章(他們實際上剛剛發布了前版 2): https : //devblogs.microsoft.com/dotnet/introducing-yarp-preview-1/
從文章...
YARP 是一個創建反向代理服務器的項目。 當我們注意到 Microsoft 內部團隊提出的一系列問題時,他們要么為他們的服務構建反向代理,要么一直在詢問 API 和構建一個的技術,所以我們決定讓他們一起研究一個通用的解決方案,這已成為 YARP。
YARP 是一個反向代理工具包,用於使用來自 ASP.NET 和 .NET 的基礎結構在 .NET 中構建快速代理服務器。 YARP 的關鍵區別在於它被設計為易於定制和調整,以滿足每個部署場景的特定需求。 YARP 插入 ASP.NET 管道以處理傳入請求,然后擁有自己的子管道來執行將請求代理到后端服務器的步驟。 客戶可以根據需要添加額外的模塊,或更換庫存模塊。
...
YARP 適用於 .NET Core 3.1 或 .NET 5 preview 4(或更高版本)。 從https://dotnet.microsoft.com/download/dotnet/5.0下載 .NET 5 SDK 的預覽版 4(或更高版本)
更具體地說,他們的示例應用程序之一實現了身份驗證(至於 OP 的原始意圖) https://github.com/microsoft/reverse-proxy/blob/master/samples/ReverseProxy.Auth.Sample/Startup.cs
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.