Abp: How to override ABP API Controller (AccountController)

Created on 2 Sep 2020  路  6Comments  路  Source: abpframework/abp

I am trying to override the SendPasswordResetCodeAsync and ResetPasswordAsync methods of the AccountController but I am having issues. They both are virtual methods.

I followed the Overriding Services guide and created the controller as follows:

namespace MyProject.Controllers
{
    [Dependency(ReplaceServices = true)]
    [Area("account")]
    [RemoteService(IsEnabled = false, IsMetadataEnabled = false, Name = AccountRemoteServiceConsts.RemoteServiceName)]
    [Route("api/account")]
    public class AccountController : Volo.Abp.Account.AccountController, IAccountAppService
    {
        public AccountController(IAccountAppService accountAppService)
            : base(accountAppService) { }

        [HttpPost]
        [Route("send-password-reset-code")]
        public override Task SendPasswordResetCodeAsync(SendPasswordResetCodeDto input)
        {
            // my logic...

            return AccountAppService.SendPasswordResetCodeAsync(input);
        }

        [HttpPost]
        [Route("reset-password")]
        public override Task ResetPasswordAsync(ResetPasswordDto input)
        {
            // my logic...

            return AccountAppService.ResetPasswordAsync(input);
        }
    }
}

But I get the following exception when using the endpoints:

Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints. Matches:
Volo.Abp.Account.AccountController.SendPasswordResetCodeAsync (Volo.Abp.Account.HttpApi)
MyProject.Controllers.AccountController.SendPasswordResetCodeAsync (MyProject.HttpApi.Host)

So I scour the existing abp issues and come across this from a long time ago: https://github.com/abpframework/abp/issues/2428#issuecomment-567778737 so add a custom routing convention i.e.

context.Services.Configure<MvcOptions>(options =>
{
    options.Conventions.Add(new MyRoutingConvention());
});

But I am not happy with this approach because it removes all the original /account routes so then I have to use: [RemoteService(true)], when I only want to override a few methods of the existing controller.

Is there a better approach now in ABP 3.1.0 and could you add a section to the 'Overriding Services' guide to explain how to override API Controllers specifically?

effort-2 high problem

Most helpful comment

With the v3.3, you will be able to do it like;

````csharp
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.Account;
using Volo.Abp.DependencyInjection;

namespace MyCompanyName.MyProjectName.Controllers
{
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(AccountController))]
public class MyAccountController : AccountController
{
public MyAccountController(IAccountAppService accountAppService)
: base(accountAppService)
{

    }

    public override async Task SendPasswordResetCodeAsync(SendPasswordResetCodeDto input)
    {
        Logger.LogInformation("Your custom logic...");

        await base.SendPasswordResetCodeAsync(input);
    }
}

}
````

No need to repeat all the route and other attributes. It is that simple and documented: https://github.com/abpframework/abp/blob/dev/docs/en/Customizing-Application-Modules-Overriding-Services.md#example-overriding-a-controller

All 6 comments

@realLiangshiwei May it be updated in the the 'Overriding Services' guide ?

I will work on this in the next milestone.

See my comment:

https://support.abp.io/QA/Questions/438#answer-0ee7923f-f88b-b6cc-6f63-39f7f9779443

With the v3.3, you will be able to do it like;

````csharp
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.Account;
using Volo.Abp.DependencyInjection;

namespace MyCompanyName.MyProjectName.Controllers
{
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(AccountController))]
public class MyAccountController : AccountController
{
public MyAccountController(IAccountAppService accountAppService)
: base(accountAppService)
{

    }

    public override async Task SendPasswordResetCodeAsync(SendPasswordResetCodeDto input)
    {
        Logger.LogInformation("Your custom logic...");

        await base.SendPasswordResetCodeAsync(input);
    }
}

}
````

No need to repeat all the route and other attributes. It is that simple and documented: https://github.com/abpframework/abp/blob/dev/docs/en/Customizing-Application-Modules-Overriding-Services.md#example-overriding-a-controller

666

Was this page helpful?
0 / 5 - 0 ratings