diff --git a/samples/RewriteSample/Startup.cs b/samples/RewriteSample/Startup.cs index ea80638c30..85918d2459 100644 --- a/samples/RewriteSample/Startup.cs +++ b/samples/RewriteSample/Startup.cs @@ -27,7 +27,7 @@ namespace RewriteSample // TODO make this startup do something useful. app.UseRewriter(new RewriteOptions() - .Rewrite(@"foo/(\d+)", "foo?id={R:1}") + .Rewrite(@"foo/(\d+)", "foo?id=$1") .ImportFromUrlRewrite(hostingEnv, "UrlRewrite.xml") .ImportFromModRewrite(hostingEnv, "Rewrite.txt")); diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RedirectRule.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RedirectRule.cs new file mode 100644 index 0000000000..7c33bd426f --- /dev/null +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RedirectRule.cs @@ -0,0 +1,66 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Http; +using Microsoft.Net.Http.Headers; + +namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules +{ + public class RedirectRule : Rule + { + private readonly TimeSpan _regexTimeout = TimeSpan.FromSeconds(1); + public Regex InitialMatch { get; } + public string Replacement { get; } + public int StatusCode { get; } + public RedirectRule(string regex, string replacement, int statusCode) + { + InitialMatch = new Regex(regex, RegexOptions.Compiled | RegexOptions.CultureInvariant, _regexTimeout); + Replacement = replacement; + StatusCode = statusCode; + } + + public override void ApplyRule(RewriteContext context) + { + var path = context.HttpContext.Request.Path; + Match initMatchResults; + if (path == PathString.Empty) + { + initMatchResults = InitialMatch.Match(path.ToString()); + } + else + { + initMatchResults = InitialMatch.Match(path.ToString().Substring(1)); + } + + if (initMatchResults.Success) + { + var newPath = initMatchResults.Result(Replacement); + var response = context.HttpContext.Response; + response.StatusCode = StatusCode; + + if (newPath.IndexOf("://", StringComparison.Ordinal) == -1 && !newPath.StartsWith("/")) + { + newPath = '/' + newPath; + } + + var split = newPath.IndexOf('?'); + if (split >= 0) + { + var query = context.HttpContext.Request.QueryString.Add( + QueryString.FromUriComponent( + newPath.Substring(split))); + // not using the HttpContext.Response.redirect here because status codes may be 301, 302, 307, 308 + response.Headers[HeaderNames.Location] = newPath.Substring(0, split) + query; + } + else + { + response.Headers[HeaderNames.Location] = newPath; + } + + context.Result = RuleTermination.ResponseComplete; + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RewriteRule.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RewriteRule.cs new file mode 100644 index 0000000000..4892a3d467 --- /dev/null +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RewriteRule.cs @@ -0,0 +1,94 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; + +namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules +{ + public class RewriteRule : Rule + { + private readonly string ForwardSlash = "/"; + private readonly TimeSpan _regexTimeout = TimeSpan.FromSeconds(1); + public Regex InitialMatch { get; } + public string Replacement { get; } + public bool StopProcessing { get; } + public RewriteRule(string regex, string replacement, bool stopProcessing) + { + InitialMatch = new Regex(regex, RegexOptions.Compiled | RegexOptions.CultureInvariant, _regexTimeout); + Replacement = replacement; + StopProcessing = stopProcessing; + } + + public override void ApplyRule(RewriteContext context) + { + var path = context.HttpContext.Request.Path; + Match initMatchResults; + if (path == PathString.Empty) + { + initMatchResults = InitialMatch.Match(path.ToString()); + } + else + { + initMatchResults = InitialMatch.Match(path.ToString().Substring(1)); + } + + if (initMatchResults.Success) + { + var result = initMatchResults.Result(Replacement); + var request = context.HttpContext.Request; + + if (result.IndexOf("://", StringComparison.Ordinal) >= 0) + { + string scheme; + HostString host; + PathString pathString; + QueryString query; + FragmentString fragment; + UriHelper.FromAbsolute(result, out scheme, out host, out pathString, out query, out fragment); + + request.Scheme = scheme; + request.Host = host; + request.Path = pathString; + request.QueryString = query.Add(request.QueryString); + } + else + { + var split = result.IndexOf('?'); + if (split >= 0) + { + var newPath = result.Substring(0, split); + if (newPath.StartsWith(ForwardSlash)) + { + request.Path = PathString.FromUriComponent(newPath); + } + else + { + request.Path = PathString.FromUriComponent(ForwardSlash + newPath); + } + request.QueryString = request.QueryString.Add( + QueryString.FromUriComponent( + result.Substring(split))); + } + else + { + if (result.StartsWith(ForwardSlash)) + { + request.Path = PathString.FromUriComponent(result); + } + else + { + request.Path = PathString.FromUriComponent(ForwardSlash + result); + } + } + } + if (StopProcessing) + { + context.Result = RuleTermination.StopRules; + } + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ModRewriteRedirectAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ModRewriteRedirectAction.cs deleted file mode 100644 index 6198b1a35e..0000000000 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ModRewriteRedirectAction.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using Microsoft.AspNetCore.Http; -using Microsoft.Net.Http.Headers; - -namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite -{ - public class ModRewriteRedirectAction : UrlAction - { - public int StatusCode { get; } - public bool QueryStringAppend { get; } - public bool QueryStringDelete { get; } - public bool EscapeBackReferences { get; } - - public ModRewriteRedirectAction( - 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, MatchResults ruleMatch, MatchResults condMatch) - { - var pattern = Url.Evaluate(context, ruleMatch, condMatch); - if (EscapeBackReferences) - { - // because escapebackreferences will be encapsulated by the pattern, just escape the pattern - pattern = Uri.EscapeDataString(pattern); - } - context.HttpContext.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 - context.HttpContext.Response.Headers[HeaderNames.Location] = 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) - { - context.HttpContext.Response.Headers[HeaderNames.Location] = pattern; - } - else - { - context.HttpContext.Response.Headers[HeaderNames.Location] = pattern + context.HttpContext.Request.QueryString; - } - } - context.Result = RuleTermination.ResponseComplete; - } - } -} diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ModRewriteRewriteAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ModRewriteRewriteAction.cs deleted file mode 100644 index 1186f3f8f9..0000000000 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ModRewriteRewriteAction.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Extensions; - -namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite -{ - public class ModRewriteRewriteAction : UrlAction - { - private readonly string ForwardSlash = "/"; - public RuleTermination Result { get; } - public bool QueryStringAppend { get; } - public bool QueryStringDelete { get; } - public bool EscapeBackReferences { get; } - - public ModRewriteRewriteAction( - RuleTermination result, - Pattern pattern, - bool queryStringAppend, - bool queryStringDelete, - bool escapeBackReferences) - { - Result = result; - Url = pattern; - QueryStringAppend = queryStringAppend; - QueryStringDelete = queryStringDelete; - EscapeBackReferences = escapeBackReferences; - } - - public override void ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) - { - var pattern = Url.Evaluate(context, ruleMatch, condMatch); - - // TODO PERF, substrings, object creation, etc. - if (pattern.IndexOf("://", StringComparison.Ordinal) >= 0) - { - string scheme; - HostString host; - PathString path; - QueryString query; - FragmentString fragment; - UriHelper.FromAbsolute(pattern, out scheme, out host, out path, out query, out fragment); - - if (query.HasValue) - { - if (QueryStringAppend) - { - context.HttpContext.Request.QueryString = context.HttpContext.Request.QueryString.Add(query); - } - else - { - context.HttpContext.Request.QueryString = query; - } - } - else if (QueryStringDelete) - { - context.HttpContext.Request.QueryString = QueryString.Empty; - } - - context.HttpContext.Request.Scheme = scheme; - context.HttpContext.Request.Host = host; - context.HttpContext.Request.Path = path; - } - else - { - var split = pattern.IndexOf('?'); - if (split >= 0) - { - var path = pattern.Substring(0, split); - if (path.StartsWith(ForwardSlash)) - { - context.HttpContext.Request.Path = PathString.FromUriComponent(path); - } - else - { - context.HttpContext.Request.Path = PathString.FromUriComponent(ForwardSlash + path); - } - - if (QueryStringAppend) - { - context.HttpContext.Request.QueryString = context.HttpContext.Request.QueryString.Add( - QueryString.FromUriComponent( - pattern.Substring(split))); - } - else - { - context.HttpContext.Request.QueryString = QueryString.FromUriComponent( - pattern.Substring(split)); - } - } - else - { - if (pattern.StartsWith(ForwardSlash)) - { - context.HttpContext.Request.Path = PathString.FromUriComponent(pattern); - } - else - { - context.HttpContext.Request.Path = PathString.FromUriComponent(ForwardSlash + pattern); - } - - if (QueryStringDelete) - { - context.HttpContext.Request.QueryString = QueryString.Empty; - } - } - } - context.Result = Result; - } - } -} diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleBuilder.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleBuilder.cs index be83b1f76e..8d578feed8 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleBuilder.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleBuilder.cs @@ -210,13 +210,13 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite { throw new FormatException(Resources.FormatError_InputParserInvalidInteger(statusCode, -1)); } - _action = new ModRewriteRedirectAction(res, pattern, queryStringAppend, queryStringDelete, escapeBackReference); + _action = new RedirectAction(res, pattern, queryStringAppend, queryStringDelete, escapeBackReference); } else { var last = flags.HasFlag(FlagType.End) || flags.HasFlag(FlagType.Last); var termination = last ? RuleTermination.StopRules : RuleTermination.Continue; - _action = new ModRewriteRewriteAction(termination, pattern, queryStringAppend, queryStringDelete, escapeBackReference); + _action = new RewriteAction(termination, pattern, queryStringAppend, queryStringDelete, escapeBackReference); } } } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectAction.cs index f73baad942..ef616c16b5 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectAction.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectAction.cs @@ -2,7 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions @@ -10,41 +12,80 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions public class RedirectAction : UrlAction { public int StatusCode { get; } - public bool AppendQueryString { get; } + public bool QueryStringAppend { get; } + public bool QueryStringDelete { get; } + public bool EscapeBackReferences { get; } - public RedirectAction(int statusCode, Pattern pattern, bool appendQueryString) + public RedirectAction( + int statusCode, + Pattern pattern, + bool queryStringAppend, + bool queryStringDelete, + bool escapeBackReferences) { StatusCode = statusCode; Url = pattern; - AppendQueryString = appendQueryString; + QueryStringAppend = queryStringAppend; + QueryStringDelete = queryStringDelete; + EscapeBackReferences = escapeBackReferences; + } + + public RedirectAction( + int statusCode, + Pattern pattern, + bool queryStringAppend) + : this( + statusCode, + pattern, + queryStringAppend, + queryStringDelete: true, + escapeBackReferences: false) + { + } public override void ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) { var pattern = Url.Evaluate(context, ruleMatch, condMatch); - context.HttpContext.Response.StatusCode = StatusCode; + var response = context.HttpContext.Response; + if (EscapeBackReferences) + { + // because escapebackreferences will be encapsulated by the pattern, just escape the pattern + pattern = Uri.EscapeDataString(pattern); + } - // TODO IIS guarantees that there will be a leading slash if (pattern.IndexOf("://", StringComparison.Ordinal) == -1 && !pattern.StartsWith("/")) { 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 && AppendQueryString) + if (split >= 0 && QueryStringAppend) { var query = context.HttpContext.Request.QueryString.Add( QueryString.FromUriComponent( pattern.Substring(split))); - // not using the HttpContext.Response.redirect here because status codes may be 301, 302, 307, 308 - context.HttpContext.Response.Headers[HeaderNames.Location] = pattern.Substring(0, split) + query; + + // not using the response.redirect here because status codes may be 301, 302, 307, 308 + response.Headers[HeaderNames.Location] = pattern.Substring(0, split) + query; } else { - context.HttpContext.Response.Headers[HeaderNames.Location] = pattern; + // If the request url has a query string and the target does not, append the query string + // by default. + if (QueryStringDelete) + { + response.Headers[HeaderNames.Location] = pattern; + } + else + { + response.Headers[HeaderNames.Location] = pattern + context.HttpContext.Request.QueryString; + } } context.Result = RuleTermination.ResponseComplete; } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RewriteAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RewriteAction.cs index 13683a875f..c95bf909a6 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RewriteAction.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RewriteAction.cs @@ -11,24 +11,48 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions { private readonly string ForwardSlash = "/"; public RuleTermination Result { get; } - public bool ClearQuery { get; } + public bool QueryStringAppend { get; } + public bool QueryStringDelete { get; } + public bool EscapeBackReferences { get; } - public RewriteAction(RuleTermination result, Pattern pattern, bool clearQuery) + public RewriteAction( + RuleTermination result, + Pattern pattern, + bool queryStringAppend, + bool queryStringDelete, + bool escapeBackReferences) { Result = result; Url = pattern; - ClearQuery = clearQuery; + QueryStringAppend = queryStringAppend; + QueryStringDelete = queryStringDelete; + EscapeBackReferences = escapeBackReferences; + } + + public RewriteAction( + RuleTermination result, + Pattern pattern, + bool queryStringAppend): + this (result, + pattern, + queryStringAppend, + queryStringDelete: false, + escapeBackReferences: false) + { + } public override void ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) { var pattern = Url.Evaluate(context, ruleMatch, condMatch); - - if (ClearQuery) + var request = context.HttpContext.Request; + if (EscapeBackReferences) { - context.HttpContext.Request.QueryString = QueryString.Empty; + // because escapebackreferences will be encapsulated by the pattern, just escape the pattern + pattern = Uri.EscapeDataString(pattern); } + // TODO PERF, substrings, object creation, etc. if (pattern.IndexOf("://", StringComparison.Ordinal) >= 0) { string scheme; @@ -38,10 +62,25 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions FragmentString fragment; UriHelper.FromAbsolute(pattern, out scheme, out host, out path, out query, out fragment); - context.HttpContext.Request.Scheme = scheme; - context.HttpContext.Request.Host = host; - context.HttpContext.Request.Path = path; - context.HttpContext.Request.QueryString = query.Add(context.HttpContext.Request.QueryString); + if (query.HasValue) + { + if (QueryStringAppend) + { + request.QueryString = request.QueryString.Add(query); + } + else + { + request.QueryString = query; + } + } + else if (QueryStringDelete) + { + request.QueryString = QueryString.Empty; + } + + request.Scheme = scheme; + request.Host = host; + request.Path = path; } else { @@ -51,25 +90,39 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions var path = pattern.Substring(0, split); if (path.StartsWith(ForwardSlash)) { - context.HttpContext.Request.Path = PathString.FromUriComponent(path); + request.Path = PathString.FromUriComponent(path); } else { - context.HttpContext.Request.Path = PathString.FromUriComponent(ForwardSlash + path); + request.Path = PathString.FromUriComponent(ForwardSlash + path); + } + + if (QueryStringAppend) + { + request.QueryString = request.QueryString.Add( + QueryString.FromUriComponent( + pattern.Substring(split))); + } + else + { + request.QueryString = QueryString.FromUriComponent( + pattern.Substring(split)); } - context.HttpContext.Request.QueryString = context.HttpContext.Request.QueryString.Add( - QueryString.FromUriComponent( - pattern.Substring(split))); } else { if (pattern.StartsWith(ForwardSlash)) { - context.HttpContext.Request.Path = PathString.FromUriComponent(pattern); + request.Path = PathString.FromUriComponent(pattern); } else { - context.HttpContext.Request.Path = PathString.FromUriComponent(ForwardSlash + pattern); + request.Path = PathString.FromUriComponent(ForwardSlash + pattern); + } + + if (QueryStringDelete) + { + request.QueryString = QueryString.Empty; } } } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlRewriteRuleBuilder.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlRewriteRuleBuilder.cs index bb8f2e8e7c..38d942912c 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlRewriteRuleBuilder.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlRewriteRuleBuilder.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite; using Microsoft.AspNetCore.Rewrite.Internal.UrlActions; using Microsoft.AspNetCore.Rewrite.Internal.UrlMatches; @@ -46,7 +47,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite break; case ActionType.Rewrite: _action = new RewriteAction(stopProcessing ? RuleTermination.StopRules : RuleTermination.Continue, - url, clearQuery: !appendQueryString); + url, appendQueryString); break; case ActionType.Redirect: _action = new RedirectAction(statusCode, url, appendQueryString); diff --git a/src/Microsoft.AspNetCore.Rewrite/RewriteOptionsExtensions.cs b/src/Microsoft.AspNetCore.Rewrite/RewriteOptionsExtensions.cs index e0cf65479f..76c4a1e791 100644 --- a/src/Microsoft.AspNetCore.Rewrite/RewriteOptionsExtensions.cs +++ b/src/Microsoft.AspNetCore.Rewrite/RewriteOptionsExtensions.cs @@ -42,11 +42,11 @@ namespace Microsoft.AspNetCore.Rewrite /// /// The Rewrite options. /// The regex string to compare with. - /// If the regex matches, what to replace HttpContext with. + /// If the regex matches, what to replace HttpContext with. /// The Rewrite options. - public static RewriteOptions Rewrite(this RewriteOptions options, string regex, string urlPattern) + public static RewriteOptions Rewrite(this RewriteOptions options, string regex, string replacement) { - return Rewrite(options, regex, urlPattern, stopProcessing: false); + return Rewrite(options, regex, replacement, stopProcessing: false); } /// @@ -54,17 +54,12 @@ namespace Microsoft.AspNetCore.Rewrite /// /// The Rewrite options. /// The regex string to compare with. - /// If the regex matches, what to replace the uri with. + /// If the regex matches, what to replace the uri with. /// If the regex matches, conditionally stop processing other rules. /// The Rewrite options. - public static RewriteOptions Rewrite(this RewriteOptions options, string regex, string urlPattern, bool stopProcessing) + public static RewriteOptions Rewrite(this RewriteOptions options, string regex, string replacement, bool stopProcessing) { - var builder = new UrlRewriteRuleBuilder(); - var pattern = new InputParser().ParseInputString(urlPattern); - - builder.AddUrlMatch(regex); - builder.AddUrlAction(pattern, actionType: ActionType.Rewrite, stopProcessing: stopProcessing); - options.Rules.Add(builder.Build()); + options.Rules.Add(new RewriteRule(regex, replacement, stopProcessing)); return options; } @@ -73,11 +68,11 @@ namespace Microsoft.AspNetCore.Rewrite /// /// The Rewrite options. /// The regex string to compare with. - /// If the regex matches, what to replace the uri with. + /// If the regex matches, what to replace the uri with. /// The Rewrite options. - public static RewriteOptions Redirect(this RewriteOptions options, string regex, string urlPattern) + public static RewriteOptions Redirect(this RewriteOptions options, string regex, string replacement) { - return Redirect(options, regex, urlPattern, statusCode: 302); + return Redirect(options, regex, replacement, statusCode: 302); } /// @@ -85,21 +80,19 @@ namespace Microsoft.AspNetCore.Rewrite /// /// The Rewrite options. /// The regex string to compare with. - /// If the regex matches, what to replace the uri with. + /// If the regex matches, what to replace the uri with. /// The status code to add to the response. /// The Rewrite options. - public static RewriteOptions Redirect(this RewriteOptions options, string regex, string urlPattern, int statusCode) + public static RewriteOptions Redirect(this RewriteOptions options, string regex, string replacement, int statusCode) { - var builder = new UrlRewriteRuleBuilder(); - var pattern = new InputParser().ParseInputString(urlPattern); - - builder.AddUrlMatch(regex); - builder.AddUrlAction(pattern, actionType: ActionType.Redirect, stopProcessing: false); - options.Rules.Add(builder.Build()); + options.Rules.Add(new RedirectRule(regex, replacement, statusCode)); return options; } - // TODO 301 overload + public static RewriteOptions RedirectToHttpsPermanent(this RewriteOptions options) + { + return RedirectToHttps(options, statusCode: 301, sslPort: null); + } /// /// Redirect a request to https if the incoming request is http diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/CodeRules/MiddlewareTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/CodeRules/MiddlewareTests.cs index ce73843067..bb284b74a5 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/CodeRules/MiddlewareTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/CodeRules/MiddlewareTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.CodeRules [Fact] public async Task CheckRewritePath() { - var options = new RewriteOptions().Rewrite("(.*)", "http://example.com/{R:1}"); + var options = new RewriteOptions().Rewrite("(.*)", "http://example.com/$1"); var builder = new WebHostBuilder() .Configure(app => { @@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.CodeRules [Fact] public async Task CheckRedirectPath() { - var options = new RewriteOptions().Redirect("(.*)","http://example.com/{R:1}", statusCode: 301); + var options = new RewriteOptions().Redirect("(.*)","http://example.com/$1", statusCode: 301); var builder = new WebHostBuilder() .Configure(app => { diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/FileParserTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/FileParserTests.cs index 43a8bac189..4512d9328b 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/FileParserTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/FileParserTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Rewrite.Internal; +using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite; using Microsoft.AspNetCore.Rewrite.Internal.UrlActions; using Microsoft.AspNetCore.Rewrite.Internal.UrlMatches; using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite; @@ -146,7 +147,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite ) { return new UrlRewriteRule(name, new RegexMatch(new Regex("^OFF$"), false), conditions, - new RewriteAction(RuleTermination.Continue, new InputParser().ParseInputString(url), clearQuery: false)); + new RewriteAction(RuleTermination.Continue, new InputParser().ParseInputString(url), queryStringAppend: false)); } // TODO make rules comparable?