Asp.net core를 사용하여 다른 웹 API에 대한 프록시 만들기
다른(외부) 웹 서비스에 대한 일종의 "인증 프록시"를 만들어야 하는 ASP.Net Core 웹 응용 프로그램을 개발하고 있습니다.
인증 프록시를 의미하는 것은 웹 앱의 특정 경로를 통해 요청을 수신하고 이전에 발급한 인증 토큰에 대해 해당 요청의 헤더를 확인해야 한다는 것입니다.그런 다음 동일한 요청 문자열/내용을 가진 모든 요청을 HTTP Basic 인증을 통해 내 앱이 인증할 외부 웹 API로 리디렉션합니다.
여기 의사 코드로 된 모든 과정이 있습니다.
- 클라이언트가 내가 이전에 보낸 고유 URL에 POST를 만들어 토큰을 요청합니다.
- 내 앱은 이 POST에 대한 응답으로 그에게 고유 토큰을 보냅니다.
- 는 내. 를 들어, "GET"입니다.
/extapi
HTTP 헤더에 인증 토큰을 추가합니다. - 앱이 요청을 받고 인증 토큰이 있고 유효한지 확인하는 경우
- 내 앱이 외부 웹 API에 동일한 요청을 수행하고 BASIC 인증을 사용하여 요청을 인증합니다.
- 내 앱이 요청에서 결과를 수신하여 클라이언트에 다시 보냅니다.
제가 지금 가지고 있는 것은 다음과 같습니다.잘 작동하는 것 같지만, 정말 이런 식으로 해야 하는지 아니면 이에 대한 더 우아하거나 더 나은 해결책이 없는지 궁금합니다.이 솔루션이 장기적으로 애플리케이션 확장에 문제를 일으킬 수 있습니까?
[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
a가 되는 것HttpClient
수업은 다른 곳에서 인스턴스화되고 싱글톤이 되고 그리고.BaseAddress
http://someexternalapp.com/api/
또한 토큰 생성/토큰 검사를 수동으로 하는 것보다 더 간단한 방법이 있습니까?
관심 있는 사람이 있으면 마이크로소프트를 선택했습니다.AsNetCore.프록시 코드를 사용하여 미들웨어를 조금 개선했습니다.
여기를 확인해 보세요: https://github.com/twitchax/AspNetCore.Proxy .NuGet here: https://www.nuget.org/packages/AspNetCore.Proxy/ .마이크로소프트는 이 게시물에 언급된 다른 하나를 보관했으며, 저는 이 프로젝트에 대한 모든 문제에 대응할 계획입니다.
기본적으로 다른 웹 서버의 역방향 프록시를 사용하면 인수가 있는 경로를 사용하고 프록시 주소를 계산하는 메서드에 속성을 사용할 수 있습니다.
[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}");
}
이 게시물은 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 new EmptyResult();
}
그리고 접근해보세요.google.com 에 프록시됩니다.
저는 Asp의 프로젝트에서 영감을 얻은 프록시 미들웨어를 구현하게 되었습니다.넷의 깃허브.
기본적으로 요청을 읽고, 요청에서 복사본을 생성하여 구성된 서비스로 다시 보내고, 서비스의 응답을 읽고, 호출자에게 다시 보내는 미들웨어를 구현합니다.
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의 AsNetCore를 사용한 운이 좋았습니다.프록시 NuGet 패키지를 사용할 수 없지만ProxyRoute
twitchax의 답변에 표시된 방법. (내 쪽에서는 쉽게 실수할 수 있었습니다.)
대신 아래 코드와 유사하게 Statup.cs Configure() 메서드에서 매핑을 정의했습니다.
app.UseProxy("api/someexternalapp-proxy/{arg1}", async (args) =>
{
string url = "https://someexternalapp.com/" + args["arg1"];
return await Task.FromResult<string>(url);
});
twitchax Proxy 속성이 작동하도록 하기 위해 James Lawruk의 답변 https://stackoverflow.com/a/54149906/6596451 을 피기백하여 ProxyRoute 속성에 전체 경로를 지정하기 전까지 404 오류도 발생했습니다.정적 경로를 별도의 컨트롤러에 두고 컨트롤러 경로의 상대 경로가 작동하지 않았습니다.
이것은 효과가 있었습니다.
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/");
}
}
이것이 누군가에게 도움이 되기를 바랍니다!
트위치악스의 답변이 현재로서는 최선의 해결책인 것 같습니다.이것을 조사하면서, 저는 마이크로소프트가 OP가 해결하려고 했던 정확한 문제에 맞는 더 강력한 솔루션을 개발하고 있다는 것을 알게 되었습니다.
담당자: https://github.com/microsoft/reverse-proxy
프리뷰 1의 기사 (실제로 그들은 이전 2를 막 발표했습니다): https://devblogs.microsoft.com/dotnet/introducing-yarp-preview-1/
기사에서...
YARP는 역방향 프록시 서버를 만드는 프로젝트입니다.마이크로소프트 내부 팀들이 역방향 프록시를 구축하거나, 역방향 프록시를 구축하기 위한 API와 기술에 대해 질문하는 패턴을 발견했을 때 시작되었습니다. 그래서 우리는 그들 모두가 YARP가 된 공통 솔루션을 함께 작업하기로 결정했습니다.
YARP는 ASP.NET 및 .NET의 인프라를 사용하여 .NET에서 고속 프록시 서버를 구축하기 위한 역방향 프록시 툴킷입니다.YARP의 주요 차별화 요소는 각 구축 시나리오의 특정 요구사항에 맞게 쉽게 사용자 지정하고 조정할 수 있도록 설계되었다는 점입니다.YARP는 들어오는 요청을 처리하기 위해 ASP.NET 파이프라인에 연결한 다음 요청을 백엔드 서버로 프록시하는 단계를 수행하기 위한 자체 하위 파이프라인을 가집니다.고객은 모듈을 추가하거나 필요에 따라 재고 모듈을 교체할 수 있습니다.
...
YARP는 .NET Core 3.1 또는 .NET 5 미리 보기 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 .
다음은 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();
}
}
}
언급URL : https://stackoverflow.com/questions/42000362/creating-a-proxy-to-another-web-api-with-asp-net-core
'programing' 카테고리의 다른 글
개체 배열에서 Mongoose / MongoDB $addToSet 기능 사용 (0) | 2023.07.13 |
---|---|
행이 없는 경우 Oracle 삽입 (0) | 2023.07.13 |
VueActions에서 약속을 데이지 체인으로 연결하면 무한 루프가 발생함 (0) | 2023.07.13 |
Oracle에서 보기를 참조하는 외부 키 (0) | 2023.07.13 |
그라들 사용법.자바와 그루비가 함께? (0) | 2023.07.13 |