2

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

3 Answers 3

6

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" };

    }
Sign up to request clarification or add additional context in comments.

2 Comments

I changed from Request.Path.Value to Request.GetDisplayUrl(). Request.Path is giving only route where Request.GetDisplayUrl() is giving entire url which has maxrecords. Thanks for the above approach which resolved my issue temporarily till my clients are updated.
Updated the answer based on your comment!
1

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.

5 Comments

I agree. I need to support existing clients who are using startRecordId in route. so I can't change url's. Is there any recommendation how to handle such issue.
API versioning. Old clients keep using old API. You set a deprecation date, and then that date lands, you kill it.
That way clients are given time to update their code, but APIs evolve and you can't support every client forever.
Versioning might not work in my case. I'm converting Asp.net webapi project to asp.net core with expectation clients should not be impacted and webapi project is converted to asp.net core on server side.
They can remain on the default version while you correct the shape of your API on a new one.
1

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" };

    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.