Skip to content

Service to recalculate endpoint to execute with endpoint routing #30549

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
brockallen opened this issue Mar 1, 2021 · 16 comments
Closed

Service to recalculate endpoint to execute with endpoint routing #30549

brockallen opened this issue Mar 1, 2021 · 16 comments
Assignees
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates feature-routing
Milestone

Comments

@brockallen
Copy link

Is there a way to cause endpoint routing to re-calculate the endpoint that is to be executed? Perhaps as a service in DI?

My Scenario:
I have a middleware that is often registered after UseRouting. During request processing I have the need to re-write the request so it changes the request path (e.g. from /foo to /bar), but once the next middleware is executed which eventually ends up at UseEndpoints we get a 404 due to the now mismatched path.

I'd love a service in DI to help with this that I can trigger from my middleware.

@mkArtakMSFT mkArtakMSFT added area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates feature-minimal-actions Controller-like actions for endpoint routing feature-routing and removed feature-minimal-actions Controller-like actions for endpoint routing labels Mar 1, 2021
@Tratcher
Copy link
Member

Tratcher commented Mar 1, 2021

https://github.com/dotnet/aspnetcore/blob/7bbaec1512682ab2a8f7d82440bc2bb428cc1857/src/Http/Routing/src/EndpointRoutingMiddleware.cs does use DI resolved services to match routes, but some of them are internal like MatcherFactory. It looks like it would be possible to wrap that logic in a public service.

@brockallen
Copy link
Author

It looks like it would be possible to wrap that logic in a public service.

That'd be much appreciated.

@mkArtakMSFT
Copy link
Contributor

@javiercn can you share some writeup about how this can be done, if it's easy? Thanks!

@mkArtakMSFT mkArtakMSFT added this to the Discussions milestone Mar 1, 2021
@davidfowl
Copy link
Member

@mkArtakMSFT I think we'd need to expose public API here...

@javiercn
Copy link
Member

javiercn commented Mar 4, 2021

My first suggestion would be to perform the url rewrite before app.UseRouting.

If for some reason that is not possible, I would try using a matcher policy to do it by:

  • Creating a dynamic endpoint that matches the original route.
  • Resolving to the destination endpoint.
  • Updating information on the request if necessary.

If you can provide a more concrete example I can probably offer more targeted guidance.

@brockallen
Copy link
Author

Well, it's part of IdentityServer's middleware. By default IdentityServer when it receives an authorization request and needs to render a response as HTML to post results back to a client it would normally render some hard coded HTML, but the customer might want their own (styling, branding, etc), so to achieve this I thought it'd be quite nice to re-execute in the pipeline to hit one of their endpoints (razor, MVC, whatever) to allow them to render the HTML.

@javiercn
Copy link
Member

javiercn commented Mar 4, 2021

@brockallen I see.

I think there might be a better way to do that. The way I would go about it is as follows:

  • Define an attribute for handling the specific UI endpoint you care about.
  • For people who want to override the UI, require them to annotate their endpoint with your metadata.
  • Mark your endpoint as dynamic (using DynamicMetadata).
  • Write a MatcherPolicy that applies to your endpoint, finds the target endpoint on the endpoint data source, (accessible through routing options) and replaces it with the target endpoint.

If all you care is about being able to override the emitted response, that's the best way to go about it.

@javiercn
Copy link
Member

javiercn commented Mar 4, 2021

Actually, it can be a bit simpler than what I'm suggesting. Assuming that you aren't using endpoint routing, in your middleware, when you hit the path, resolve the EndpointDataSource and search for an endpoint with your metadata on it (a controller with an attribute basically) and call context.SetEndpoint(endpoint) and that should do it.

Or check if the current selected endpoint has the metadata and noop in that case (let it override and handle the response) and produce a default otherwise.

@davidfowl
Copy link
Member

Right, this solution basically means your middleware can inspect the selected endpoint (if any) and check if it has the right metadata to override the response. It does mean that the user needs to register the route with the right information (path and metadata).

@javiercn
Copy link
Member

javiercn commented Mar 4, 2021

if("/my/original/path"){
  var endpoint = context.GetEndpoint();
  if(endpoint == null){
    // Execute default response logic
  }else{
    if(endpoint.Metadata.OfType<MyMetadata>().Any())
    {
      // let the endpoint override an execute
    }else
    {
      throw InvalidOperationException("Overriden by mistake.");
    }
  }
}

@brockallen
Copy link
Author

What if I don't know what endpoint they want to implement to supply this response processing? I was going to allow them to configure a path to rewrite the request to if they wanted to use this feature. If they did not, then we'd fall back to our default implementation. I think it would feel odd for them to to be forced to register an endpoint for our middleware's authorization path (i.e. ~/connect/authorize").

@javiercn
Copy link
Member

javiercn commented Mar 4, 2021

@brockallen in that case the best approach is to use a middleware to rewrite the path before app.UseRouting or trigger a redirect to the other page.

The problem with "recalculating" the route is that middleware before your middleware doesn't get to see the "real" endpoint.

If the target uses endpoint routing you can search for the endpoint in the EndpointDataSource and set the endpoint on the request (same problem as described above, middleware before it doesn't get to see the actual endpoint) and if you don't find an endpoint then you can probably just replace the path.

In my opinion, the most correct way to handle this is via a redirect or a rewrite before app.UseRouting

@brockallen
Copy link
Author

brockallen commented Mar 4, 2021

The problem with "recalculating" the route is that middleware before your middleware doesn't get to see the "real" endpoint.

My middleware doesn't care much about this custom endpoint, other than to know to execute it (which I was going to use a Path setting on our options). I really just want to allow the customer to use their existing skills to write some code in ASP.NET Core to emit some HTML.

@brockallen in that case the best approach is to use a middleware to rewrite the path before app.UseRouting or trigger a redirect to the other page.

Hmm... since I want the pit of success to allow customers to put our middleware before or after UseRouting I was hoping for something more flexible.

I could try this: In our middleware when I detect the conditions I need to do this re-writing, I'd use a forked pipeline. In the fork I would I will first call SetEndpoint to null to "undo" the prior endpoint middleware, then enter another instance of UseRouting middleware, and then after join back up to the regular/main pipeline. That would seem to have the same effect as an API I was asking for.

I guess in essence this means that in ASP.NET Core we can't really achieve the same thing as old-school ASP.NET's RewritePath or RemapHandler.

@davidfowl
Copy link
Member

RewritePath - Sure, if you change the order, its not like you could rewrite the path in EndRequest. You need to do it at the right phase and in this case, that's before routing.

RemapHandler - This is like setting the end point on a request.

@brockallen
Copy link
Author

The problem with "recalculating" the route is that middleware before your middleware doesn't get to see the "real" endpoint.

Perhaps my explanation wasn't clear enough on my use case or assumptions about the rewritten path. The original request path is what needs to be the logical endpoint for purposes of EP routing and all the features it brings. The the rewritten path is never meant to be seen by other middleware in the pipeline -- it's just an implementation detail of the processing of the original path and only sometimes does it need to then trigger this downstream rewritten path to generate the response.

@ghost
Copy link

ghost commented May 19, 2021

Thank you for contacting us. Due to a lack of activity on this discussion issue we're closing it in an effort to keep our backlog clean. If you believe there is a concern related to the ASP.NET Core framework, which hasn't been addressed yet, please file a new issue.

This issue will be locked after 30 more days of inactivity. If you still wish to discuss this subject after then, please create a new issue!

@ghost ghost closed this as completed May 19, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Jun 18, 2021
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates feature-routing
Projects
None yet
Development

No branches or pull requests

5 participants