gpt4 book ai didi

c# - 在 ASP.NET 5 (vNext) MVC 6 中实现自定义路由器

转载 作者:IT王子 更新时间:2023-10-29 04:39:36 26 4
gpt4 key购买 nike

我正在尝试转换 this sample RouteBase implementation使用 MVC 6。我已经按照 the example in the Routing project 解决了大部分问题,但我对如何从该方法返回异步 Task 感到困惑。我真的不在乎它是否真的是异步的(为任何可以提供该答案的人欢呼),现在我只想让它正常运行。

我有传出路由功能(意味着 ActionLink 在我输入路由值时工作正常)。问题出在 RouteAsync 方法上。

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);
}

整个方法一直运行到匹配结束。但是当它完成执行时,它不会调用 CustomPage Controller 的 Details 方法,而它应该这样做。我只是在浏览器中看到一个空白的白页。

我添加了 WriteAsync 行,就像在 this post 中所做的那样并将 Hello there 写入空白页,但我不明白为什么 MVC 不调用我的 Controller (在以前的版本中,这工作顺利)。不幸的是,该帖子涵盖了路由的每个部分,除了如何实现 IRouterINamedRouter

如何使 RouteAsync 方法起作用?

完整的 CustomRoute 实现

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 注册

services.AddTransient<ICustomRoute, CustomRoute>();

MVC路由配置

// 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?}");
});

以防万一,我正在使用 Beta 5DNX 4.5.1DNX Core 5

Solution

I created a generic solution that can be used for a simple primary key to URL 2-way mapping in this answer based on the information I learned here. The controller, action, data provider, and datatype of the primary key can be specified when wiring it into MVC 6 routing.

最佳答案

正如@opiants 所说,问题是您在RouteAsync 方法中什么也没做。

如果您打算最终调用 Controller 操作方法,则可以使用以下方法而不是默认 MVC 路由:

By default MVC uses a TemplateRoute with an inner target IRouter. In RouteAsync, the TemplateRoute will delegate to the inner IRouter. This inner router is being set as the MvcRouteHandler by the default builder extensions. In your case, start by adding an IRouter as your inner target:

public class CustomRoute : ICustomRoute
{
private readonly IMemoryCache cache;
private readonly IRouter target;
private object synclock = new object();

public CustomRoute(IMemoryCache cache, IRouter target)
{
this.cache = cache;
this.target = target;
}

然后更新您的启动以将该目标设置为 MvcRouteHandler,它已被设置为 routes.DefaultHandler:

app.UseMvc(routes =>
{
routes.Routes.Add(
new CustomRoute(routes.ServiceProvider.GetRequiredService<IMemoryCache>(),
routes.DefaultHandler));

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?}");
});

最后,更新您的 AsyncRoute 方法以调用内部 IRouter,即 MvcRouteHandler。您可以在 TemplateRoute 中使用该方法的实现作为指南。我很快使用了这种方法并修改了你的方法如下:

public async 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)
{
return;
}


//Invoke MVC controller/action
var oldRouteData = context.RouteData;
var newRouteData = new RouteData(oldRouteData);
newRouteData.Routers.Add(this.target);

// 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.
newRouteData.Values["controller"] = "CustomPage";
newRouteData.Values["action"] = "Details";

// This will be the primary key of the database row.
// It might be an integer or a GUID.
newRouteData.Values["id"] = page.Id;

try
{
context.RouteData = newRouteData;
await this.target.RouteAsync(context);
}
finally
{
// Restore the original values to prevent polluting the route data.
if (!context.IsHandled)
{
context.RouteData = oldRouteData;
}
}
}

更新 RC2

看起来像TemplateRoute不再出现在 RC2 aspnet 路由中。

我查了一下历史,改名为RouteBasecommit 36180ab作为更大重构的一部分。

关于c# - 在 ASP.NET 5 (vNext) MVC 6 中实现自定义路由器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32582232/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com