将 asp.net 核心 2.0 网址重定向为小写

Redirect asp.net core 2.0 urls to lowercase

我看到您可以在 ASP.NET Core 2.0 中配置路由以生成小写 urls,如下所述:

使用这个:

services.AddRouting(options => options.LowercaseUrls = true);

然而,尽管生成 urls 很好,但它似乎没有做任何实际执行它们的事情,也就是说,重定向任何不全是小写的 urls到相应的小写 url(最好通过 301 重定向)。

我知道人们通过不同大小写的 url 访问我的网站,我希望他们都永久小写。

通过 RewriteOptions 和 Regex 进行标准重定向是唯一的方法吗?执行此操作的适当表达式是什么:

var options = new RewriteOptions().AddRedirect("???", "????");

或者有别的方法吗?

我很感激这已经有好几个月了,但是对于可能正在寻找相同解决方案的人,您可以添加一个复杂的重定向实现 IRule 例如:

public class RedirectLowerCaseRule : IRule
{
    public int StatusCode { get; } = (int)HttpStatusCode.MovedPermanently;

    public void ApplyRule(RewriteContext context)
    {
        HttpRequest request = context.HttpContext.Request;
        PathString path = context.HttpContext.Request.Path;
        HostString host = context.HttpContext.Request.Host;

        if (path.HasValue && path.Value.Any(char.IsUpper) || host.HasValue && host.Value.Any(char.IsUpper))
        {
            HttpResponse response = context.HttpContext.Response;
            response.StatusCode = StatusCode;
            response.Headers[HeaderNames.Location] = (request.Scheme + "://" + host.Value + request.PathBase + request.Path).ToLower() + request.QueryString;
            context.Result = RuleResult.EndResponse; 
        }
        else
        {
            context.Result = RuleResult.ContinueRules;
        } 
    }
}

然后可以在 Configure 方法下将其应用到您的 Startup.cs 中:

new RewriteOptions().Add(new RedirectLowerCaseRule());

希望对您有所帮助!

您确定要重定向吗?如果没有,并且您的目标是在您的主机和路径中没有大写这样的东西,您可以使用以下 IRule。这让我确信,无论我在管道中查看什么路径,它都是小写的。

public class RewriteLowerCaseRule : IRule
{
    public void ApplyRule(RewriteContext context)
    {
        var request = context.HttpContext.Request;
        var host = request.Host;
        var pathBase = request.PathBase;
        var path = request.Path;
        if (host.HasValue)
        {
            if (host.Port == null)
            {
                request.Host = new HostString(host.Host.ToLower());
            }
            else
            {
                request.Host = new HostString(host.Host.ToLower(), (int) host.Port);
            }
        }

        if (pathBase.HasValue)
        {
            request.PathBase = new PathString(pathBase.Value.ToLower());
        }

        if (path.HasValue)
        {
            request.Path = new PathString(path.Value.ToLower());
            request.PathBase = new PathString(pathBase.Value.ToLower());
        }

        context.Result = RuleResult.ContinueRules;
    }
}

用法:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseRewriter(new RewriteOptions().Add(new RewriteLowerCaseRule()));
    ...
}

添加为答案,因为我(还)不能发表评论。这是对 Ben Maxfields 回答的补充。

使用他的代码 http://www.example.org/Example/example 不会被重定向,因为 PathBase 没有检查大写字母(即使它用于构建新的小写 URI)。

所以根据他的代码,我最终使用了这个:

public class RedirectLowerCaseRule : IRule
{
    public int StatusCode { get; } = (int)HttpStatusCode.MovedPermanently;

    public void ApplyRule(RewriteContext context)
    {
        HttpRequest request = context.HttpContext.Request;
        PathString path = context.HttpContext.Request.Path;
        PathString pathbase = context.HttpContext.Request.PathBase;
        HostString host = context.HttpContext.Request.Host;

        if ((path.HasValue && path.Value.Any(char.IsUpper)) || (host.HasValue && host.Value.Any(char.IsUpper)) || (pathbase.HasValue && pathbase.Value.Any(char.IsUpper)))
        {
            Console.WriteLine("Redirect should happen");

            HttpResponse response = context.HttpContext.Response;
            response.StatusCode = StatusCode;
            response.Headers[HeaderNames.Location] = (request.Scheme + "://" + host.Value + request.PathBase + request.Path).ToLower() + request.QueryString;
            context.Result = RuleResult.EndResponse;
        }
        else
        {
            context.Result = RuleResult.ContinueRules;
        }
    }
}

实施方式略有不同,同样受到 this other thread 的启发。

public class RedirectLowerCaseRule : IRule
{
    public void ApplyRule(RewriteContext context)
    {
        HttpRequest request = context.HttpContext.Request;
        string url = request.Scheme + "://" + request.Host + request.PathBase + request.Path;
        bool isGet = request.Method.ToLowerInvariant().Contains("get");

        if ( isGet && url.Contains(".") == false && Regex.IsMatch(url, @"[A-Z]") )
        {
            HttpResponse response = context.HttpContext.Response;
            response.Clear();
            response.StatusCode = StatusCodes.Status301MovedPermanently;
            response.Headers[HeaderNames.Location] = url.ToLowerInvariant() + request.QueryString;
            context.Result = RuleResult.EndResponse;
        }
        else
        {
            context.Result = RuleResult.ContinueRules;
        }
    }
}

我认为有用的更改:
使用 ToLowerInvariant() 而不是 ToLower()(查看可能的问题 here
保留端口号。
绕过 GET 以外的请求方法。
用点绕过请求,假设像 js/css/images 等静态文件应该保留任何大写字母。
使用 Microsoft.AspNetCore.Http.StatusCodes.

我的两分钱...基于https://github.com/aspnet/AspNetCore/blob/master/src/Middleware/Rewrite/src/RedirectToWwwRule.cs

public class RedirectToLowercaseRule : IRule
{
    private readonly int _statusCode;

    public RedirectToLowercaseRule(int statusCode)
    {
        _statusCode = statusCode;
    }

    public void ApplyRule(RewriteContext context)
    {
        var req = context.HttpContext.Request;

        if (!req.Scheme.Any(char.IsUpper)
            && !req.Host.Value.Any(char.IsUpper)
            && !req.PathBase.Value.Any(char.IsUpper)
            && !req.Path.Value.Any(char.IsUpper))
        {
            context.Result = RuleResult.ContinueRules;
            return;
        }

        var newUrl = UriHelper.BuildAbsolute(req.Scheme.ToLowerInvariant(), new HostString(req.Host.Value.ToLowerInvariant()), req.PathBase.Value.ToLowerInvariant(), req.Path.Value.ToLowerInvariant(), req.QueryString);

        var response = context.HttpContext.Response;
        response.StatusCode = _statusCode;
        response.Headers[HeaderNames.Location] = newUrl;
        context.Result = RuleResult.EndResponse;
        context.Logger.RedirectedToLowercase();
    }
}

使用扩展方法:

public static class RewriteOptionsExtensions
{
    public static RewriteOptions AddRedirectToLowercase(this RewriteOptions options, int statusCode)
    {
        options.Add(new RedirectToLowercaseRule(statusCode));
        return options;
    }

    public static RewriteOptions AddRedirectToLowercase(this RewriteOptions options)
    {
        return AddRedirectToLowercase(options, StatusCodes.Status307TemporaryRedirect);
    }

    public static RewriteOptions AddRedirectToLowercasePermanent(this RewriteOptions options)
    {
        return AddRedirectToLowercase(options, StatusCodes.Status308PermanentRedirect);
    }
}

和日志记录:

internal static class MiddlewareLoggingExtensions
{
    private static readonly Action<ILogger, Exception> _redirectedToLowercase = LoggerMessage.Define(LogLevel.Information, new EventId(1, "RedirectedToLowercase"), "Request redirected to lowercase");

    public static void RedirectedToLowercase(this ILogger logger)
    {
        _redirectedToLowercase(logger, null);
    }
}

和用法:

app.UseRewriter(new RewriteOptions()
    .AddRedirectToLowercase());

其他考虑因素是状态代码的选择。我在扩展方法中使用了 307 和 308,因为它们可以防止在请求期间更改请求方法(例如,从 GET 到 POST),但是如果你想允许这种行为,你可以使用 301 和 302。请参阅 了解更多信息。