Ich versuche, this sample RouteBase implementation zu konvertieren, um mit MVC 6 zu arbeiten. Ich habe das meiste davon ausgearbeitet, indem ich the example in the Routing project folgte, aber ich stolpere wie ich zurückkomme die asynchrone Task
von der Methode. Es ist mir wirklich egal, ob es wirklich asynchron ist (Prost für jeden, der diese Antwort geben kann), denn jetzt will ich es nur funktionieren lassen.Ein angepasster IRouter in ASP.NET 5 (vNext) MVC 6
Ich habe die ausgehenden Routen funktioniert (dh ActionLink
funktioniert gut, wenn ich die Route Werte). Das Problem ist mit der Methode.
public Task RouteAsync(RouteContext context)
{
var requestPath = context.HttpContext.Request.Path.Value;
if (!string.IsNullOrEmpty(requestPath) && requestPath[0] == '/')
{
// Trim the leading slash
requestPath = requestPath.Substring(1);
}
// Get the page that matches.
var page = GetPageList()
.Where(x => x.VirtualPath.Equals(requestPath))
.FirstOrDefault();
// If we got back a null value set, that means the URI did not match
if (page != null)
{
var routeData = new RouteData();
// This doesn't work
//var routeData = new RouteData(context.RouteData);
// This doesn't work
//routeData.Routers.Add(this);
// This doesn't work
//routeData.Routers.Add(new MvcRouteHandler());
// TODO: You might want to use the page object (from the database) to
// get both the controller and action, and possibly even an area.
// Alternatively, you could create a route for each table and hard-code
// this information.
routeData.Values["controller"] = "CustomPage";
routeData.Values["action"] = "Details";
// This will be the primary key of the database row.
// It might be an integer or a GUID.
routeData.Values["id"] = page.Id;
context.RouteData = routeData;
// When there is a match, the code executes to here
context.IsHandled = true;
// This test works
//await context.HttpContext.Response.WriteAsync("Hello there");
// This doesn't work
//return Task.FromResult(routeData);
// This doesn't work
//return Task.FromResult(context);
}
// This satisfies the return statement, but
// I'm not sure it is the right thing to return.
return Task.FromResult(0);
}
Die gesamte Methode läuft bis zum Ende, wenn es eine Übereinstimmung gibt. Aber wenn es fertig ausgeführt wird, ruft es nicht die Details
Methode des CustomPage
Controllers auf, wie es sollte. Ich bekomme nur eine leere weiße Seite im Browser.
Ich habe die WriteAsync
Linie wie in this post getan wurde und es Hello there
auf die leere Seite schreibt, aber ich kann nicht verstehen, warum MVC meine Controller Aufruf nicht (in früheren Versionen dieses ohne Probleme gearbeitet). Leider umfasste dieser Beitrag jeden Teil des Routings, außer der Implementierung eines IRouter
oder INamedRouter
.
Wie kann ich die Methode RouteAsync
funktionieren lassen?
Entire CustomRoute Implementierung
using Microsoft.AspNet.Routing;
using Microsoft.Framework.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
public class PageInfo
{
// VirtualPath should not have a leading slash
// example: events/conventions/mycon
public string VirtualPath { get; set; }
public int Id { get; set; }
}
public interface ICustomRoute : IRouter
{ }
public class CustomRoute : ICustomRoute
{
private readonly IMemoryCache cache;
private object synclock = new object();
public CustomRoute(IMemoryCache cache)
{
this.cache = cache;
}
public Task RouteAsync(RouteContext context)
{
var requestPath = context.HttpContext.Request.Path.Value;
if (!string.IsNullOrEmpty(requestPath) && requestPath[0] == '/')
{
// Trim the leading slash
requestPath = requestPath.Substring(1);
}
// Get the page that matches.
var page = GetPageList()
.Where(x => x.VirtualPath.Equals(requestPath))
.FirstOrDefault();
// If we got back a null value set, that means the URI did not match
if (page != null)
{
var routeData = new RouteData();
// TODO: You might want to use the page object (from the database) to
// get both the controller and action, and possibly even an area.
// Alternatively, you could create a route for each table and hard-code
// this information.
routeData.Values["controller"] = "CustomPage";
routeData.Values["action"] = "Details";
// This will be the primary key of the database row.
// It might be an integer or a GUID.
routeData.Values["id"] = page.Id;
context.RouteData = routeData;
context.IsHandled = true;
}
return Task.FromResult(0);
}
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
VirtualPathData result = null;
PageInfo page = null;
// Get all of the pages from the cache.
var pages = GetPageList();
if (TryFindMatch(pages, context.Values, out page))
{
result = new VirtualPathData(this, page.VirtualPath);
context.IsBound = true;
}
return result;
}
private bool TryFindMatch(IEnumerable<PageInfo> pages, IDictionary<string, object> values, out PageInfo page)
{
page = null;
int id;
object idObj;
object controller;
object action;
if (!values.TryGetValue("id", out idObj))
{
return false;
}
id = Convert.ToInt32(idObj);
values.TryGetValue("controller", out controller);
values.TryGetValue("action", out action);
// The logic here should be the inverse of the logic in
// GetRouteData(). So, we match the same controller, action, and id.
// If we had additional route values there, we would take them all
// into consideration during this step.
if (action.Equals("Details") && controller.Equals("CustomPage"))
{
page = pages
.Where(x => x.Id.Equals(id))
.FirstOrDefault();
if (page != null)
{
return true;
}
}
return false;
}
private IEnumerable<PageInfo> GetPageList()
{
string key = "__CustomPageList";
IEnumerable<PageInfo> pages;
// Only allow one thread to poplate the data
if (!this.cache.TryGetValue(key, out pages))
{
lock (synclock)
{
if (!this.cache.TryGetValue(key, out pages))
{
// TODO: Retrieve the list of PageInfo objects from the database here.
pages = new List<PageInfo>()
{
new PageInfo() { Id = 1, VirtualPath = "somecategory/somesubcategory/content1" },
new PageInfo() { Id = 2, VirtualPath = "somecategory/somesubcategory/content2" },
new PageInfo() { Id = 3, VirtualPath = "somecategory/somesubcategory/content3" }
};
this.cache.Set(key, pages,
new MemoryCacheEntryOptions()
{
Priority = CacheItemPriority.NeverRemove,
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(15)
});
}
}
}
return pages;
}
}
CustomRoute DI Registrierung
services.AddTransient<ICustomRoute, CustomRoute>();
MVC Routen Konfiguration
// Add MVC to the request pipeline.
app.UseMvc(routes =>
{
routes.Routes.Add(routes.ServiceProvider.GetService<ICustomRoute>());
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
// Uncomment the following line to add a route for porting Web API 2 controllers.
// routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");
});
Falls es darauf ankommt, ich bin mit Beta 5
, DNX 4.5.1
und DNX Core 5
.
Lösung
habe ich eine generische Lösung, die für eine einfache Primärschlüssel URL 2-Wege-Mapping in this answer auf der Grundlage der Informationen verwendet werden kann, die ich hier gelernt. Der Controller, die Aktion, der Datenprovider und der Datentyp des Primärschlüssels können beim Anschluss an das MVC 6-Routing angegeben werden.
Ja, ich habe darüber nachgedacht, einen inneren IRouter zu haben, aber ich glaube nicht, dass du das brauchst. Wenn context.IsHandle auf false gesetzt wird und früh zurückkehrt, wird es zum nächsten registrierten IRouter verschoben und fällt schließlich auf routes.DefaultHandler zurück (was der MvcRouteHandler ist) – Dealdiane
Sind Sie sicher, dass der DefaultHandler verwendet wird, wenn keine Route eine Übereinstimmung hat? ? Mit Blick auf den Code scheint es nur für die Erweiterungsmethode ['MapRoute'] (https://github.com/aspnet/Routing/blob/dev/src/Microsoft.AspNet.Routing/RouteBuilderExtensions.cs) verwendet zu werden Die MVC-Routen werden mithilfe von TemplateRoute mit einem internen MvcRouteHandler –
hinzugefügt. Überprüfen Sie auch ['RouteBuilder.Build'] (https://github.com/aspnet/Routing/blob/dev/src/Microsoft.AspNet.Routing/RouteBuilder.cs). das wird nur jede definierte Routen hinzufügen, aber nicht die Standard-Handler –