.NET Core (2.1) web API controller accepting all t

2019-07-30 14:25发布

问题:

What I have is a .NET Core 2.1 web API controller (in the following the TestController) that should generate redirects to other urls when receiving GET requests.

Example:

The controller is called like: http://localhost/api/v1/Test/somedir/somesubdir/filename.extension

and it should return a redirect to https://example-domain.com/somedir/somesubdir/filename.extension

My example controller for this question looks like:

  [Authorize]
  [Route("api/v1/[controller]")]
  public class TestController : ControllerBase
  {
    [HttpGet("{path}")]
    public async Task<IActionResult> Get(string path)
    {
      //path e.g. is somedir/somesubdir/filename.extension
      string prefix = "https://example-domain.com/api/v1/Other/";
      //string path2 = HttpContext.Request.Path.Value.Replace("/api/v1/Test/", "/api/v1/Other/").Replace("%2F", "/");
      return Redirect(prefix + path);
    }
  }

I don't get the routing to work. If I call the method with Swagger it get's called (with the slashes replaced by %2F) but at least it gets called. If I call the controller via postman .NET Core just returns 404 Not Found.

I do not necessarily need the HttpGet("{path}"). I know that I could get the path like I assigned the path2 variable.

Any hints how I could get the routing right?


Another possible solution:

As pointed out by John and Kirk Larkin in the comments what I was looking for is a catch-all route filling the path argument with "somedir/somesubdir/filename.extension" independent how long the path afterwards is.

Just an asterisk in front of the variable name does the trick.

[HttpGet("{*path}")]

回答1:

You don't need to take "api/v1/Test" into account as your code comments suggest, it is already filtered out by the [Route] attribute on the Controller level.

For the rest of the path that follows you can use {*path}:

[HttpGet("{*path}")]
public async Task<IActionResult> Get(string path)
{
    const string prefix = "https://example-domain.com/api/v1/Other/";
    return Redirect(prefix + path);
}


回答2:

@john, his solution is wonderful: [HttpGet("{*path}")], just tested. but I want to keep my answer for feature use as an option:

as another option, you can follow MSDN [Catch-all route]: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-2.1

routes.MapRoute(
  name: "blog",
  template: "Blog/{*article}", //<==
  defaults: new { controller = "Blog", action = "ReadArticle" });

This template will match a URL path like /Blog/All-About-Routing/Introduction and will extract the values { controller = Blog, action = ReadArticle, article = All-About-Routing/Introduction }. The default route values for controller and action are produced by the route even though there are no corresponding route parameters in the template. Default values can be specified in the route template. The article route parameter is defined as a catch-all by the appearance of an asterisk * before the route parameter name. Catch-all route parameters capture the remainder of the URL path, and can also match the empty string.

last options is: don't use controller, and do it in global configure:

public class Startup
{
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.Map("api/v1/Test", x =>
        {
            x.Run(async context =>
            {
                //path e.g. is somedir/somesubdir/filename.extension
                string prefix = "https://example-domain.com/api/v1/Other/";
                string path = context.Request.Path.Value.Replace("/api/v1/Test/", "/api/v1/Other/").Replace("%2F", "/");
                context.Response.Redirect(prefix + path);
            });
        });
    }
}


回答3:

I think you need to receive those 3 params separated as you have in your URL, so.. that method should be something like this...

[Route("{dir}/{subdir}/filename")]
public async Task<IActionResult> Get(string dir, string subdir, string filename)
{
  string path = dir + "/" + subdir + "/" + filename;
  //path e.g. is somedir/somesubdir/filename.extension
  string prefix = "https://example-domain.com/api/v1/Other/";
  //string path2 = HttpContext.Request.Path.Value.Replace("/api/v1/Test/", "/api/v1/Other/").Replace("%2F", "/");
  return Redirect(prefix + path);
}