aspnet / BasicMiddleware

[Archived] Basic middleware components for ASP.NET Core. Project moved to https://github.com/aspnet/AspNetCore
Apache License 2.0
169 stars 84 forks source link

GetDisplayUrl (and PathBase) showing rewritten URL instead of Request URL #269

Closed muratg closed 5 years ago

muratg commented 7 years ago

From @Eilon on June 5, 2017 16:22

From @DaveSlinn on May 19, 2017 23:40

When using IIS to perform a URL rewrite, is PathBase or GetDisplayUrl supposed to reflect the original Request, or the rewritten one?

I am using IIS URL rewrite to remove a segment of the path (i.e. remove '/shared/'), so instead of the user seeing "https://test.gms.ca/test01/api/shared/test", I want them to see "https://test.gms.ca/test01/api/test".

I have the rule setup in IIS and all seems well. The request is rewritten with an inbound rule and the controllers are responding. However, within the controller, I am using GetDisplayUrl thinking that would give me the original request, not the actual one where the application is hosted within IIS.

I have a test controller returning the request headers and some values off the HttpRequest object from within the controller. As you can see, PathBase is always showing the actual hosted path and GetDisplayUrl is merely reflecting that portion. I thought GetDisplayUrl is a method to get the same URL that the user is seeing. Am I misunderstanding what GetDisplayUrl is for?

I was thinking maybe I had to also use the ASP.NET Core Rewrite middleware to rewrite the url, but I'm not 100% sure I am doing it right. This is what I have:

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider serviceProvider)
        {
            var options = new RewriteOptions()
                .AddRewrite(@"(\d+)/api/shared/(\d+)", "$1/api/$2", false);

            app.UseRewriter(options);

            app.ConfigureCommonSettings(env, loggerFactory, Configuration, serviceProvider);
        }

Copied from original issue: aspnet/Mvc#6301

Copied from original issue: aspnet/HttpAbstractions#855

muratg commented 7 years ago

From @Eilon on June 5, 2017 16:22

@Tratcher / @mikaelm12 - any idea where this bug should go? HttpAbstractions or BasicMiddleware?

muratg commented 7 years ago

From @Eilon on June 5, 2017 16:22

From @Tratcher on May 20, 2017 11:53

By design. https://github.com/aspnet/HttpAbstractions/blob/dev/src/Microsoft.AspNetCore.Http.Extensions/UriHelper.cs Display refers to the escaping state, as compared to GetEncodedUrl. @mikalem12, does rewrite stash the original values anywhere?

muratg commented 7 years ago

From @Eilon on June 5, 2017 16:22

cc @mikaelm12 w/ proper spelling 😄

muratg commented 7 years ago

From @Eilon on June 5, 2017 16:22

From @mikaelm12 on May 22, 2017 17:37

does rewrite stash the original values anywhere?

No. There are server variables for that but we don't support them atm They are

HTTP_X_ORIGINAL_URL – this server variable contains the original URL in decoded format; UNENCODED_URL – this server variable contains the original URL exactly as it was requested by a Web client, with all original encoding preserved

There was a discussion on it a while ago here https://github.com/aspnet/BasicMiddleware/issues/151

muratg commented 7 years ago

From @Eilon on June 5, 2017 16:22

From @DaveSlinn on May 23, 2017 16:8

Is using the Rewrite middleware an option to override the PathBase value?

Perhaps I'm going about this the wrong way. I have 4 separate API projects, 1 that consists of general functions, the other 3 separated by their domain concerns. I want the URL for the common functions to appear at the top of the url and the domain specific ones to be lower, as in:

Given a Clients, Address and Countries controller in the general library (called common), I want those URLs to be:

https://api.domain.com/clients/12345 https://api.domain.com/addresses/12 https://api.domain.com/countries/12

And then I have several controllers related to administration tasks (called admin). I want those URLs to be:

https://api.domain.com/admin/policies/123456 https://api.domain.com/admin/applications/123456 https://api.domain.com/admin/quotes/123456

I tried hosting my projects in IIS with a structure that matched this hierarchy:

api.domain.com common.dll /admin/admin.dll

but I couldn't get this to work and assumed it was because the top level project (common.dll) was intercepting all requests including ones with admin in their URL and then trying to find a controller called "admin", which doesn't exist in that project, and returning a 404. The admin project (admin.dll) in a sub folder under common would then never see the request.

To work around this, I thought I would attempt to use URL rewrite, so that the projects would be hosted in side-by-side on IIS:

api.domain.dom /common/common.dll /admin/admin.dll

essentially I have the top level projects in a subfolder called common and then use URL rewrite to remove /common/ from the URL, but this is causing some other issues, such as when my code relies on GetDisplayUrl to build a URL for the response, it is not reflecting the rewritten URL, and I am also using Swashbuckle to create a Swagger UI, which also seems to reflect the actual hosted URL and not the rewritten one.

muratg commented 7 years ago

@Tratcher Is this the right repo for this bug?

muratg commented 7 years ago

From @Tratcher on August 9, 2017 21:58

but I couldn't get this to work and assumed it was because the top level project (common.dll) was intercepting all requests including ones with admin in their URL and then trying to find a controller called "admin", which doesn't exist in that project, and returning a 404. The admin project (admin.dll) in a sub folder under common would then never see the request.

In IIS you can't just use sub folders or virtual directories, you have to create sub applications. Once you do that it should route correctly based on the request path. Note the web.config for nested applications needs to be customized. https://github.com/aspnet/Docs/pull/3878/files

popraducj commented 6 years ago

Hi guys does this help?

Tratcher commented 6 years ago

The Headers dictionary is missing entries such as “Host”, presumably because it’s exposed a first-class property.

No? Host is in the header dictionary. The HttpRequest.Host property is a wrapper around the header dictionary.

In the case of Path, some encoded characters are decoded while others remain encoded.

Only %2F "/" should remain encoded. Decoding it would break routing.

Any discussion of RawTarget is incomplete without covering the caveats: https://github.com/aspnet/HttpAbstractions/blob/a6bdb9b1ec6ed99978a508e71a7f131be7e4d9fb/src/Microsoft.AspNetCore.Http.Features/IHttpRequestFeature.cs#L51-L59

In short, consuming raw user input is very dangerous, especially if you try to decode it yourself. That's why it's not exposed first class.

aspnet-hello commented 5 years ago

We periodically close 'discussion' issues that have not been updated in a long period of time.

We apologize if this causes any inconvenience. We ask that if you are still encountering an issue, please log a new issue with updated information and we will investigate.