Post Action method overload with same route name in ASP.Net Web API Controller

Viewed 1437

Is it possible to have two actions with same route name and same method but different parameter? I have tried this:

[HttpPost]
[Route("gstr4")]
public HttpResponseMessage SubmitGSTR4([FromBody] RequestPayloadWithoutSign requestPayload)
{ }

[HttpPost]
[Route("gstr4")]
public HttpResponseMessage FileGSTR4([FromBody] RequestPayloadWithSign requestPayload)
{ }

I received a Status Code of 500 (InternalServerError) and here is raw response:

{"Message":"An error has occurred.","ExceptionMessage":"Multiple actions were found that match the request: \r\nFileGSTR4 on type APIPortal.Controllers.GSTR4Controller\r\nSubmitGSTR4 on type APIPortal.Controllers.GSTR4Controller","ExceptionType":"System.InvalidOperationException","StackTrace":"   at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.SelectAction(HttpControllerContext controllerContext)\r\n   at System.Web.Http.Controllers.ApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext)\r\n   at System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)\r\n   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"}
3 Answers

It's not possible to have many actions with the same route and the same HTTP verb.

What you could do is to have a single action which accepts a model of a base class (possibly an abstract class) and a custom resolver.

Example:

public abstract class RequestPayload 
{
}

public class RequestPayloadWithoutSign : RequestPayload
{
}

public class RequestPayloadWithSign : RequestPayload
{
   public string Sign { get; set; }
}

public class MyController
{
    [HttpPost]
    [Route("gstr4")]
    public HttpResponseMessage SubmitGSTR4([FromBody] RequestPayload payload)
    {
        switch(payload)
        {
            case RequestPayloadWithoutSign a:
                // handle RequestPayloadWithoutSign
                break;
            case RequestPayloadWithSign b:
                // handle RequestPayloadWithSign
                break;
        }
    }
}

And, if you're using JSON mediatype for transmitting payloads, a custom JSON resolver attached to your JSON deserializer. Here's an example for Newtonsoft.Json:

class HolderConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(RequestPayload);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        if (jo.ContainsKey("sign"))
        {
            return new RequestPayloadWithSign
            {
                Sign = (string) jo["sign"]
            };
        }

        return new RequestPayloadWithoutSign();
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Now add the converter to serializer settings or mark your RequestPayload class definition with [JsonConverter(typeof(RequestPayload))] attribute and it will start converting JSONs to either RequestPayloadWithSign or RequestPayloadWithoutSign types.

It's not possible for the same method type as http Get for multiple methods. You should have a different route

Related