2

I am converting an ASP.NET MVC (.NET Framework) application to ASP.NET Core MVC. This is strictly a conversion, I cannot make any breaking changes hence I cannot change any Routes or Methods. I am unable to match the same functionality in ASP.NET Core MVC.

Working ASP.NET MVC:

  [HttpPut]
  [Route("status")]
  public async Task<IHttpActionResult> UpdateStatusByOrderGuid([FromUri] Guid orderGUID, [FromBody] POST_Status linkStatusModel)
  {

  }

  [HttpPut]
  [Route("status")]
  public async Task<IHttpActionResult> UpdateStatusById([FromUri] Guid id, [FromBody] POST_Status linkStatusModel)
  {

  }

Not working, ASP.NET Core MVC.

I get an error:

Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints

Code:

    [HttpPut]
    [Route("status")]
    public async Task<IActionResult> UpdateStatusByOrderGuid([FromQuery] Guid orderGUID, [FromBody] POST_Status statusModel)
    {

    }

    [HttpPut]
    [Route("status")]
    public async Task<IActionResult> UpdateStatusById([FromQuery] Guid id, [FromBody] POST_Status statusModel)
    {

    }

I need to include the query parameters when it resolves which route. It should match based on whether orderGUID or id is in the query string.

Thanks.

0

2 Answers 2

3

You needs to custom ActionMethodSelectorAttribute:

1.QueryStringConstraintAttribute:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class QueryStringConstraintAttribute : ActionMethodSelectorAttribute
{
    public string ValueName { get; private set; }
    public bool ValuePresent { get; private set; }
    public QueryStringConstraintAttribute(string valueName, bool valuePresent)
    {
        this.ValueName = valueName;
        this.ValuePresent = valuePresent;
    }
    public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
    {
        var value = routeContext.HttpContext.Request.Query[this.ValueName];
        if (this.ValuePresent)
        {
            return !StringValues.IsNullOrEmpty(value);
        }
        return StringValues.IsNullOrEmpty(value);
    }
}

2.Controller:

[HttpPut]
[Route("status")]
[QueryStringConstraint("orderGUID",true)]
[QueryStringConstraint("id", false)]
public void UpdateStatusByOrderGuid([FromQuery] Guid orderGUID,[FromBody]POST_Status model)
{

}

[HttpPut]
[Route("status")]
[QueryStringConstraint("id", true)]
[QueryStringConstraint("orderGUID", false)]
public void UpdateStatusById([FromQuery] Guid id, [FromBody]POST_Status model)
{

}

3.Startup.cs:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    //...
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

Result: enter image description here

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

Comments

2

Why not use a single endpoint instead? You don't need to pass Guid's, since it's a GET operation, you can pass strings and cast them later. That way you can send one parameter or the other.

[HttpPut]
[Route("status")]
public async Task<IActionResult> UpdateStatus([FromBody] POST_Status statusModel, [FromQuery] string orderGUID = null, [FromQuery] string id = null)
{
    if (!string.IsNullOrEmpty(orderGUID))
    {
        // UpdateStatusByOrderGuid implementation here
        // Guid guid = Guid.Parse(orderGUID);

    }
    else if (!string.IsNullOrEmpty(id))
    {
        // UpdateStatusById implementation here
        // Guid guid = Guid.Parse(id);
    }
    else
    {
        throw new ArgumentException("No valid GUID.");
    }
}

This endpoint should be compatible with both scenarios you specified.

1 Comment

Accepting because its less code and avoids creating a new class.

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.