简体   繁体   中英

Map / Route HTTP Request to endpoint based on query parameter value? asp.net core 3.x

I have a client that will need to hit the same base endpoint url for multiple requests using rest HttpGet calls, each with unique query parameters and values. The function I need to map for each request will be determined by the value provided by the "request" query parameter. Is this possible to do inside of the app.UseEndpoints(), attributes on the endpoint functions, or a similar simple solution?

I'm a bit new to this part, and the docs don't have an example for this. What I have so far (doesn't work):

Startup.cs

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "test1",
            pattern: "serviceapi",
            constraints: new { request = "test1" }, //map action if param "request" value is "test1"?
            defaults: new { controller = "ServiceApi", action = "TestRequest1" }
        );
        endpoints.MapControllerRoute(
            name: "test2",
            pattern: "serviceapi",
            constraints: new { request = "test2" }, //map action if param "request" value is "test2"?
            defaults: new { controller = "ServiceApi", action = "TestRequest2" }
        );
    });

what I want is when I use postman to send a get request to " {{url}}/api/serviceapi/ ? request=test1 &uniqueparam1=foo", it runs the code in my ServiceApi.TestRequest1()

[Route("api/[controller]")]
ServiceApiController.cs

    [HttpGet]
    public async Task<IActionResult> TestRequest1([FromQuery]string request, [FromQuery]string uniqueparam1)
    {
        return Ok($"TestRequest1: {request} - {uniqueparam1}");
    }

    [HttpGet]
    public async Task<IActionResult> TestRequest2([FromQuery]string request, [FromQuery]string uniqueparam2, [FromQuery]string uniqueparam3)
    {
        return Ok($"TestRequest2: {request} - {uniqueparam2}, {uniqueparam3}");
    }

The error that I get currently with this code is that both functions were candidates: "Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints."

Thanks!

------ UPDATE ------

Thanks to Rena's answer, I was able to get this working:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class RequestValueConstraintAttribute : ActionMethodSelectorAttribute
{
    public string RequestValue { get; private set; }

    private readonly string _requestParam = "request";

    public RequestValueConstraintAttribute(string requestValue)
    {
        RequestValue = requestValue;
    }

    public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
    {
        return (string)routeContext.HttpContext.Request.Query[_requestParam] == RequestValue;
    }
}

ServiceApiController.cs

[Route("api/[controller]")]
[ApiController]
public class ServiceApiController : ControllerBase
{
    [RequestValueConstraint("test1")] //uses this endpoint function if param "request" value is "test1"
    [HttpGet]
    public async Task<IActionResult> TestRequest1([FromQuery]string request, [FromQuery]string uniqueparam1)
    {
        return Ok($"TestRequest1: {request} - {uniqueparam1}");
    }

    [RequestValueConstraint("test2")] //uses this endpoint function if param "request" value is "test2"
    [HttpGet]
    public async Task<IActionResult> TestRequest2([FromQuery]string request, [FromQuery]string uniqueparam2, [FromQuery]string uniqueparam3)
    {
        return Ok($"TestRequest2: {request} - {uniqueparam2}, {uniqueparam3}");
    }
}

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:

[Route("api/[controller]")]
[ApiController]
public class ValuesController : Controller
{
    [QueryStringConstraint("uniqueparam1", true)]
    [QueryStringConstraint("uniqueparam2", false)]
    [HttpGet]
    public async Task<IActionResult> TestRequest1([FromQuery]string request, [FromQuery]string uniqueparam1)
    {
        return Ok($"TestRequest1: {request} - {uniqueparam1}");
    }
    [QueryStringConstraint("uniqueparam2", true)]
    [QueryStringConstraint("uniqueparam1", false)]
    [HttpGet]
    public async Task<IActionResult> TestRequest2([FromQuery]string request, [FromQuery]string uniqueparam2, [FromQuery]string uniqueparam3)
    {
        return Ok($"TestRequest2: {request} - {uniqueparam2}, {uniqueparam3}");
    }
}

3.Startup.cs:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    //...   
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();                
    });

}

Reference:

https://stackoverflow.com/a/60770054/11398810

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