简体   繁体   中英

Asp.net core Routing with multiple optional parameters calling different action

In Asp.net WebApi2

when api/values/9b858599-7639-45da-acd6-a1323fb019b5 is called get Action is invoked.

Action with optional parameters.

When api/values/9b858599-7639-45da-acd6-a1323fb019b5?maxRecords=100 or api/values/?maxRecords=100 GetProducts Action is invoked.

In Asp.net Core

But in asp.net core when api/values/9b858599-7639-45da-acd6-a1323fb019b5 is called GetProducts Action is getting invoked. I wanted to call Get action without changing existing url's.

How to fix this issue in Asp.net core 2.0

Contoller

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    //https://localhost:44323/api/values/9b858599-7639-45da-acd6-a1323fb019b5
    [HttpGet("{productId:Guid}", Order = 1)]
    public ActionResult<string> Get(Guid productId)
    {
        return "value1";
    }


    //https://localhost:44323/api/values/9b858599-7639-45da-acd6-a1323fb019b5?maxRecords=100
    //https://localhost:44323/api/values/?maxRecords=100
    [HttpGet("{startRecordId:Guid?}")]
    public ActionResult<IEnumerable<string>> GetProducts(Guid? startRecordId, int maxRecords, DateTimeOffset? minimumChangeDate = null)
    {
        return new string[] { "value1", "value2" };

    }

}

Startup.cs

app.UseMvc(routes =>
        {
            routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
            routes.MapRoute("DefaultApi", "api/{controller}/{id?}");
        });

While not ideal, and this is a temporary shoehorn, till you can update the client calls. You could try this, if you can assume that you must have a maxRecords QueryString. Since if it defaults to 0 it is useless, unless you have logic in place in the case that it is 0.

    //https://localhost:44323/api/values/9b858599-7639-45da-acd6-a1323fb019b5
    [HttpGet("{productId:Guid}", Order = 1)]
    public ActionResult<string> Get(Guid productId)
    {
        return "value1";
    }


    //https://localhost:44323/api/values/9b858599-7639-45da-acd6-a1323fb019b5?maxRecords=100
    //https://localhost:44323/api/values/?maxRecords=100
    [HttpGet("{startRecordId:Guid?}")]
    public ActionResult<IEnumerable<string>> GetProducts(Guid? startRecordId, [FromQuery] int maxRecords, [FromQuery] DateTimeOffset? minimumChangeDate = null)
    {
        if (!Request.GetDisplayUrl().Contains(nameof(maxRecords)) &&
            startRecordId.HasValue)
        {
            return Get(startRecordId.Value);
        }

        return new string[] { "value1", "value2" };

    }

You could combine your actions and change the behaviour based on which parameters have a supplied value;

    //https://localhost:44323/api/values/9b858599-7639-45da-acd6-a1323fb019b5
    //https://localhost:44323/api/values/9b858599-7639-45da-acd6-a1323fb019b5?maxRecords=100
    //https://localhost:44323/api/values/?maxRecords=100
    [HttpGet("{startRecordId:Guid?}")]
    public ActionResult<IEnumerable<string>> GetProducts(Guid? startRecordId, int? maxRecords, DateTimeOffset? minimumChangeDate = null)
    {
        if (startRecordId.HasValue && !maxRecords.HasValue)
            return "value";
        else
            return new string[] { "value1", "value2" };

    }

Your URIs aren't following RESTful convention. Frankly, I can't see how you ever made this work in the first place, because the same problem should have resulted in ASP.NET Web Api, but it's possible you just got lucky about the way the routing was done in ASP.NET Web Api. ASP.NET Core handles routing completely differently.

Regardless, a route for list of multiple products should not contain an id in the actual URI. In other words:

/api/values - Multiple values
/api/values/{id} - Single value

For things like filtering, ordering, etc. the multiple records, those should be part of the query string. That includes something like startRecordId :

/api/values?startRecordId={id}

Remove the ambiguity in your routes and you'll have no issue. Long and short, you can't have two routes both accepting a Guid in the same path segment, even if it's optional on one. Move the startRecordId into the query string, which is the correct way to do it anyways, and you're good to go.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM