YouTubeApiKeyHandler.cs 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. using Application.Abstractions.Data;
  2. using Microsoft.EntityFrameworkCore;
  3. using Microsoft.Extensions.DependencyInjection;
  4. using Microsoft.Extensions.Logging;
  5. namespace Infrastructure.YouTube;
  6. /// <summary>
  7. /// YouTube Data API v3 요청에 API Key를 자동으로 추가하는 DelegatingHandler
  8. /// DB Config에서 YouTube API Key를 조회하여 쿼리스트링에 &amp;key=xxx 추가
  9. /// </summary>
  10. internal sealed class YouTubeApiKeyHandler(
  11. IServiceProvider serviceProvider,
  12. ILogger<YouTubeApiKeyHandler> logger
  13. ) : DelegatingHandler
  14. {
  15. private string? _cachedApiKey;
  16. private DateTime _cachedAt = DateTime.MinValue;
  17. private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(30);
  18. protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  19. {
  20. var apiKey = await GetApiKeyAsync(cancellationToken);
  21. if (!string.IsNullOrEmpty(apiKey) && request.RequestUri is not null)
  22. {
  23. // 이미 Authorization 헤더(OAuth)가 있으면 API Key 추가 안 함
  24. if (request.Headers.Authorization is null)
  25. {
  26. var uriStr = request.RequestUri.ToString();
  27. var separator = uriStr.Contains('?') ? "&" : "?";
  28. request.RequestUri = new Uri($"{uriStr}{separator}key={Uri.EscapeDataString(apiKey)}");
  29. }
  30. }
  31. else
  32. {
  33. logger.LogWarning("[YouTube] API Key가 비어있습니다. DB Config > External > YouTubeApiKeyEnc를 확인하세요.");
  34. }
  35. return await base.SendAsync(request, cancellationToken);
  36. }
  37. private async Task<string?> GetApiKeyAsync(CancellationToken ct)
  38. {
  39. if (_cachedApiKey is not null && DateTime.UtcNow - _cachedAt < CacheDuration)
  40. {
  41. return _cachedApiKey;
  42. }
  43. try
  44. {
  45. using var scope = serviceProvider.CreateScope();
  46. var db = scope.ServiceProvider.GetRequiredService<IAppDbContext>();
  47. var config = await db.Config.AsNoTracking().FirstOrDefaultAsync(ct);
  48. if (config is null)
  49. {
  50. return null;
  51. }
  52. // Config.External.YouTubeApiKeyEnc에서 API Key 가져옴
  53. _cachedApiKey = config.External?.YouTubeApiKeyEnc;
  54. _cachedAt = DateTime.UtcNow;
  55. return _cachedApiKey;
  56. }
  57. catch (Exception ex)
  58. {
  59. logger.LogWarning(ex, "[YouTube] API Key 조회 실패");
  60. return _cachedApiKey; // 이전 캐시 반환
  61. }
  62. }
  63. }