38 lines
2.5 KiB
C#
38 lines
2.5 KiB
C#
using System.Net;
|
|
using System.Net.Http.Headers;
|
|
|
|
namespace Kbx.Shared.Providers;
|
|
|
|
public sealed class KrxOpenApiAdapter(HttpClient http, IKbxProviderSecretStore secrets) : IKbxExternalProviderAdapter
|
|
{
|
|
public string ProviderId => "provider.krx.openapi";
|
|
|
|
public async Task<KbxProviderResult<string>> InvokeApprovedServiceAsync(KrxApprovedServiceRequest request, CancellationToken ct)
|
|
{
|
|
if (!request.ServiceUri.IsAbsoluteUri || request.ServiceUri.Scheme != Uri.UriSchemeHttps || !request.ServiceUri.Host.EndsWith(".krx.co.kr", StringComparison.OrdinalIgnoreCase))
|
|
return new(KbxProviderResultKind.PermanentFailure, Code:"KRX_SERVICE_URL_NOT_APPROVED", Message:"KRX 승인 서비스의 HTTPS URL만 사용할 수 있습니다.");
|
|
if (request.Method != HttpMethod.Get && request.Method != HttpMethod.Post)
|
|
return new(KbxProviderResultKind.PermanentFailure, Code:"KRX_METHOD_NOT_ALLOWED", Message:"조회성 GET/POST만 허용됩니다.");
|
|
|
|
var key = await secrets.GetRequiredAsync("ExternalProviders:Krx:AuthKey", ct);
|
|
var uri = AppendQuery(request.ServiceUri, request.Query);
|
|
using var message = new HttpRequestMessage(request.Method, uri);
|
|
message.Headers.TryAddWithoutValidation("AUTH_KEY", key);
|
|
if (request.Method == HttpMethod.Post && request.Body is not null)
|
|
message.Content = new StringContent(request.Body, System.Text.Encoding.UTF8, "application/json");
|
|
using var response = await http.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, ct);
|
|
var body = await response.Content.ReadAsStringAsync(ct);
|
|
if (response.IsSuccessStatusCode) return new(KbxProviderResultKind.Success, body, HttpStatus:(int)response.StatusCode);
|
|
var transient = response.StatusCode is HttpStatusCode.RequestTimeout or (HttpStatusCode)429 || (int)response.StatusCode >= 500;
|
|
return new(transient ? KbxProviderResultKind.TransientFailure : KbxProviderResultKind.PermanentFailure, Code:$"HTTP_{(int)response.StatusCode}", Message:"KRX OPEN API 호출이 실패했습니다.", HttpStatus:(int)response.StatusCode);
|
|
}
|
|
|
|
private static Uri AppendQuery(Uri uri, IReadOnlyDictionary<string,string?> query)
|
|
{
|
|
if (query.Count == 0) return uri;
|
|
var parts = query.Where(x => x.Value is not null).Select(x => $"{Uri.EscapeDataString(x.Key)}={Uri.EscapeDataString(x.Value!)}");
|
|
var builder = new UriBuilder(uri) { Query = string.Join("&", parts) };
|
|
return builder.Uri;
|
|
}
|
|
}
|