SitemapController.cs 4.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. using System.Text;
  2. using Microsoft.AspNetCore.Mvc;
  3. using economy.Controllers.Price.Global;
  4. namespace economy.Controllers
  5. {
  6. // sitemap.xml 생성
  7. //
  8. // 기존에는 wwwroot/sitemap.xml 정적 파일을 외부 생성기로 만들어 두었는데,
  9. // 같은 페이지의 쿼리스트링 변형(페이지네이션 41개, 조회기간/코드 파라미터 등)이 대부분을 차지해
  10. // 98건 중 실제 고유 페이지는 20여 개뿐이었고 lastmod 도 2025-08-10 에 고정되어 있었다.
  11. // 여기서는 정규 URL 만 내보낸다.
  12. public class SitemapController : Controller
  13. {
  14. // 리버스 프록시 뒤에서는 Request.Scheme 이 http 로 들어올 수 있어 설정값을 우선 사용한다.
  15. private const string DefaultBaseUrl = "https://economy.web.or.kr";
  16. // 파라미터가 없는 정규 경로만 담는다.
  17. // 제외 대상:
  18. // - Business/Search, Whois/Search : [HttpPost] 전용이라 GET 은 405
  19. // - Home/Error : 오류 페이지
  20. // - Price/Domestic/Item/Detail/{item} : 품목 코드가 외부 API 응답에서 오므로 목록을 고정할 수 없다.
  21. // Item/List 페이지의 링크를 통해 크롤링된다.
  22. private static readonly string[] StaticPaths =
  23. {
  24. "/",
  25. "/Home/Privacy",
  26. "/Business/Number",
  27. "/Days/Anniversary",
  28. "/Days/Holiday",
  29. "/Days/Seasonal",
  30. "/Days/Sundry",
  31. "/FIFA",
  32. "/Financial/Exchange",
  33. "/Financial/Interest",
  34. "/Financial/International",
  35. "/Lotto",
  36. "/Whois",
  37. "/Price/Domestic/Emission",
  38. "/Price/Domestic/Flower",
  39. "/Price/Domestic/Gold",
  40. "/Price/Domestic/Oil",
  41. "/Price/Domestic/Item/List.cshtml",
  42. "/Price/Global/NaturalGas"
  43. };
  44. private readonly IConfiguration _configuration;
  45. public SitemapController(IConfiguration configuration)
  46. {
  47. _configuration = configuration;
  48. }
  49. [HttpGet("/sitemap.xml")]
  50. [HttpHead("/sitemap.xml")]
  51. [ResponseCache(Duration = 3600, Location = ResponseCacheLocation.Any)]
  52. public IActionResult Index()
  53. {
  54. var baseUrl = (_configuration["Site:BaseUrl"] ?? DefaultBaseUrl).TrimEnd('/');
  55. // 시세 페이지는 매일 갱신되므로 날짜 단위로만 표기한다.
  56. var today = DateTime.UtcNow.ToString("yyyy-MM-dd");
  57. var builder = new StringBuilder();
  58. builder.AppendLine(@"<?xml version=""1.0"" encoding=""UTF-8""?>");
  59. builder.AppendLine(@"<urlset xmlns=""http://www.sitemaps.org/schemas/sitemap/0.9"">");
  60. foreach (var path in StaticPaths)
  61. {
  62. AppendUrl(builder, baseUrl + path, today, "daily", path == "/" ? "1.0" : "0.8");
  63. }
  64. // 국제 원자재 시세. 품목 목록은 GlobalController.CommodityMap 을 그대로 읽어
  65. // 라우트가 실제로 처리하는 값과 어긋나지 않게 한다.
  66. foreach (var commodity in GlobalController.CommodityMap.Keys.OrderBy(key => key, StringComparer.OrdinalIgnoreCase))
  67. {
  68. AppendUrl(builder, $"{baseUrl}/Price/Global/{commodity}", today, "daily", "0.8");
  69. }
  70. builder.AppendLine("</urlset>");
  71. return Content(builder.ToString(), "application/xml", Encoding.UTF8);
  72. }
  73. private static void AppendUrl(StringBuilder builder, string location, string lastModified, string changeFrequency, string priority)
  74. {
  75. builder.AppendLine(" <url>");
  76. builder.AppendLine($" <loc>{location}</loc>");
  77. builder.AppendLine($" <lastmod>{lastModified}</lastmod>");
  78. builder.AppendLine($" <changefreq>{changeFrequency}</changefreq>");
  79. builder.AppendLine($" <priority>{priority}</priority>");
  80. builder.AppendLine(" </url>");
  81. }
  82. }
  83. }