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}")]
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);
}
@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);
});
});
}
}
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);
}