Why does asp.net core's URL Rewriting middle ware does not match following regex?

Viewed 427

I am trying to rewrite simple url to other url in asp.net core 3 application. I have used the re-writer as follows

app.UseRewriter(new RewriteOptions().AddRewrite(@"^(?i)quote_request.aspx\?id=(.*)", "quote/revise/$1", skipRemainingRules: true));

But this is not matching and rewriting (mysite)/quote_request.aspx?id=123 to (mysite)/quote/revise/123

Am I missing something? Regex matches perfectly at https://dotnetfiddle.net/vk0ZVn though.

also,

app.UseRewriter(new RewriteOptions().AddRewrite(@"^(?i)quote_request.aspx(.*)", "quote/revise$1", skipRemainingRules: true));

is correctly rewriting (mysite)/quote_request.aspx?id=123 to (mysite)/quote/revise?id=123.

1 Answers

I had to look into code of RewriteRule in order to find answer. Here is fragment of ApplyRule method:

PathString path1 = context.HttpContext.Request.Path;
Match match = path1 != PathString.Empty ? this.InitialMatch.Match(path1.ToString().Substring(1)) : this.InitialMatch.Match(path1.ToString());
if (!match.Success)
    return;

context.HttpContext.Request.Path returns only /quote_request.aspx for request to quote_request.aspx?id=123 while missing part is in context.HttpContext.Request.QueryString! So it just ignores part of url after ? and doesn't apply rule even if passed regex is correct. So in order to do avoid skipping query you have to write custom IRule based on existing RewriteRule class, here is simplified example:

using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite;

public class CustomRewriteRule : IRule
{
    public Regex InitialMatch { get; }

    public string Replacement { get; }

    public bool StopProcessing { get; }

    public CustomRewriteRule(string regex, string replacement, bool stopProcessing)
    {
        InitialMatch = new Regex(regex, RegexOptions.Compiled | RegexOptions.CultureInvariant);
        Replacement = replacement;
        StopProcessing = stopProcessing;
    }

    public virtual void ApplyRule(RewriteContext context)
    {
        var fullPath = context.HttpContext.Request.Path + context.HttpContext.Request.QueryString;
        var match = fullPath != string.Empty ? InitialMatch.Match(fullPath.Substring(1)) : InitialMatch.Match(fullPath);
        if (!match.Success)
            return;

        var str = match.Result(this.Replacement);
        var request = context.HttpContext.Request;
        if (StopProcessing)
            context.Result = RuleResult.SkipRemainingRules;

        request.Path = str[0] != '/' ? PathString.FromUriComponent("/" + str) : PathString.FromUriComponent(str);
        request.QueryString = QueryString.Empty;
    }
}

Usage:

var options = new RewriteOptions().Add(new CustomRewriteRule(@"^(?i)quote_request.aspx\?id=(.*)", "quote/revise/$1", true));
app.UseRewriter(options);
Related