Nswag: The method 'Post' on path '/api/admin/v/product' is registered multiple times

Created on 28 Mar 2018  路  10Comments  路  Source: RicoSuter/NSwag

Hi, there is problem on generating swagger.json

Exception content in swagger.json

InvalidOperationException: The method 'Post' on path '/api/admin/v/product' is registered multiple times (check the DefaultUrlTemplate setting [default for Web API: 'api/{controller}/{id}'; for MVC projects: '{controller}/{action}/{id?}']).
NSwag.AspNetCore.Middlewares.WebApiToSwaggerMiddleware+d__10.MoveNext()

version used

AspNetCore v1.1.2
NSwag.AspNetCore v11.16.1


I double checked the API list and I confirm that there is no duplicated Paths, everything works but SwaggerGenerator cannot generate API documents and give the above 500 response.
One of the sample controller is defined as below:
```C#
[Authorize("product.create")]
[Authorize("product.delete")]
[ApiVersion("1.0")]
[Route("api/admin/v{version:apiVersion}/product")]
[TypeFilter(typeof(UserAccessFilter))]
[ServiceFilter(typeof(LanguageActionFilter))]
[ValidateModel]
public class ProductCopyController : ApiController //ApiController inherits ControllerBase
{
public ProductCopyController()
{
}

    [HttpPost]
    [Route("copy-by-slot-to-exact-date")]
    [ProducesResponseType(200, Type = typeof(List<int>))]
    [ProducesResponseType(400, Type = typeof(OperationError))]
    public async Task<IActionResult> CopyBySlotToExactDate([FromBody] RequestA r)
    {
    }

    [HttpPost]
    [Route("copy-by-slot-to-exact-period")]
    [ProducesResponseType(200, Type = typeof(List<int>))]
    [ProducesResponseType(400, Type = typeof(OperationError))]
    public async Task<IActionResult> CopyBySlotToExactDateRange([FromBody] RequestB r)
    {
    }

    [HttpPost]
    [Route("copy-by-date-to-exact-date")]
    [ProducesResponseType(200, Type = typeof(List<int>))]
    [ProducesResponseType(400, Type = typeof(OperationError))]
    public async Task<IActionResult> CopyByDateToExactDate([FromBody] RequestC r)
    {
    }

    [HttpPost]
    [Route("copy-by-date-to-exact-period")]
    [ProducesResponseType(200, Type = typeof(List<int>))]
    [ProducesResponseType(400, Type = typeof(OperationError))]
    public async Task<IActionResult> CopyByDateToExactDateRange([FromBody] RequestD r)
    {
    }

    [HttpPost]
    [Route("copy-by-period-to-exact-period")]
    [ProducesResponseType(200, Type = typeof(List<int>))]
    [ProducesResponseType(400, Type = typeof(OperationError))]
    public async Task<IActionResult> CopyByDateRangeToExactDateRange([FromBody] RequestE r)
    {
    }
}

### Configuration
Also, I configured Swagger at `StartUp.cs` : 
```C#
                app.UseSwaggerUi(typeof(Startup).GetTypeInfo().Assembly, settings =>
                {
                    settings.GeneratorSettings.DefaultPropertyNameHandling = PropertyNameHandling.CamelCase;
                    settings.GeneratorSettings.DefaultEnumHandling = EnumHandling.String;
                    settings.PostProcess = document => { document.Info.Description = "admin api"; };
                    settings.GeneratorSettings.OperationProcessors.Insert(0, new ApiVersionProcessor());
                    settings.GeneratorSettings.IsAspNetCore = true;

                });

ApiVersionProcessor is referenced from #655


I have tried comment out the "problematic" APIs but Swagger response with the same error to different API, eg.

The method 'Post' on path '/api/admin/v/order' is registered multiple times

await-repro done question

Most helpful comment

Hi, @RSuter , I have solved the problem that is interesting and nice to inform you.
As mentioned all controller inherit ApiController and it is defined like this:

public abstract class ApiController : ControllerBase
{
        protected IActionResult ApiResponse<T>(Result<T> result)
        {
            return result.Match<IActionResult>(
                Succ: domain => Ok(domain),
                Fail: HandleBadRequest
            );
        }

        public IActionResult HandleBadRequest(Exception ex) // here is the problematic code
        {
            return ex
                .Match<IActionResult>()
                .With<BusinessException>(BadRequest)
                .Otherwise(e =>
                {
                    LogError(e);
                    return BadRequest(new UnknownError{ Message = e.Message });
                });
        }
}

The public method here is registered by swagger on every controller since it returns an IActionResult.
After we change it into a protect method, problem solved.

All 10 comments

You don't need to add the ApiVersionProcessor because it is added by default. Otherwise I have to reproduce this first...

Works for me, do you see the problematic difference to yours?

image

I removed this line and the problem still exist:
```C#
settings.GeneratorSettings.OperationProcessors.Insert(0, new ApiVersionProcessor());


I have another controller that looks like duplicating the given controller. However Swagger used to work when both Controllers coexist.
Also, FYI, we used Swagger v11.12.16 for a time (~around 3 months) and it functioned correctly until an unknown date this month. Yesterday I upgrade Swagger to v11.16.1, then convert the setting to a new format. The problem still exist.

Another controller is defined below.
```C#
    [Authorize]
    [ApiVersion("1.0")]
    [Route("api/admin/v{version:apiVersion}/product")]
    [TypeFilter(typeof(UserAccessFilter))]
    [ServiceFilter(typeof(LanguageActionFilter))]
    [ValidateModel]
    public class ProductController : ApiController
    {
        public ProductController ()
        {
        }

        [HttpPost]
        [Authorize("product.create")]
        [Route("")]
        [ProducesResponseType(200, Type = typeof(Product))]
        [ProducesResponseType(400, Type = typeof(OperationError))]
        public async Task<IActionResult> Create([FromBody] ProductCreateRequest p)
        {//
        }

        [HttpPut]
        [Authorize("product.edit")]
        [Route("{productId:int}")]
        [ProducesResponseType(200, Type = typeof(Product))]
        [ProducesResponseType(400, Type = typeof(OperationError))]
        public async Task<IActionResult> Update(int productId, [FromBody] ProductUpdateRequest p)
        {//
        }
    }

Maybe the other controller is interfering... try adding SwaggerIgnoreAttribute to one of them to see if this is the problem...

I received a similar error message. I added NSwag to asp.net core 2 / angular 2 project template. Then added a new web api controller and angular code. Everything worked. I then performed the steps to integrated nswag.

Note: I changed the default route rule... changed 'id' to 'locale'. The website worked fine with this change.

Going to http://localhost:50895/swagger/v1/swagger.json returned this error:

  • InvalidOperationException: The method 'Post' on path '/api/Home' is registered multiple times (check the DefaultUrlTemplate setting [default for Web API: 'api/{controller}/{id}'; for MVC projects: '{controller}/{action}/{id?}'])

When I added [SwaggerIgnoreAttribute] to the HomeController, then NSwag worked.
When I restored the default rule template to "{controller=Home}/{action=Index}/{id?}" and removed the [SwaggerIgnoreAttribute] from the HomeController, then it also worked.

Questions:

  • Does NSwag have a special requirement that the default route rule must be "{controller=Home}/{action=Index}/{id?}"?
  • Are there other NSwag expectations about route rules?

I was able to freely define route variable names for api routes.

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
...
    services.AddMvc();
    services.AddSwagger();
...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
    app.UseSwaggerUi3(typeof(Startup).Assembly, settings =>
    {
        settings.GeneratorSettings.DefaultPropertyNameHandling = PropertyNameHandling.CamelCase;
        settings.GeneratorSettings.IsAspNetCore = true;

    });

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{locale?}");
    });

    app.MapWhen(r => !r.Request.Path.Value.StartsWith("/swagger"), builder =>
    {
        builder.UseMvc(routes =>
        {
            routes.MapSpaFallbackRoute(
                name: "spa-fallback",
                defaults: new { controller = "Home", action = "Index" });
        });
    });
...
}

visual studio 2017
AspNetCore v2.0.6
NSwag.AspNetCore v11.16.1

My mistake. I missed this setting: settings.GeneratorSettings.DefaultUrlTemplate
Adding this to the code resolved the error reported by NSwag.

    app.UseSwaggerUi3(typeof(Startup).Assembly, settings =>
    {
        settings.GeneratorSettings.DefaultPropertyNameHandling = PropertyNameHandling.CamelCase;
        settings.GeneratorSettings.DefaultUrlTemplate = "{controller=Home}/{action=Index}/{locale?}";
        settings.GeneratorSettings.IsAspNetCore = true;
    });

Ok. I just tried adding [SwaggerIgnore] to each controller action start with api/admin/v{version:apiVersion}/product . After I apply the attribute to all product controller, the same exception still prompt:

InvalidOperationException: The method 'Post' on path '/api/admin/v/product' is registered multiple times (check the DefaultUrlTemplate setting [default for Web API: 'api/{controller}/{id}'; for MVC projects: '{controller}/{action}/{id?}']).
NSwag.AspNetCore.Middlewares.WebApiToSwaggerMiddleware+d__10.MoveNext()

Hi, @RSuter , I have solved the problem that is interesting and nice to inform you.
As mentioned all controller inherit ApiController and it is defined like this:

public abstract class ApiController : ControllerBase
{
        protected IActionResult ApiResponse<T>(Result<T> result)
        {
            return result.Match<IActionResult>(
                Succ: domain => Ok(domain),
                Fail: HandleBadRequest
            );
        }

        public IActionResult HandleBadRequest(Exception ex) // here is the problematic code
        {
            return ex
                .Match<IActionResult>()
                .With<BusinessException>(BadRequest)
                .Otherwise(e =>
                {
                    LogError(e);
                    return BadRequest(new UnknownError{ Message = e.Message });
                });
        }
}

The public method here is registered by swagger on every controller since it returns an IActionResult.
After we change it into a protect method, problem solved.

I was configuring NSwag for the first time and I had this error too. I think any time there's a public method that does not have an explicit decoration it assumes it's a Post. I fixed this by either explicitly adding a [Http*] decorator (e.g. on implicit Index() methods) or else adding [SwaggerIgnore] to non-callable public methods on WebApi controllers.

Just add Route to your Action Name, like below
[Route("ForgotPassword")]
public async Task ForgotPassword([FromBody]ForgotPasswordRequestViewModel forgotPasswordRequestViewModel)

Was this page helpful?
0 / 5 - 0 ratings