Wie implementiert man attributbasiertes Routing im Asp.Net Core-Stil in Asp.Net Mvc?C#

Ein Treffpunkt für C#-Programmierer
Anonymous
 Wie implementiert man attributbasiertes Routing im Asp.Net Core-Stil in Asp.Net Mvc?

Post by Anonymous »

Also habe ich es tatsächlich implementiert, aber es gibt noch ein letztes Problem, das ich lösen muss.
Meine Implementierung kann durch diese beiden Klassen zusammengefasst werden:
Einbinden in den Asp.Net Mvc-Startcode, um TokenizedDirectRouteProvider zu registrieren:

Code: Select all

public static class RouteCollectionExtensions
{
public static void MapMvcAttributeRoutesWithTokens(this RouteCollection routes, IInlineConstraintResolver constraintResolver = null)
{
if (routes == null)
{
throw new ArgumentNullException(nameof(routes));
}

routes.MapMvcAttributeRoutes(constraintResolver ?? new DefaultInlineConstraintResolver(), new TokenizedDirectRouteProvider());
}
}
Und seine Verwendung:

Code: Select all

public static class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapMvcAttributeRoutesWithTokens();

// Conventional route for SimpleController (global)
routes.MapRoute(
name: "Simple",
url: "{controller}/{action}/{id}",
defaults: null,
constraints: new { controller = "Simple" },
namespaces: [typeof(WebProcessorLibrary.Controllers.SimpleController).Namespace]
);
}
}
Wo der TokenizedDirectRouteProvider ist:

Code: Select all

internal class TokenizedDirectRouteProvider : DefaultDirectRouteProvider
{
protected override IReadOnlyList GetControllerDirectRoutes(ControllerDescriptor controllerDescriptor, IReadOnlyList actionDescriptors, IReadOnlyList factories, IInlineConstraintResolver constraintResolver) =>
[.. actionDescriptors.SelectMany(o => GetActionDirectRoutes(o, factories, constraintResolver))];

protected override IReadOnlyList GetActionDirectRoutes(ActionDescriptor actionDescriptor, IReadOnlyList factories, IInlineConstraintResolver constraintResolver) =>
base.GetActionDirectRoutes(actionDescriptor, [..  factories.Select(f => WrapDirectRouteFactory(f, actionDescriptor))], constraintResolver);

private static IDirectRouteFactory WrapDirectRouteFactory(IDirectRouteFactory proto, ActionDescriptor actionDescriptor) =>
new TokenReplacingRouteFactory(proto, actionDescriptor);

/// 
/// Wrapper factory that processes tokens in route templates before creating routes.
/// 
private class TokenReplacingRouteFactory(IDirectRouteFactory m_innerFactory, ActionDescriptor m_actionDescriptor) : IDirectRouteFactory
{
public RouteEntry CreateRoute(DirectRouteFactoryContext context)
{
if (m_innerFactory is RouteAttribute routeAttr)
{
string template = routeAttr.Template;

if (!string.IsNullOrEmpty(template))
{
var areaName = GetAreaName(m_actionDescriptor.ControllerDescriptor);
var hasAreaToken = template.Contains("[area]");
if (!string.IsNullOrEmpty(areaName) != hasAreaToken)
{
var msg = hasAreaToken
? $"The route template '{template}' has the [area] token, but the controller '{m_actionDescriptor.ControllerDescriptor.ControllerName}' is not in an area."
: $"Controller '{m_actionDescriptor.ControllerDescriptor.ControllerName}' is in area '{areaName}', but route template '{template}' does not contain the required '[area]' token.";

throw new InvalidOperationException(msg);
}

var processedTemplate = template
.Replace("[controller]", m_actionDescriptor.ControllerDescriptor.ControllerName)
.Replace("[action]", m_actionDescriptor.ActionName)
.Replace("[area]", areaName);

// If the template contains [area] token and doesn't start with ~, prepend ~ to make it absolute
// This prevents ASP.NET Framework from adding the area prefix automatically
if (hasAreaToken && !processedTemplate.StartsWith("~"))
{
processedTemplate = "~/" + processedTemplate;
}

if (processedTemplate != template)
{
LogEx.ForContext().Debug("'{OriginalTemplate}' ->  '{ProcessedTemplate}' for {Controller}.{Action}",
template, processedTemplate, m_actionDescriptor.ControllerDescriptor.ControllerName, m_actionDescriptor.ActionName);

IDirectRouteFactory newRouteFactory = new RouteAttribute(processedTemplate)
{
Name = routeAttr.Name,
Order = routeAttr.Order
};

// Create the route entry using the new factory
var routeEntry = newRouteFactory.CreateRoute(context);
if (areaName != null)
{
Debug.Assert(areaName.Equals(context.AreaPrefix));
Debug.Assert(areaName.Equals(routeEntry.Route.DataTokens["area"]));
}

return routeEntry;
}
}
}

return m_innerFactory.CreateRoute(context);
}

private string GetAreaName(ControllerDescriptor controllerDescriptor)
{
var routeAreaAttr = controllerDescriptor.GetCustomAttributes(typeof(RouteAreaAttribute), inherit: true)
.OfType()
.FirstOrDefault();

if (routeAreaAttr != null)
{
return routeAreaAttr.AreaName;
}

// Fallback: check the namespace for area name
string controllerNamespace = controllerDescriptor.ControllerType.Namespace ?? string.Empty;
var i = controllerNamespace.IndexOf(".Areas.");
if (i >= 0)
{
int j = controllerNamespace.IndexOf('.', i + 7);
if (j >= 0)
{
return controllerNamespace[(i + 7)..j];
}
}

return null;
}
}
}
Dadurch kann ich Asp.Net Core-Stilattribute wie [Route("[controller]/[action]/{id?}")] verwenden.
Mein Problem ist die Controller.Initialize-Überschreibung:

Code: Select all

[Route("[controller]/[action]/{id?}")]
public class TestController: Controller
{
...
protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
}
}
Darin kann ich wie folgt auf die Routendaten zugreifen:

Code: Select all

var routeData = requestContext.RouteData;
routeData.Values.TryGetValue("controller", out var controller);
routeData.Values.TryGetValue("action", out var action);
routeData.Values.TryGetValue("id", out var id);
Das Problem – wenn ich meine Implementierung des attributbasierten Routings verwende, fehlen die Schlüssel action und id. (Die betreffende Anfrage enthält id im URL-Pfad).
Ein Controller, der auf das konventionsbasierte Routing zurückgreift (

Code: Select all

SimpleController
) hat dieses Problem nicht – alle Schlüssel sind in der Überschreibung der Controller.Initialize-Methode vorhanden.
Ich interessiere mich speziell für die Controller.Initialize-Methode. Und ich kann Asp.Net Core selbst nicht verwenden, obwohl ich dessen attributbasiertes Routing benötigen.
Wie kann das behoben werden?

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post