2021-01-08 02:10:13 +11:00
using System.Collections.Generic ;
using System.Linq ;
using Microsoft.AspNetCore.Mvc ;
using Microsoft.AspNetCore.Mvc.Controllers ;
using Microsoft.AspNetCore.Mvc.Infrastructure ;
using Microsoft.Extensions.Logging ;
using Umbraco.Core ;
using Umbraco.Core.Composing ;
using Umbraco.Web.Common.Controllers ;
namespace Umbraco.Web.Website.Routing
{
/// <summary>
2021-02-03 15:47:27 +11:00
/// Used to find a controller/action in the current available routes
2021-01-08 02:10:13 +11:00
/// </summary>
2021-02-03 15:47:27 +11:00
public class ControllerActionSearcher : IControllerActionSearcher
2021-01-08 02:10:13 +11:00
{
2021-02-03 15:47:27 +11:00
private readonly ILogger < ControllerActionSearcher > _logger ;
2021-01-08 02:10:13 +11:00
private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider ;
private const string DefaultActionName = nameof ( RenderController . Index ) ;
/// <summary>
2021-02-03 15:47:27 +11:00
/// Initializes a new instance of the <see cref="ControllerActionSearcher"/> class.
2021-01-08 02:10:13 +11:00
/// </summary>
2021-02-03 15:47:27 +11:00
public ControllerActionSearcher (
ILogger < ControllerActionSearcher > logger ,
2021-01-08 02:10:13 +11:00
IActionDescriptorCollectionProvider actionDescriptorCollectionProvider )
{
_logger = logger ;
_actionDescriptorCollectionProvider = actionDescriptorCollectionProvider ;
}
2021-01-08 02:36:55 +11:00
/// <summary>
/// Determines if a custom controller can hijack the current route
/// </summary>
2021-02-03 15:47:27 +11:00
/// <typeparam name="T">The controller type to find</typeparam>
public ControllerActionSearchResult Find < T > ( string controller , string action )
2021-01-08 02:10:13 +11:00
{
IReadOnlyList < ControllerActionDescriptor > candidates = FindControllerCandidates ( controller , action , DefaultActionName ) ;
// check if there's a custom controller assigned, base on the document type alias.
var customControllerCandidates = candidates . Where ( x = > x . ControllerName . InvariantEquals ( controller ) ) . ToList ( ) ;
// check if that custom controller exists
if ( customControllerCandidates . Count > 0 )
{
ControllerActionDescriptor controllerDescriptor = customControllerCandidates [ 0 ] ;
2021-02-03 15:47:27 +11:00
// ensure the controller is of type T and ControllerBase
if ( TypeHelper . IsTypeAssignableFrom < T > ( controllerDescriptor . ControllerTypeInfo )
2021-01-08 02:10:13 +11:00
& & TypeHelper . IsTypeAssignableFrom < ControllerBase > ( controllerDescriptor . ControllerTypeInfo ) )
{
// now check if the custom action matches
2021-01-12 16:28:00 +11:00
var resultingAction = DefaultActionName ;
if ( action ! = null )
{
var found = customControllerCandidates . FirstOrDefault ( x = > x . ActionName . InvariantEquals ( action ) ) ? . ActionName ;
if ( found ! = null )
{
resultingAction = found ;
}
}
2021-01-08 02:10:13 +11:00
// it's a hijacked route with a custom controller, so return the the values
2021-02-03 15:47:27 +11:00
return new ControllerActionSearchResult (
2021-01-08 02:10:13 +11:00
controllerDescriptor . ControllerName ,
controllerDescriptor . ControllerTypeInfo ,
2021-01-12 16:28:00 +11:00
resultingAction ) ;
2021-01-08 02:10:13 +11:00
}
else
{
_logger . LogWarning (
"The current Document Type {ContentTypeAlias} matches a locally declared controller of type {ControllerName}. Custom Controllers for Umbraco routing must implement '{UmbracoRenderController}' and inherit from '{UmbracoControllerBase}'." ,
controller ,
controllerDescriptor . ControllerTypeInfo . FullName ,
2021-02-03 15:47:27 +11:00
typeof ( T ) . FullName ,
2021-01-08 02:10:13 +11:00
typeof ( ControllerBase ) . FullName ) ;
// we cannot route to this custom controller since it is not of the correct type so we'll continue with the defaults
// that have already been set above.
}
}
2021-02-03 15:47:27 +11:00
return ControllerActionSearchResult . Failed ( ) ;
2021-01-08 02:10:13 +11:00
}
/// <summary>
/// Return a list of controller candidates that match the custom controller and action names
/// </summary>
private IReadOnlyList < ControllerActionDescriptor > FindControllerCandidates ( string customControllerName , string customActionName , string defaultActionName )
{
var descriptors = _actionDescriptorCollectionProvider . ActionDescriptors . Items
. Cast < ControllerActionDescriptor > ( )
. Where ( x = > x . ControllerName . InvariantEquals ( customControllerName )
& & ( x . ActionName . InvariantEquals ( defaultActionName ) | | ( customActionName ! = null & & x . ActionName . InvariantEquals ( customActionName ) ) ) )
. ToList ( ) ;
return descriptors ;
}
}
}