Search code examples
c#asp.netasp.net-coreasp.net-core-mvcattributerouting

How to retrieve a suffix from url as an action parameter when performing attribute routing in ASP.NET


Given Attribute Routing in ASP.Net Core (but I guess MVC and WebAPI work the same way), I want to be able to do something like this:

[Route("api/[controller]")]
public class SampleController : Controller {
    // GET api/sample/123e4567-e89b-12d3-a456-426655440000/folder/subfolder/file.css
    [HttpGet("{id}")] // this is wrong, how should it be written correctly?
    public string Get(Guid id, string urlSuffix) {
        return null; // return stuff based on the id and the full url
    }
}

in the URL taken as example in the comment (api/sample/123e4567-e89b-12d3-a456-426655440000/folder/subfolder/file.css) the SampleController.Get method should be called with the following parameters:

  • id: 123e4567-e89b-12d3-a456-426655440000
  • urlSuffix: folder/subfolder/file.css or /folder/subfolder/file.css (I don't really care for the leading /)

If there are additional query parameters, these should be included in the suffix as well.

I thought about using the raw request URL, but I'd still need a way to specify an action which is executed and what ever I thought of was too late, ASP.Net already figured out that there isn't any URL for the given action.

I would like to use controllers for this, instead of adding some "raw" code into the ASP.Net Core execution pipeline.

Update:

This exact example doesn't work for me with asp.net core dotnet core and kestrel service:

[Route("api/[controller]")]
public class SampleController : Controller
{
    // GET api/values/5
    [HttpGet("{id}/{urlSuffix}")]
    public object Get(string id, string urlSuffix)
    {
        return new {id, urlSuffix};
    }
}

When I call http://localhost:5000/api/sample/some-id/folder I get the correct result, but when I call http://localhost:5000/api/sample/some-id/folder/subfolder/file.extension I get a 404 error.


Solution

  • Referencing: Handling a Variable Number of Segments in a URL Pattern

    Sometimes you have to handle URL requests that contain a variable number of URL segments. When you define a route, you can specify that if a URL has more segments than there are in the pattern, the extra segments are considered to be part of the last segment. To handle additional segments in this manner you mark the last parameter with an asterisk (*). This is referred to as a catch-all parameter. A route with a catch-all parameter will also match URLs that do not contain any values for the last parameter.

    Your template and placeholders will change to ...

    [HttpGet("{id:guid}/{*urlSuffix}")]
    

    Given the following URL ...

    "api/sample/123e4567-e89b-12d3-a456-426655440000/folder/subfolder/file.css"
    

    then

    • id = 123e4567-e89b-12d3-a456-426655440000
    • urlSuffix = "folder/subfolder/file.css"

    Because the / is already part of the template, it will be excluded from the urlSuffix parameter.

    the *urlSuffix acts as a catch all for everything after the {id}/ in the URL. If there are additional query parameters, these will also be included in the urlSuffix as well.

    You were getting the not found error because your example URL could not find a matching route of api/sample/{id}.

    I included the :guid route constraint based on your original example expecting a Guid for id parameter.

    If the id is not going to be a Guid always you can remove the constraint and it will work for your updated example.