QrRapido/Middleware/LanguageRedirectionMiddleware.cs
Ricardo Carneiro 7dccfc10f0
Some checks failed
Deploy QR Rapido / test (push) Failing after 1m18s
Deploy QR Rapido / build-and-push (push) Has been skipped
Deploy QR Rapido / deploy-staging (push) Has been skipped
Deploy QR Rapido / deploy-production (push) Has been skipped
fix: ajustes de rota
2026-02-15 21:05:57 -03:00

147 lines
5.5 KiB
C#

using System.Globalization;
using System.Linq;
namespace QRRapidoApp.Middleware
{
/// <summary>
/// Middleware de redirecionamento de idioma otimizado para SEO.
///
/// Comportamento:
/// - "/" → Retorna 200 OK em Português (canonical)
/// - "/pt-BR" ou "/pt-BR/*" → Redireciona 301 para "/" ou "/*" (sem prefixo)
/// - "/es-PY" ou "/es-PY/*" → Retorna 200 OK em Espanhol (mantém URL)
/// - "/pix", "/wifi", etc. → Retorna 200 OK em Português (sem redirect)
/// </summary>
public class LanguageRedirectionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<LanguageRedirectionMiddleware> _logger;
private readonly string[] _supportedCultures = { "pt-BR", "es-PY" };
private const string DefaultCulture = "pt-BR";
private const string CookieName = ".AspNetCore.Culture";
public LanguageRedirectionMiddleware(RequestDelegate next, ILogger<LanguageRedirectionMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
var path = context.Request.Path.Value?.TrimStart('/') ?? "";
// Skip special routes (static files, API, auth callbacks, etc.)
if (IsSpecialRoute(path))
{
await _next(context);
return;
}
var segments = path.Split('/', StringSplitOptions.RemoveEmptyEntries);
var firstSegment = segments.Length > 0 ? segments[0] : "";
// Check if URL starts with a culture segment (supported or unsupported)
if (IsCultureLikeSegment(firstSegment))
{
// Supported: pt-BR (Default/Canonical)
if (string.Equals(firstSegment, "pt-BR", StringComparison.OrdinalIgnoreCase))
{
return RedirectToCanonical(context, segments);
}
// Supported: es-PY
if (string.Equals(firstSegment, "es-PY", StringComparison.OrdinalIgnoreCase))
{
SetCulture(context, "es-PY");
await _next(context);
return;
}
// Handle aliases or unsupported cultures (e.g. /en/, /pt/, /es/)
if (TryHandleCultureAliasOrUnknown(context, firstSegment, segments))
{
return;
}
}
// No culture prefix or unknown segment -> Serve in Portuguese (default)
SetCulture(context, DefaultCulture);
await _next(context);
}
private bool IsCultureLikeSegment(string segment)
{
if (string.IsNullOrEmpty(segment)) return false;
// Matches xx (like en, pt, es) or xx-XX (like pt-BR, en-US)
return segment.Length == 2 || (segment.Length == 5 && segment[2] == '-');
}
private bool TryHandleCultureAliasOrUnknown(HttpContext context, string firstSegment, string[] segments)
{
// Map known aliases to canonical forms
if (string.Equals(firstSegment, "es", StringComparison.OrdinalIgnoreCase) ||
string.Equals(firstSegment, "es-py", StringComparison.OrdinalIgnoreCase))
{
return RedirectToLanguage(context, "es-PY", segments);
}
// For anything else that looks like a culture (en, pt, fr, etc.)
// but isn't explicitly es-PY, we redirect to the canonical (no prefix)
// This prevents 404s for /en/, /pt-BR/ (redirects to /), etc.
return RedirectToCanonical(context, segments);
}
private bool RedirectToLanguage(HttpContext context, string culture, string[] segments)
{
var remainingPath = segments.Length > 1
? "/" + string.Join('/', segments.Skip(1))
: "";
var redirectUrl = "/" + culture + remainingPath;
return ExecuteRedirect(context, redirectUrl);
}
private bool RedirectToCanonical(HttpContext context, string[] segments)
{
var remainingPath = segments.Length > 1
? "/" + string.Join('/', segments.Skip(1))
: "/";
return ExecuteRedirect(context, remainingPath);
}
private bool ExecuteRedirect(HttpContext context, string url)
{
if (context.Request.QueryString.HasValue)
{
url += context.Request.QueryString.Value;
}
_logger.LogInformation("SEO Redirect: {Source} -> {Dest}", context.Request.Path, url);
context.Response.Redirect(url, permanent: true);
return true;
}
private void SetCulture(HttpContext context, string culture)
{
var cultureInfo = new CultureInfo(culture);
CultureInfo.CurrentCulture = cultureInfo;
CultureInfo.CurrentUICulture = cultureInfo;
context.Items["Culture"] = culture;
}
private bool IsSpecialRoute(string path)
{
var specialRoutes = new[]
{
"api/", "health", "_framework/", "lib/", "css/", "js/", "images/",
"favicon.ico", "robots.txt", "sitemap.xml", "ads.txt",
"signin-", "signout-", "Account/", "Pagamento/", "Home/Error"
};
return specialRoutes.Any(route => path.StartsWith(route, StringComparison.OrdinalIgnoreCase));
}
}
}