在ASP.Net核心中,您可以通过多种方式为控制器操作生成URL,最新的是标签助手。
对GET-requests使用tag-helpers asp-route用于指定路由参数。据我所知,不支持在路由请求中使用复杂对象。有时,一个页面可以有许多不同的链接指向它自己,可以在每个链接的URL上添加少量的链接。
在我看来,对控制器操作签名的任何修改都需要更改使用该操作的所有标记助手,这似乎是错误的。也就是说,如果将string query添加到控制器,则必须将查询添加到模型,并添加散布在asp-route-query="@Model.Query"文件中的cshtml20个不同位置。使用这种方法是为将来的bug设置代码。
有没有更优雅的方式来处理这个问题?例如,拥有请求对象的某种方式?(例如,来自控制器的请求对象可以放入Model中,并反馈给action URL。)
发布于 2018-12-07 00:27:57
在我的另一个答案中,我找到了一种通过Model提供请求对象的方法。
从SO文章@tseng中,我找到了一个更小的解决方案。此方法不使用Model中的请求对象,但保留所有路由参数,除非显式覆盖。它不允许您指定通过请求对象路由,这通常不是您想要的。但它解决了操作中的问题。
<a asp-controller="Test" asp-action="HelloWorld" asp-all-route-data="@Context.GetQueryParameters()" asp-route-somestring="optional override">Link</a>
这需要一个扩展方法来将查询参数转换为字典。
public static Dictionary GetQueryParameters(this HttpContext context) { return context.Request.Query.ToDictionary(d => d.Key, d => d.Value.ToString()); }
发布于 2018-12-05 22:46:02
这里有一个我不认为你能理解的理由。GET请求故意简单化。它们应该描述特定的资源。它们没有主体,因为你一开始就不应该传递复杂的数据对象。这不是HTTP协议的设计方式。
此外,查询字符串params通常应该是可选的。如果需要一些数据来标识资源,那么它应该是主URI (即路径)的一部分。因此,忽略添加像query参数这样的东西,应该只会导致返回完整的数据集,而不是query定义的某个子集。或者在搜索页面之类的情况下,它通常会导致向用户呈现一个表单来收集query。换句话说,您的操作应该说明参数丢失的原因并相应地处理这种情况。
长话短说,不,我想没有办法“优雅”地处理这件事,但原因是没有必要这样做。如果你正确地设计了你的路线和动作,这通常不是问题。
发布于 2018-12-06 05:47:32
为了解决这个问题,我希望使用一个请求对象作为锚TagHelper的路由参数。这意味着所有路由链接仅定义在一个位置,而不是整个解决方案。对请求对象模型所做的更改会自动传播到<a asp-action>-tags的URL。
这样做的好处是减少了在更改控制器操作的方法签名时需要更改的代码中的位置数。我们只对模型和操作进行本地化更改。
我认为为自定义asp-object-route编写标记助手可能会有所帮助。我研究了Taghelper的链接,以便我的可以在AnchorTagHelper之前运行,但这不起作用。创建实例和嵌套它们需要我对ASP.Net Cores AnchorTagHelper的所有属性进行硬编码,这可能需要在将来进行维护。我也考虑过使用带有UrlHelper的自定义方法来构建URL,但是TagHelper就不能工作了。
我找到的解决方案是按照@kirk-larkin的建议使用asp-all-route-data,以及用于序列化到字典的扩展方法。任何asp-all-route-*都将覆盖asp-all-route-data中的值。
<a asp-controller="Test" asp-action="HelloWorld" asp-all-route-data="@Model.RouteParameters.ToDictionary()" asp-route-somestring="optional override">Link</a>
ASP.Net核心可以反序列化复杂的对象(包括列表和子对象)。
public IActionResult HelloWorld(HelloWorldRequest request) { }
在请求对象中(使用时)通常只有几个简单的属性。但我认为如果它也支持子对象就好了。将对象序列化到Dictionary中通常是使用反射完成的,这可能会很慢。我认为Newtonsoft.Json会比我自己编写简单的反射代码更优化,并发现这个实现已经准备就绪:
public static class ExtensionMethods
{
public static IDictionary ToDictionary(this object metaToken)
{
// From https://geeklearning.io/serialize-an-object-to-an-url-encoded-string-in-csharp/
if (metaToken == null)
{
return null;
}
JToken token = metaToken as JToken;
if (token == null)
{
return ToDictionary(JObject.FromObject(metaToken));
}
if (token.HasValues)
{
var contentData = new Dictionary();
foreach (var child in token.Children().ToList())
{
var childContent = child.ToDictionary();
if (childContent != null)
{
contentData = contentData.Concat(childContent)
.ToDictionary(k => k.Key, v => v.Value);
}
}
return contentData;
}
var jValue = token as JValue;
if (jValue?.Value == null)
{
return null;
}
var value = jValue?.Type == JTokenType.Date ?
jValue?.ToString("o", CultureInfo.InvariantCulture) :
jValue?.ToString(CultureInfo.InvariantCulture);
return new Dictionary { { token.Path, value } };
}
}https://stackoverflow.com/questions/53634430
复制相似问题