Hello,
Example solution: Version 10.5.0 Layered + Blazor Server + EF Core SQL Server + Multi-Tenancy + Public Web + CMS Kit + Tiered License: Team edition
Based on the documentation “Customizing the Application Modules: Overriding Services,” I am trying to override certain methods of the BlogPostPublicAppService.
The override works, but Swagger throws an error.
In the example below, I’m not overriding anything—I’m just defining my own implementation—and I’m already getting the error. This is a completely clean, new solution,
Everything works fine when overriding the MediaDescriptorAppService; I’ve only encountered this issue when overriding the BlogPostPublicAppService.
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IBlogPostPublicAppService), typeof(BlogPostPublicAppService), typeof(VoxoBlogPostPublicAppService))]
public class VoxoBlogPostPublicAppService : BlogPostPublicAppService
{
public VoxoBlogPostPublicAppService(IBlogRepository blogRepository, IBlogPostRepository blogPostRepository, ITagRepository tagRepository, BlogPostManager blogPostManager)
: base(blogRepository, blogPostRepository, tagRepository, blogPostManager)
{
}
}
Error: 2026. 09. 20. 17:16:31 [Error] Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Conflicting method/path combination "GET api/app/voxo-blog-post-public" for actions - ReplaceTest2.Blog.VoxoBlogPostPublicAppService.GetAsync (ReplaceTest2.Application), ReplaceTest2.Blog.VoxoBlogPostPublicAppService.GetListAsync (ReplaceTest2.Application). Actions require a unique method/path combination for Swagger/OpenAPI 2.0 and 3.0. Use ConflictingActionsResolver as a workaround or provide your own implementation of PathGroupSelector. at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.PrepareGenerateOperation(IGrouping`2 group)
4 Answer(s)
-
0
BlogPostPublicAppServiceis exposed as an HTTP API via Auto API Controllers. When you add your derived class in the Application layer, ABP will treat it as a new application service and (by convention) it will also be turned into an API controller.- With your attributes you also replace the original service in DI, but you still end up with two actions that Swagger sees under the same controller/route group, so Swashbuckle detects a conflict.
In your specific case, both of these methods end up being mapped to the same conventional route:
GetAsync(...)→GET /api/app/voxo-blog-post-publicGetListAsync(...)→GET /api/app/voxo-blog-post-public
Swashbuckle then throws:
Conflicting method/path combination "GET api/app/voxo-blog-post-public" …
This happens because Auto API Controller conventional routing requires unique GET routes, and ABP’s conventional routing is based on method names/signatures. If the method parameters don’t let ASP.NET Core distinguish the templates (e.g., no
{id}segment ends up in the template for one of them), you’ll get exactly this kind of Swagger collision.Fix (recommended): don’t expose your replacement type as an API controller
Keep the replacement only in DI, and ensure only one controller is generated.
Use only the interface/base service exposure, and do not expose the concrete type itself:
[Dependency(ReplaceServices = true)] [ExposeServices(typeof(IBlogPostPublicAppService))] public class VoxoBlogPostPublicAppService : BlogPostPublicAppService { public VoxoBlogPostPublicAppService( IBlogRepository blogRepository, IBlogPostRepository blogPostRepository, ITagRepository tagRepository, BlogPostManager blogPostManager) : base(blogRepository, blogPostRepository, tagRepository, blogPostManager) { } }Notes:
- Remove
typeof(BlogPostPublicAppService)andtypeof(VoxoBlogPostPublicAppService)from[ExposeServices]. - This way ABP still resolves
IBlogPostPublicAppServiceto your class, but the framework won’t treat your class as an additional HTTP endpoint “service type” to generate routes for.
Alternative fix: explicitly set routes (if you must expose the class)
If you really want the class to be treated as a separate API controller, you must ensure
GetAsyncandGetListAsyncdo not map to the same GET path by explicitly adding[HttpGet]/[Route]attributes (or changing method names). But in an override/replacement scenario, that’s usually not what you want.
Links (verified):
- https://abp.io/docs/10.5/framework/architecture/modularity/extending/customizing-application-modules-overriding-services
- https://abp.io/docs/10.5/framework/api-development/auto-controllers
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Using the
[ExposeServices(typeof(IBlogPostPublicAppService))]setting yields the same result.Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
- This specific Swagger error means ASP.NET Core has two GET actions that end up with the exact same route template under ABP’s Auto API Controllers conventions.
- In your case, after replacing
BlogPostPublicAppService, ABP’s conventional routing ends up mapping both:GetAsync(...)andGetListAsync(...)to the same path:GET /api/app/voxo-blog-post-public
That can happen if
GetAsyncis not considered to have anidroute parameter by the conventional route builder (e.g., its parameter name/type doesn’t match what the convention expects), so ABP doesn’t add/{id}and both methods collapse to the same route.Solution (definitive): explicitly define HTTP routes for the conflicting methods in your replacement
Override the methods you want (even if you keep the base logic) and add explicit ASP.NET Core route attributes so they can’t collapse to the same path.
Example pattern:
using Microsoft.AspNetCore.Mvc; public class VoxoBlogPostPublicAppService : BlogPostPublicAppService { public VoxoBlogPostPublicAppService( IBlogRepository blogRepository, IBlogPostRepository blogPostRepository, ITagRepository tagRepository, BlogPostManager blogPostManager) : base(blogRepository, blogPostRepository, tagRepository, blogPostManager) { } [HttpGet] [Route("api/app/voxo-blog-post-public/{id}")] public override Task<BlogPostPublicDto> GetAsync(Guid id) { return base.GetAsync(id); } [HttpGet] [Route("api/app/voxo-blog-post-public")] public override Task<PagedResultDto<BlogPostPublicDto>> GetListAsync(BlogPostPublicGetListInput input) { return base.GetListAsync(input); } }Key points:
- Force
GetAsyncto be.../{id}andGetListAsyncto be the collection route. - This avoids any ambiguity/bug/edge-case in conventional route calculation after replacement.
Why
[ExposeServices(typeof(IBlogPostPublicAppService))]didn’t change itBecause the problem is not “two controllers exist”; it’s “two actions resolve to the same HTTP method + path” for the generated controller of the service type that ends up exposed. Replacing DI alone doesn’t guarantee the auto-controller route templates remain unique after conventions are applied.
Sources (validated)
- https://abp.io/docs/10.5/framework/api-development/auto-controllers
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Yes, that turned out to be the solution.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)