Browse Source

Resolved #5269: allow to easily override a controller.

pull/5750/head
Halil İbrahim Kalkan 6 years ago
parent
commit
71b1384598
  1. 35
      docs/en/Customizing-Application-Modules-Overriding-Services.md
  2. 33
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs
  3. 25
      framework/src/Volo.Abp.Core/System/AbpTypeExtensions.cs
  4. 16
      framework/src/Volo.Abp.Core/System/Collections/Generic/AbpCollectionExtensions.cs
  5. 10
      framework/test/Volo.Abp.Core.Tests/System/AbpTypeExtensions_Tests.cs

35
docs/en/Customizing-Application-Modules-Overriding-Services.md

@ -162,6 +162,41 @@ This example class inherits from the `IdentityUserManager` [domain service](Doma
Check the [localization system](Localization.md) to learn how to localize the error messages.
### Example: Overriding a Controller
````csharp
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.Account;
using Volo.Abp.DependencyInjection;
namespace MyProject.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);
}
}
}
````
This example replaces the `AccountController` (An API Controller defined in the [Account Module](Modules/Account.md)) and overrides the `SendPasswordResetCodeAsync` method.
**`[ExposeServices(typeof(AccountController))]` is essential** here since it registers this controller for the `AccountController` in the dependency injection system. `[Dependency(ReplaceServices = true)]` is also recommended to clear the old registration (even the ASP.NET Core DI system selects the last registered one).
### Overriding Other Classes
Overriding controllers, framework services, view component classes and any other type of classes registered to dependency injection can be overridden just like the examples above.

33
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs

@ -7,24 +7,30 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection;
using Volo.Abp.GlobalFeatures;
using Volo.Abp.Http;
using Volo.Abp.Http.Modeling;
using Volo.Abp.Http.ProxyScripting.Generators;
using Volo.Abp.Reflection;
namespace Volo.Abp.AspNetCore.Mvc.Conventions
{
public class AbpServiceConvention : IAbpServiceConvention, ITransientDependency
{
public ILogger<AbpServiceConvention> Logger { get; set; }
private readonly AbpAspNetCoreMvcOptions _options;
public AbpServiceConvention(IOptions<AbpAspNetCoreMvcOptions> options)
public AbpServiceConvention(
IOptions<AbpAspNetCoreMvcOptions> options)
{
_options = options.Value;
Logger = NullLogger<AbpServiceConvention>.Instance;
}
public void Apply(ApplicationModel application)
@ -34,9 +40,12 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
protected virtual void ApplyForControllers(ApplicationModel application)
{
RemoveDuplicateControllers(application);
foreach (var controller in application.Controllers)
{
var controllerType = controller.ControllerType.AsType();
var configuration = GetControllerSettingOrNull(controllerType);
//TODO: We can remove different behaviour for ImplementsRemoteServiceInterface. If there is a configuration, then it should be applied!
@ -59,6 +68,26 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
}
}
protected virtual void RemoveDuplicateControllers(ApplicationModel application)
{
var derivedControllerModels = new List<ControllerModel>();
foreach (var controllerModel in application.Controllers)
{
var baseControllerTypes = controllerModel.ControllerType
.GetBaseClasses(typeof(Controller))
.Where(t => !t.IsAbstract)
.ToArray();
if (baseControllerTypes.Length > 0)
{
derivedControllerModels.Add(controllerModel);
Logger.LogInformation($"Removing the controller {controllerModel.ControllerType.AssemblyQualifiedName} from the application model since it replaces the controller(s): {baseControllerTypes.Select(c => c.AssemblyQualifiedName).JoinAsString(", ")}");
}
}
application.Controllers.RemoveAll(derivedControllerModels);
}
protected virtual void ConfigureRemoteService(ControllerModel controller, [CanBeNull] ConventionalControllerSetting configuration)
{
ConfigureApiExplorer(controller);

25
framework/src/Volo.Abp.Core/System/AbpTypeExtensions.cs

@ -55,14 +55,27 @@ namespace System
return types.ToArray();
}
/// <summary>
/// Gets all base classes of this type.
/// </summary>
/// <param name="type">The type to get its base classes.</param>
/// <param name="stoppingType">A type to stop going to the deeper base classes. This type will be be included in the returned array</param>
public static Type[] GetBaseClasses([NotNull] this Type type, Type stoppingType)
{
Check.NotNull(type, nameof(type));
var types = new List<Type>();
AddTypeAndBaseTypesRecursively(types, type.BaseType, true, stoppingType);
return types.ToArray();
}
private static void AddTypeAndBaseTypesRecursively(
[NotNull] List<Type> types,
[CanBeNull] Type type,
bool includeObject)
[CanBeNull] Type type,
bool includeObject,
[CanBeNull] Type stoppingType = null)
{
Check.NotNull(types, nameof(types));
if (type == null)
if (type == stoppingType)
{
return;
}
@ -72,7 +85,7 @@ namespace System
return;
}
AddTypeAndBaseTypesRecursively(types, type.BaseType, includeObject);
AddTypeAndBaseTypesRecursively(types, type.BaseType, includeObject, stoppingType);
types.Add(type);
}
}

16
framework/src/Volo.Abp.Core/System/Collections/Generic/AbpCollectionExtensions.cs

@ -105,5 +105,19 @@ namespace System.Collections.Generic
return items;
}
/// <summary>
/// Removes all items from the collection those satisfy the given <paramref name="predicate"/>.
/// </summary>
/// <typeparam name="T">Type of the items in the collection</typeparam>
/// <param name="source">The collection</param>
/// <param name="items">Items to be removed from the list</param>
public static void RemoveAll<T>([NotNull] this ICollection<T> source, IEnumerable<T> items)
{
foreach (var item in items)
{
source.Remove(item);
}
}
}
}
}

10
framework/test/Volo.Abp.Core.Tests/System/AbpTypeExtensions_Tests.cs

@ -6,7 +6,7 @@ namespace System
public class AbpTypeExtensions_Tests
{
[Fact]
public void GetBaseClasses()
public void GetBaseClasses_Excluding_Object()
{
var baseClasses = typeof(MyClass).GetBaseClasses(includeObject: false);
baseClasses.Length.ShouldBe(2);
@ -14,6 +14,14 @@ namespace System
baseClasses[1].ShouldBe(typeof(MyBaseClass2));
}
[Fact]
public void GetBaseClasses_With_StoppingType()
{
var baseClasses = typeof(MyClass).GetBaseClasses(typeof(MyBaseClass1));
baseClasses.Length.ShouldBe(1);
baseClasses[0].ShouldBe(typeof(MyBaseClass2));
}
public abstract class MyBaseClass1
{

Loading…
Cancel
Save