File: UrlActions\RedirectAction.cs
Web Access
Project: src\src\Middleware\Rewrite\src\Microsoft.AspNetCore.Rewrite.csproj (Microsoft.AspNetCore.Rewrite)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.AspNetCore.Http;
 
namespace Microsoft.AspNetCore.Rewrite.UrlActions;
 
internal sealed class RedirectAction : UrlAction
{
    public int StatusCode { get; }
    public bool QueryStringAppend { get; }
    public bool QueryStringDelete { get; }
    public bool EscapeBackReferences { get; }
 
    public RedirectAction(
        int statusCode,
        Pattern pattern,
        bool queryStringAppend,
        bool queryStringDelete,
        bool escapeBackReferences)
    {
        StatusCode = statusCode;
        Url = pattern;
        QueryStringAppend = queryStringAppend;
        QueryStringDelete = queryStringDelete;
        EscapeBackReferences = escapeBackReferences;
    }
 
    public override void ApplyAction(RewriteContext context, BackReferenceCollection? ruleBackReferences, BackReferenceCollection? conditionBackReferences)
    {
        var pattern = Url!.Evaluate(context, ruleBackReferences, conditionBackReferences);
        var response = context.HttpContext.Response;
        var pathBase = context.HttpContext.Request.PathBase;
        if (EscapeBackReferences)
        {
            // because escapebackreferences will be encapsulated by the pattern, just escape the pattern
            pattern = Uri.EscapeDataString(pattern);
        }
 
        if (string.IsNullOrEmpty(pattern))
        {
            response.Headers.Location = pathBase.HasValue ? pathBase.Value : "/";
            return;
        }
 
        if (!pattern.Contains(Uri.SchemeDelimiter, StringComparison.Ordinal) && pattern[0] != '/')
        {
            pattern = '/' + pattern;
        }
        response.StatusCode = StatusCode;
 
        // url can either contain the full url or the path and query
        // always add to location header.
        // TODO check for false positives
 
        var split = pattern.IndexOf('?');
        if (split >= 0 && QueryStringAppend)
        {
            var query = context.HttpContext.Request.QueryString.Add(
                QueryString.FromUriComponent(
                    pattern.Substring(split)));
 
            // not using the response.redirect here because status codes may be 301, 302, 307, 308
            response.Headers.Location = pathBase + pattern.Substring(0, split) + query;
        }
        else
        {
            // If the request url has a query string and the target does not, append the query string
            // by default.
            if (QueryStringDelete)
            {
                response.Headers.Location = pathBase + pattern;
            }
            else
            {
                response.Headers.Location = pathBase + pattern + context.HttpContext.Request.QueryString;
            }
        }
        context.Result = RuleResult.EndResponse;
    }
}