ASP.Net Core + Swagger - Actions require an explicit HttpMethod binding for Swagger 2.0
Asked Answered
C

9

21

I have a project MyProject.Api with following structure:

Controllers/
- Api/
  - UsersController.cs
- HomeController.cs
Startup.cs

where HomeController.cs looks like this:

namespace MyProject.Api.Controllers
{
    public class HomeController : Controller
    {
        private readonly IHostingEnvironment _hostingEnvironment;

        public HomeController(IHostingEnvironment hostingEnv) {...}

        [HttpGet]
        public async Task<IActionResult> Index() {...}

        [HttpGet("sitemap.xml")]
        public IActionResult SiteMap() {...}

        [HttpGet("error")]
        public IActionResult Error() {...}
    }
}

and UsersController.cs looks like this:

namespace MyProject.Api.Controllers.Api
{
    [Route("api/[controller]")]
    public class UsersController : Controller
    {
        private readonly ApiHelper<UsersController> _apiHelper;
        private readonly IUserService _userService;

        public UsersController(ILogger<UsersController> logger, IUserService userService) {...}

        [HttpPost("login")]
        public async Task<JsonResult> Login([FromBody] LoginRequest request) {...}

        [HttpPost("register")]
        public async Task<JsonResult> Register([FromBody] RegisterRequest request) {...}

        [HttpGet("renew")]
        [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
        public async Task<JsonResult> Renew() {...}
    }
}

and Startup.cs:


namespace MyProjet.Api
{
    public class Startup
    {
        private IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration) {...}

        public void ConfigureServices(IServiceCollection services)
        {
            ...

            services.AddSwaggerGen(c => c.SwaggerDoc("v1", new Info {Title = "Web Api Docs", Version = "v1"}));
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            ...

            app.UseSwagger();
            app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); });
            app.MapWhen(x => !x.Request.Path.Value.StartsWith("/swagger", StringComparison.OrdinalIgnoreCase), builder =>
            {
                builder.UseMvc(routes =>
                {
                    routes.MapSpaFallbackRoute(
                       "spa-fallback",
                        new {controller = "Home", action = "Index"});
                });
            });
        }
    }
}

when I load /swagger the UI loads successfully, but with following error:

Fetch error
Internal Server Error /swagger/v1/swagger.json

and with this error on the server side

System.NotSupportedException: Ambiguous HTTP method for action - WebEssentials.AspNetCore.Pwa.PwaController.ServiceWorkerAsync (WebEssentials.AspNetCore.Pwa). Actions require an explicit HttpMethod binding for Swagger 2.0
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.CreatePathItem(IEnumerable`1 apiDescriptions, ISchemaRegistry schemaRegistry)
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(String documentName, String host, String basePath, String[] schemes)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.SpaServices.Webpack.ConditionalProxyMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.SpaServices.Webpack.ConditionalProxyMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)

but all methods have unique route, unique name and their HTTP methods are explicitly bound. I've tried adding [Route("")] to the HomeController.cs, but that did not work either.

What am I doing wrong?

Cracksman answered 11/8, 2019 at 23:2 Comment(4)
The stack trace states that the method at WebEssentials.AspNetCore.Pwa.PwaController.ServiceWorkerAsync requires an explicit HttpMethod binding. Unfortunately for you that controller is in a third party library. So you're best option to get around that is to not use that libraryCypro
@Cypro I must be blind! Thank you!Cracksman
I solved the same issue by adding [HttpGet] to the related Action.Jadejaded
1-remove parameters from Http??? and let it [Http???] 2-add [Route] attribute and add routing parameters to it and it should workDowncomer
C
15

As @devNull said, the mistake is not in my code but in WebEssentials.AspNetCore.Pwa.PwaController.ServiceWorkerAsync.

UPDATE:

My pull request with fix (Added explicit HttpMethod bindings) was now merged to WebEssentials.AspNetCore.ServiceWorker repository and will hopefully be available in next release on NuGet with version newer than 1.0.59

OLD SOLUTION:

I found the following solution posted here by Gandii.

  1. Create somewhere class ApiExplorerIgnores with following content
public class ApiExplorerIgnores : IActionModelConvention
{
    public void Apply(ActionModel action)
    {
        if (action.Controller.ControllerName.Equals("Pwa"))
            action.ApiExplorer.IsVisible = false;
    }
}
  1. Add following code to method ConfigureServices in Startup.cs
services.AddMvc(c => c.Conventions.Add(new ApiExplorerIgnores()))

This should hide the PwaController from ApiExplorer which is used by Swashbuckle.

Cracksman answered 11/8, 2019 at 23:42 Comment(3)
Not included in 1.0.59, I just ran into this issue. The "old" solution worked for me though.Ingest
I just addded [HttpGet] on method and it works fine. Environment .Net Core 3.1 with SwashBuckle 5.1Inguinal
Now you could add [ApiExplorerSettings(IgnoreApi = true)] to ignore Swagger from including the controller from the documentation. This also help prevent this issue.Lithea
L
21

Had the same error but without any third party library involved.

Problem in my code was, that there where public methods in my controller base class being used by the controller but not to be exposed by the controller.

Example:

[Authorize]
[Route("api/[controller]")]
[ApiController]
public class TestController : GenericController<TestClass>
{
    public TestController() : base()
    {
    }
}
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class GenericController<T1>
{
    public GenericController()
    {
    // ...
    }
    
    [HttpGet]
    public async Task<ActionResult<IEnumerable<T1>>> Get()
    {
    // ...
    }
    
    public void DoStuff()
    {
    // ...
    }
}

Changing the access modifier of DoStuff to internal solved the problem.

Labyrinthine answered 28/10, 2020 at 10:31 Comment(0)
C
15

As @devNull said, the mistake is not in my code but in WebEssentials.AspNetCore.Pwa.PwaController.ServiceWorkerAsync.

UPDATE:

My pull request with fix (Added explicit HttpMethod bindings) was now merged to WebEssentials.AspNetCore.ServiceWorker repository and will hopefully be available in next release on NuGet with version newer than 1.0.59

OLD SOLUTION:

I found the following solution posted here by Gandii.

  1. Create somewhere class ApiExplorerIgnores with following content
public class ApiExplorerIgnores : IActionModelConvention
{
    public void Apply(ActionModel action)
    {
        if (action.Controller.ControllerName.Equals("Pwa"))
            action.ApiExplorer.IsVisible = false;
    }
}
  1. Add following code to method ConfigureServices in Startup.cs
services.AddMvc(c => c.Conventions.Add(new ApiExplorerIgnores()))

This should hide the PwaController from ApiExplorer which is used by Swashbuckle.

Cracksman answered 11/8, 2019 at 23:42 Comment(3)
Not included in 1.0.59, I just ran into this issue. The "old" solution worked for me though.Ingest
I just addded [HttpGet] on method and it works fine. Environment .Net Core 3.1 with SwashBuckle 5.1Inguinal
Now you could add [ApiExplorerSettings(IgnoreApi = true)] to ignore Swagger from including the controller from the documentation. This also help prevent this issue.Lithea
T
9

Swagger (at least v3.0 and below) will fail if Http action method has a parameter. So, use [HttpPost] instead of [HttpPost("somename")].

Thirddegree answered 11/11, 2022 at 10:12 Comment(0)
W
5

my issue was there was no [HttpGet] above my method for it to actually get the api that i wanted

Wavell answered 6/6, 2022 at 21:41 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Saddle
P
4

Had the same error but it was different solution. When merging master to my branch, I deleted [HttpGet] in my controller above the function that was written in the debug console. Hope that my experience helps someone

Polyzoic answered 19/11, 2020 at 9:4 Comment(0)
S
0

For me, the controller action reported in the error didn't have any HTTP verb decorator. I added it and it worked. Error was

Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Ambiguous HTTP method for action xxxxxx.ErrorController.Error

Earlier Code

[Route("error/{code}", Name = "ErrorByCode")]
        public IActionResult Error(int code)
        {
            var err = ErrResp.GetErrorObject(code.ToString());
            return new ObjectResult(err);
        }

Fix

[HttpGet]
        [Route("error/{code}", Name = "ErrorByCode")]
        public IActionResult Error(int code)
        {
            var err = ErrResp.GetErrorObject(code.ToString());
            return new ObjectResult(err);
        }

Please note, [HttpGet] HTTP verb decorator added.

Strunk answered 22/9, 2023 at 10:22 Comment(0)
U
0

If, like me, you have a public method in a controller which you can't make internal (see sc911's answer), an alternative is to decorate the method so the API Explorer ignores it:

[ApiExplorerSettings(IgnoreApi = true)]
Unpolitic answered 20/3 at 21:16 Comment(0)
A
0

Just to add this possible cause to the list:

I converted a controller from an ApiController

[ApiController]
[Route("[controller]")]
[Authorize(AuthenticationSchemes = "Bearer", Roles = "admin")]
public class DocumentController : ControllerBase
{
    [HttpGet(Name = "GetDocuments")]
    public IEnumerable<Document> Get()
    {
        return Enumerable.Empty<Document>();
    }
}

To an ODataController

[ApiController]
[Route("[controller]")]
[Authorize(AuthenticationSchemes = "Bearer", Roles = "admin")]
public class DocumentController : ODataController
{
    [EnableQuery]
    public IActionResult Get()
    {
        return Ok(Enumerable.Empty<Document>());
    }
}

Everything worked fine except for swagger. Turns out I forgot to remove

[ApiController]
[Route("[controller]")]

from my controller. After removing these attributes the exception went away.

Analyst answered 27/5 at 11:52 Comment(0)
M
-1

Thanks, I would also suggest checking all the new routes and endpoints added in the new commit, that was in my case

Marsh answered 11/10, 2023 at 18:57 Comment(1)
This does not provide a new answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From ReviewAlfieri

© 2022 - 2024 — McMap. All rights reserved.