Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.Net Web Api Core - Route to action based on query string parameter

I have a single controller with two actions. Each action takes a GUID parameter. My request URL looks like this: http://baseURL/api/v1.0/loadfactors/search?cedentId=5FF7165C-7575-EA11-AA4D-949554C02DE1

This is how my actions look like:

    [HttpGet("search")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    public async Task<List<LoadFactorResource>> GetByLobSettingsId([FromQuery]Guid lobSettingsId)
    {
        return await _service.GetByLobSettingsId(lobSettingsId);
    }

    [HttpGet,Route("search")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    public async Task<List<LoadFactorResource>> GetByAccountId([FromQuery]Guid cedentId)
    {
        return await _service.GetByCedentId(cedentId);
    }

Now when I make a request, this is the error I get:

An unhandled exception occurred while processing the request. AmbiguousMatchException: The request matched multiple endpoints. Matches:

LoadFactorsController.GetByLobSettingsId (Api) LoadFactorsController.GetByAccountId (Api)

It seems it is finding multiple actions and not identifying action based on the query parameter. How do I make so it matches based on the parameter?

Thanks.

like image 785
Ashar Syed Avatar asked Apr 09 '20 14:04

Ashar Syed


People also ask

What is difference between FromQuery and FromBody?

[FromQuery] - Gets values from the query string. [FromRoute] - Gets values from route data. [FromForm] - Gets values from posted form fields. [FromBody] - Gets values from the request body.

How do I use FromUri in Web API?

Using [FromUri] To force Web API to read a complex type from the URI, add the [FromUri] attribute to the parameter. The following example defines a GeoPoint type, along with a controller method that gets the GeoPoint from the URI.

Can we use FromBody with Httpget?

A Get does not send the body of the form, it only requests a URL. Use a <form> in your view and post it to your controller method, which needs to be decorated with HttpPost.


1 Answers

An unhandled exception occurred while processing the request. AmbiguousMatchException: The request matched multiple endpoints.

As error indicates that the request matched multiple actions resulting in ambiguity.

To fix it and achieve your requirement, you can try following approaches:

Approach 1: merge these two action as one action and dynamically check query string value that client passed.

[HttpGet("search")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<List<LoadFactorResource>> GetByLobSettingsId([FromQuery]Guid lobSettingsId, [FromQuery]Guid cedentId)
{
    if (lobSettingsId != Guid.Empty)
    {
        return await _service.GetByLobSettingsId(lobSettingsId);
    }

    return await _service.GetByCedentId(cedentId);
}

Approach 2: implement a custom ActionMethodSelectorAttribute to enable or disable an action for a given request based on passed query string, like below.

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class CheckQueryStringAttribute : ActionMethodSelectorAttribute
{
    public string QueryStringName { get; set; }
    public bool CanPass { get; set; }
    public CheckQueryStringAttribute(string qname, bool canpass)
    {
        QueryStringName = qname;
        CanPass = canpass;
    }

    public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
    {
        StringValues value;

        routeContext.HttpContext.Request.Query.TryGetValue(QueryStringName, out value);

        if (CanPass)
        {
            return !StringValues.IsNullOrEmpty(value);
        }

        return StringValues.IsNullOrEmpty(value);
    }
}

Apply it to actions

[HttpGet("search")]
[CheckQueryStringAttribute("lobSettingsId",true)]
[CheckQueryStringAttribute("cedentId", false)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<List<LoadFactorResource>> GetByLobSettingsId([FromQuery]Guid lobSettingsId)
{  
    return await _service.GetByCedentId(cedentId);                
}

[HttpGet, Route("search")]
[CheckQueryStringAttribute("lobSettingsId", false)]
[CheckQueryStringAttribute("cedentId", true)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<List<LoadFactorResource>> GetByAccountId([FromQuery]Guid cedentId)
{
    return await _service.GetByCedentId(cedentId);
}
like image 68
Fei Han Avatar answered Nov 30 '22 05:11

Fei Han