Skip to content
This repository has been archived by the owner on Dec 20, 2018. It is now read-only.

Create URL Rewrite Middleware #43

Closed
jkotalik opened this issue Jun 21, 2016 · 25 comments
Closed

Create URL Rewrite Middleware #43

jkotalik opened this issue Jun 21, 2016 · 25 comments

Comments

@jkotalik
Copy link
Contributor

Objectives:

  • Add URL Rewrite for incoming PathStrings and QueryStrings
  • Support Regex expressions on input and based on match modify output
  • Add capture parameters within matching string to be used on modified output
  • Add URL Redirect which on match would terminate (Ex http to https)
@jkotalik
Copy link
Contributor Author

Implementation Proposal:

Create a new builder, UrlRewriteBuilder. This builder would, for example, have a method:
RewritePath(string regexCompare, Func<UrlRewriteContext, Task> onMatch)
which would add a rule for the HttpRequest's path string. The UrlRewriteContext will be used to store any information that was captured in the regex matching. When a HttpRequest comes in, if there is a regex match on the input's path string, the onMatch function would be called.

Users can provided their own onMatch function and have access to the UrlRewriteContext. We will also provide overloads/ methods to do certain tasks (Ex overloading RewritePath to take in two strings, with a regex string to convert the matched string appropriately).

@Cowlephant
Copy link

So I'm guessing with several months away for 1.1.0 release, we should be looking into one of the expressed alternative fixes?

@Tratcher
Copy link
Member

Tratcher commented Jul 5, 2016

Yes, it will be a few weeks before there's a working nightly build for this.

@jkotalik
Copy link
Contributor Author

Use Cases and Extensibility :

  • Redirecting from http to https
  • Rewriting Path
  • Redirecting Path
  • Rewrite Query String
  • Check existence of file/ directories
  • Change any request field

@DaveSlinn
Copy link

This is not directly related, but I am currently using web.config url rewrite for my .net core web app and it works, but i want to use web.config transformations to add url rewrite entry for release build and publish and that doesn't seem to be working with .net core. Do .config transforms not work anymore with dotnet.exe publish?

@Tratcher
Copy link
Member

No, config transforms are not supported.

@moozzyk
Copy link

moozzyk commented Jul 13, 2016

You could actually try this: aspnet/IISIntegration#146 (comment)

@DaveSlinn
Copy link

Thanks for the suggestion - managed to use nil4's dotnet xdt package integrated into our build process (angular 2 app building on-premises and publishing to azure) - and is working fine. I'll keep an eye on this WIP in the coming weeks and see if it makes sense to transition over to UrlRewriteBuilder when it becomes available.

@wpostma
Copy link

wpostma commented Jul 15, 2016

What is the plan for dotnet 1.1 with regards to URL rewrite and redirect support while deploying to IIS?

  1. Will IIS redirection rules configured from within the IIS manager with URL Rewrite extension installed, be supported? Currently it looks like in rtm, when I try to configure a redirect rule like this, from within IIS, it results in an HTTP 500.19 error, either Cannot add duplicate collection entry of type 'add' with unique key attribute 'name' set to 'aspNetCore', or that it can't find web.config? Sample IIS rewrite rule, which I want to have rewrite http://localhost/api/v3/worklist/xyz?a=3 as http://localhost/api/Worklist/xyz?a=3
<system.webServer>
  <rewrite>
    <globalRules>
      <rule name="api/v3/worklist">
        <match url="^api/v3/worklist/(.*)" />
        <conditions>
        </conditions>
        <serverVariables>
        </serverVariables>
        <action type="Rewrite" url="api/Worklist/{R:1}" />
      </rule>
    </globalRules>
  </rewrite>
</system.webServer>

  1. Is web.config also going to allow redirect and rewrite?

@jkotalik
Copy link
Contributor Author

jkotalik commented Jul 15, 2016

1: With regards to the error, it seems that the error in not related at all to URL rewrite. Try compiling the project again without the rewrite section at all, and see if you get the same error. I think you most likely have your web.config defined twice. Ex:

<?xml version="1.0"?>
<configuration>
  <system.webServer>
    <handlers>
      <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
    </handlers>
    <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
  </system.webServer>
</configuration>

The handlers section should only be in your project once.

2: web.config will still support IIS UrlRewrite which will be independent from and will run before the AspNetCore UrlRewrite. Therefore, both redirects and rewrites are still supported.

@wpostma
Copy link

wpostma commented Jul 15, 2016

Okay. It Works! Yay! In case this helps anybody else, here's what ought to have been obvious to me:

  1. I should have created the Rewrite URL rules INSIDE the application.
  2. Start with an EMPTY IIS configuration (delete your old junk) on your first developer machine attempt.
  3. Create a new IIS site, which will have its own newly created app pool. Set that app pool to use "No Managed Code".
  4. Inside the app pool, create applications.
  5. Create URL Rewrite rules in the APPLICATION node level in IIS Manager, not the MACHINE node level.

@jkotalik
Copy link
Contributor Author

jkotalik commented Jul 20, 2016

A few talking points we have to go over for the mod_rewrite implementation:
@DamianEdwards @davidfowl @Tratcher

  • If mod_rewrite rules are in the main configuration file, the whole url is rewritten. If they are in the .htaccess file, only the path from the current directory is rewritten. Currently this notion does not exist in my implementation (only paths), so we need to decide if we want to support full url rewriting and how to specify it is on the whole url.
  • If we do support full url rewriting, it means we will need to recreate the full url from the HttpContext. On top of that, we need to then translate the rewritten url back into the HttpContext, if it changed. Possible solutions would be a slight API change, adding two extension methods to obtain the url from the context and another to modify the context from a url. Else I can manually add methods to do the same specifically for rewriting, if adding urls to the HttpContext is bad practice.
  • Apache made a regex engine change that cause Apache V1 require the leading slash for rules while Apache 2 forbids it. I'm assuming we should support the latter.

@stodolos
Copy link

Need help! I couldn't put together exactly what to put in the base web.config vs. the wwwroot\web.config. This is what my wwwroot\web.config looked like in RC1 Update 1:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="OPTIONSVerbHandler" />
      <remove name="TRACEVerbHandler" />
      <add name="httpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified"/>
    </handlers>
    <!-- These caused issues in dnx46/x64 -->
    <!--<modules runAllManagedModulesForAllRequests="true" />-->
    <rewrite>
      <rules>
        <!--Redirect selected traffic to index -->
        <rule name="Index Rule" stopProcessing="true">
          <match url=".*" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_URI}" matchType="Pattern" pattern="^/api/" negate="true" />
          </conditions>
          <action type="Rewrite" url="/index.html" />
        </rule>
      </rules>
    </rewrite>
    <httpPlatform
      processPath="%DNX_PATH%"
      arguments="%DNX_ARGS%"
      stdoutLogEnabled="false"
      startupTimeLimit="3600"/>
  </system.webServer>
</configuration>

How do I keep this same behavior in ASP.NET Core 1.0?

@jkotalik
Copy link
Contributor Author

@stodolos The goal of this middleware is to allow people to transfer their rewrite rules from IIS and other sources to the ASP.NET Core middleware model. So the section:

 <rewrite>
      <rules>
           ...

will now be moved to a separate file that will be imported and parsed. The reason we can't just use the wwwroot or web.config is file checks simply will not work with Core.

The middleware for IIS Rewrite is functional, however still in a volatile state (APIs may/will change). This feature should be in a complete state in 2-3 weeks.

Regarding the other parts of web.config, @Tratcher ? Fairly sure the rest will go in wwwroot, but not sure.

@Tratcher
Copy link
Member

Web.config needs to go into your project root since RC2. We're also no longer using httpPlatformHandler, it's aspNetCore now. https://github.com/aspnet/IISIntegration/blob/dev/samples/IISSample/web.config#L9-L13

@stodolos
Copy link

stodolos commented Aug 16, 2016

Thanks @ZestyBread and @Tratcher. What's the best and proper way to make this code part of my solution against the 1.0? I noticed that this solution references the 1.1 versions of things like Microsoft.AspNetCore.Http.Extensions.

Thanks again in advance

@stodolos
Copy link

I've tried A LOT of different ways to get this to work, and even when it works fine locally, it fails in Azure.

Every time I hit a deep link in Azure, I receive the following:

The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.

However, even when browse to the site's root, and login (using 3rd party auth service), POSTs to my API controller return as 404s, even though it works fine in my local debug, and when I launch my app's EXE file from the command line on a local publish.

My latest attempt was to use a RouteBuilder to try and somewhat mimic what my old rewrites were doing. I just had all gets go to index.html, which works fine locally, but not in Azure.:

var routeBuilder = new RouteBuilder(app);

routeBuilder.MapGet("{*anything}", context =>
{
    context.Response.Redirect("/index.html");
    return Task.FromResult(0);
});

var routes = routeBuilder.Build();
app.UseRouter(routes);

@stodolos
Copy link

Update... I got it to work in Azure!

So I left in the route builder logic above, and I think the last culprit was the structure of the .xproj of my web application.

I noticed that when I started publishing to Azure, it would put ALL of the files inside of the wwwroot folder. So I would up with all the assemblies, etc. in the wwwroot folder, along with a child wwwroot folder, which had all of the files you'd expect in the parent wwwroot folder.

Seeing this, I compared a newly created ASP.NET Core 1.0 web application project file to my upgraded-several-times project file. Here are the key differences that I came across (and when I made them match the newer project format, magic took place):

  1. VSToolsPath
    -- Old:
    <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
    -- New:
    <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
  2. BaseIntermediateOutputPath
    -- Old:
    <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
    -- New:
    <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
  3. OutputPath
    -- Old:
    <OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
    -- New:
    <OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
  4. TargetFrameworkVersion
    -- Old: Didn't have it
    -- New:
    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
  5. DnxInvisibleContent
    -- Old: Didn't have it
    -- New:
<ItemGroup>
      <DnxInvisibleContent Include="bower.json" />
      <DnxInvisibleContent Include=".bowerrc" />
  </ItemGroup>
  1. targets
    -- Old:
    <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
    -- New:
    <Import Project="$(VSToolsPath)\DotNet.Web\Microsoft.DotNet.Web.targets" Condition="'$(VSToolsPath)' != ''" />

P.S. Sorry for clogging up this thread but hopefully this saves someone else out there.

@muratg muratg added enhancement and removed task labels Aug 16, 2016
@stodolos
Copy link

Wound up changing my routing to a middleware by following an example here.

@Flood
Copy link

Flood commented Sep 13, 2016

Will this middleware solve deep linking (i.e example.com/deep/link) for SPAs? I mean not to just redirect to startpage.

@wpostma
Copy link

wpostma commented Sep 13, 2016

Is this routing/middleware system only capable of redirecting within a single dotnet process, or can it redirect among multiple dotnet processes?

@villanus
Copy link

I saw you on the community standup.
Can we support NGinx rewite formats too???

@Tratcher
Copy link
Member

@villanus open a new bug for that please.

@sankritayayana
Copy link

any workaround or solution for issue. it's becoming a blocker for us.

@natemcmaster
Copy link
Contributor

@sankritayayana We haven't released the feature yet. Prerelease versions are available on our nightly feeds. (See https://github.com/aspnet/Home/wiki/NuGet-feeds)

@muratg muratg modified the milestones: 1.1.0, 1.1.0-preview1 Oct 12, 2016
@natemcmaster natemcmaster changed the title [WIP] Adding URL Rewrite Middleware Adding URL Rewrite Middleware Oct 17, 2016
@natemcmaster natemcmaster changed the title Adding URL Rewrite Middleware Create URL Rewrite Middleware Oct 17, 2016
@danroth27 danroth27 modified the milestones: 1.0.0-preview1, 1.1.0 Oct 24, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests