Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The method 'Post' on path '/api/admin/v/product' is registered multiple times #1242

Closed
wongwaijeremy opened this issue Mar 28, 2018 · 11 comments

Comments

@wongwaijeremy
Copy link

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:

    [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 :

                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

@RicoSuter
Copy link
Owner

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

@RicoSuter
Copy link
Owner

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

image

@wongwaijeremy
Copy link
Author

I removed this line and the problem still exist:

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.

    [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)
        {//
        }
    }

@RicoSuter
Copy link
Owner

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

@krze-han
Copy link

krze-han commented Mar 28, 2018

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

@krze-han
Copy link

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;
    });

@wongwaijeremy
Copy link
Author

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()

@wongwaijeremy
Copy link
Author

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.

@mikebridge
Copy link

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.

@mayankgaur
Copy link

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

@jw56578
Copy link

jw56578 commented Apr 4, 2023

Is there any other way to get around this. I am having the same issue but my legacy code has two controller methods with the same route [Route("")] and I cannot change this.
Setting the DefaultUrlTemplate does not help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants