Swagger Error Ambiguous HTTP method for action and Failed to load API definition

4 Comments

In this post, we will look at a Swagger error “System.NotSupportedException: Ambiguous HTTP method for action” and “Failed to load API definition”. These errors can occur when you add are using multiple methods on the same HTTP verb, such as POST.

The error looks like this when you browse to Swagger:

And in Visual Studio, you will see the error reported in the Output  System.NotSupportedException: Ambiguous HTTP method for action. Actions require an explicit HttpMethod binding for Swagger/OpenAPI 3.0:

Let’s look at what happened.

Our original Swagger for this app service looks like this – pretty straight forward with 2 GET methods, one distinguished by the id parameter. The other POST, PUT and DELETE are all unique:

Our original code looks something like this:

Now let’s add some new functionality, including a method to GetCustomersByLastName and some additional PostCustomer functions. We will add SwaggerResponse to the methods as well, which will implement HTTP Status Codes such as HttpStatusCode.OK, HttpStatusCode.NotFound, HttpStatusCode.Created, HttpStatusCode.NotImplemented etc.

You can see we are implementing multiple GET and POST methods now.

The code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Carl.CRM.API.Models;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;

namespace Carl.CRM.API.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class CustomersController : ControllerBase
    {
        // GET api/values
        [SwaggerOperation("GetCustomers")]
        [SwaggerResponse((int)HttpStatusCode.OK)]
        [SwaggerResponse((int)HttpStatusCode.NotFound)]
        public ActionResult<IEnumerable<string>> Get()
        {
            Customer bob = new Customer { Id = 1, FirstName = "Bob", LastName = "Smith" };
            Customer david = new Customer { Id = 2, FirstName = "David", LastName = "Johnson" };
            Customer alice = new Customer { Id = 3, FirstName = "Alice", LastName = "Carter" };
            Customer bill = new Customer { Id = 4, FirstName = "Bill", LastName = "James" };

            List<Customer> customers = new List<Customer>();
            customers.Add(bob);
            customers.Add(david);
            customers.Add(alice);
            customers.Add(bill);

            return new JsonResult(customers);
        }

        // GET api/values
        [SwaggerOperation("GetCustomers")]
        [SwaggerResponse((int)HttpStatusCode.OK)]
        [SwaggerResponse((int)HttpStatusCode.NotFound)]
        public ActionResult<IEnumerable<string>> GetCustomers(string lastname)
        {
            Customer bob = new Customer { Id = 1, FirstName = "Bob", LastName = "Smith" };
            Customer david = new Customer { Id = 2, FirstName = "David", LastName = "Johnson" };
            Customer alice = new Customer { Id = 3, FirstName = "Alice", LastName = "Carter" };
            Customer bill = new Customer { Id = 4, FirstName = "Bill", LastName = "James" };

            List<Customer> customers = new List<Customer>();
            customers.Add(bob);
            customers.Add(david);
            customers.Add(alice);
            customers.Add(bill);

            return new JsonResult(customers.Where(c => c.LastName.Contains(lastname)));
        }


        // GET api/values/5
        [SwaggerOperation("GetCustomer")]
        [SwaggerResponse((int)HttpStatusCode.OK)]
        [SwaggerResponse((int)HttpStatusCode.NotFound)]
        public ActionResult<string> Get(int id)
        {
            Customer bob = new Customer { Id = 1, FirstName = "Bob", LastName = "Smith" };
            Customer david = new Customer { Id = 2, FirstName = "David", LastName = "Johnson" };
            Customer alice = new Customer { Id = 3, FirstName = "Alice", LastName = "Carter" };
            Customer bill = new Customer { Id = 4, FirstName = "Bill", LastName = "James" };

            if (id ==1)
            {
                return Ok(bob);
            }
            else
                if (id == 2)
            {
                return Ok(david);
            }
            else
                if (id == 3)
            {
                return Ok(alice);
            }
            else
                if (id == 4)
            {
                return Ok(bill);
            }
            else
            {
                return NotFound();
            }
        }

        // POST api/values
        [SwaggerOperation("PostCustomer")]
        [SwaggerResponse((int)HttpStatusCode.OK)]
        [SwaggerResponse((int)HttpStatusCode.NotFound)]
        [SwaggerResponse((int)HttpStatusCode.Created)]
        public void PostCustomer([FromBody] string value)
        {
        }

        // POST api/values
        [HttpPost("PostCustomer2")]
        [SwaggerOperation("PostCustomer2")]
        [SwaggerResponse((int)HttpStatusCode.NotImplemented)]
        public void PostCustomer2([FromBody] string value)
        {
        }

        // POST api/values
        [SwaggerOperation("PostCustomer3")]
        [SwaggerResponse((int)HttpStatusCode.OK)]
        [SwaggerResponse((int)HttpStatusCode.NotFound)]
        public void PostCustomer3([FromBody] string value)
        {
        }

        // PUT api/values/5
        [SwaggerOperation("PutCustomer")]
        [SwaggerResponse((int)HttpStatusCode.OK)]
        [SwaggerResponse((int)HttpStatusCode.NotFound)]
        public void Put(int id, [FromBody] string value)
        {
        }

        // DELETE api/values/5
        [SwaggerOperation("DeleteCustomer")]
        [SwaggerResponse((int)HttpStatusCode.OK)]
        [SwaggerResponse((int)HttpStatusCode.NotFound)]
        public void Delete(int id)
        {
        }
    }
}

Running this and browsing to Swagger gives us the error.

There are a few ways to resolve this. One way is to tell Swagger that we have multiple routes here for these methods. We can distinguish this by adding to each function the verb as well as the method name and parameters it takes. For example:

[HttpGet(“GetCustomersByLastName/{lastname}”)]

Here’s the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Carl.CRM.API.Models;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;

namespace Carl.CRM.API.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class CustomersController : ControllerBase
    {
        // GET api/values
        [HttpGet]
        [SwaggerOperation("GetCustomers")]
        [SwaggerResponse((int)HttpStatusCode.OK)]
        [SwaggerResponse((int)HttpStatusCode.NotFound)]
        public ActionResult<IEnumerable<string>> Get()
        {
            Customer bob = new Customer { Id = 1, FirstName = "Bob", LastName = "Smith" };
            Customer david = new Customer { Id = 2, FirstName = "David", LastName = "Johnson" };
            Customer alice = new Customer { Id = 3, FirstName = "Alice", LastName = "Carter" };
            Customer bill = new Customer { Id = 4, FirstName = "Bill", LastName = "James" };

            List<Customer> customers = new List<Customer>();
            customers.Add(bob);
            customers.Add(david);
            customers.Add(alice);
            customers.Add(bill);

            return new JsonResult(customers);
        }

        // GET api/values
        [HttpGet("GetCustomersByLastName/{lastname}")]
        [SwaggerOperation("GetCustomers")]
        [SwaggerResponse((int)HttpStatusCode.OK)]
        [SwaggerResponse((int)HttpStatusCode.NotFound)]
        public ActionResult<IEnumerable<string>> GetCustomers(string lastname)
        {
            Customer bob = new Customer { Id = 1, FirstName = "Bob", LastName = "Smith" };
            Customer david = new Customer { Id = 2, FirstName = "David", LastName = "Johnson" };
            Customer alice = new Customer { Id = 3, FirstName = "Alice", LastName = "Carter" };
            Customer bill = new Customer { Id = 4, FirstName = "Bill", LastName = "James" };

            List<Customer> customers = new List<Customer>();
            customers.Add(bob);
            customers.Add(david);
            customers.Add(alice);
            customers.Add(bill);

            return new JsonResult(customers.Where(c => c.LastName.Contains(lastname)));
        }


        // GET api/values/5
        [HttpGet("{id}")]
        [SwaggerOperation("GetCustomer")]
        [SwaggerResponse((int)HttpStatusCode.OK)]
        [SwaggerResponse((int)HttpStatusCode.NotFound)]
        public ActionResult<string> Get(int id)
        {
            Customer bob = new Customer { Id = 1, FirstName = "Bob", LastName = "Smith" };
            Customer david = new Customer { Id = 2, FirstName = "David", LastName = "Johnson" };
            Customer alice = new Customer { Id = 3, FirstName = "Alice", LastName = "Carter" };
            Customer bill = new Customer { Id = 4, FirstName = "Bill", LastName = "James" };

            if (id ==1)
            {
                return Ok(bob);
            }
            else
                if (id == 2)
            {
                return Ok(david);
            }
            else
                if (id == 3)
            {
                return Ok(alice);
            }
            else
                if (id == 4)
            {
                return Ok(bill);
            }
            else
            {
                return NotFound();
            }
        }

        // POST api/values
        [HttpPost("{id}")]
        [SwaggerOperation("PostCustomer")]
        [SwaggerResponse((int)HttpStatusCode.OK)]
        [SwaggerResponse((int)HttpStatusCode.NotFound)]
        [SwaggerResponse((int)HttpStatusCode.Created)]
        public void PostCustomer([FromBody] string value)
        {
        }

        // POST api/values
        [HttpPost("PostCustomer2")]
        [SwaggerOperation("PostCustomer2")]
        [SwaggerResponse((int)HttpStatusCode.NotImplemented)]
        public void PostCustomer2([FromBody] string value)
        {
        }

        // POST api/values
        [HttpPost("PostCustomer3/{id}")]
        [SwaggerOperation("PostCustomer3")]
        [SwaggerResponse((int)HttpStatusCode.OK)]
        [SwaggerResponse((int)HttpStatusCode.NotFound)]
        public void PostCustomer3([FromBody] string value)
        {
        }

        // PUT api/values/5
        [HttpPut("{id}")]
        [SwaggerOperation("PutCustomer")]
        [SwaggerResponse((int)HttpStatusCode.OK)]
        [SwaggerResponse((int)HttpStatusCode.NotFound)]
        public void Put(int id, [FromBody] string value)
        {
        }

        // DELETE api/values/5
        [HttpDelete("{id}")]
        [SwaggerOperation("DeleteCustomer")]
        [SwaggerResponse((int)HttpStatusCode.OK)]
        [SwaggerResponse((int)HttpStatusCode.NotFound)]
        public void Delete(int id)
        {
        }
    }
}

Now when we run this, we get the new methods appearing:

 

THANKS FOR READING. BEFORE YOU LEAVE, I NEED YOUR HELP.
 

I AM SPENDING MORE TIME THESE DAYS CREATING YOUTUBE VIDEOS TO HELP PEOPLE LEARN THE MICROSOFT POWER PLATFORM.

IF YOU WOULD LIKE TO SEE HOW I BUILD APPS, OR FIND SOMETHING USEFUL READING MY BLOG, I WOULD REALLY APPRECIATE YOU SUBSCRIBING TO MY YOUTUBE CHANNEL.

THANK YOU, AND LET'S KEEP LEARNING TOGETHER.

CARL

https://www.youtube.com/carldesouza

 

ABOUT CARL DE SOUZA

Carl de Souza is a developer and architect focusing on Microsoft Dynamics 365, Power BI, Azure, and AI.

carldesouza.comLinkedIn Twitter | YouTube

 

4 Responses to Swagger Error Ambiguous HTTP method for action and Failed to load API definition

  1. “There are a few ways to resolve this.”
    And then you describe only one of them 😀

    What are the other ways? I would like to skip putting explicit verb attributes.

  2. I agree with Kansule. Is there a simpler way than having decorating each action method with a different HTTP endpoint.

  3. What is the correct way to decorate the Post action when the incoming payload is json for a D365 entity like Contact (early bound generator created)

    [HttpPost(“ContactIncoming/{contact}”, Name = “ContactIncoming”)]
    [Route(“Default/Insert”)]
    public IActionResult PostContact([FromBody] Contact contact)
    {
    string name = contact.FirstName;

    //_d365Services.UpdateEntity(contact);
    return Ok();
    }
    I am still getting this same error. 🙁

  4. I answered my own question. Sorry..

    [HttpPost(“PostContact/{contact}”)]
    public IActionResult PostContact(Contact contact)
    {
    string name = contact.FirstName;

    //_d365Services.UpdateEntity(contact);
    return Ok();
    }

Leave a Reply

Your email address will not be published. Required fields are marked *