我正在尝试创建一个简单的自定义版本的Html.ActionLink(...)HtmlHelper
我想为传入的htmlAttributes匿名对象附加一组额外的属性。
public static MvcHtmlString NoFollowActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
{
var customAttributes = new RouteValueDictionary(htmlAttributes) {{"rel", "nofollow"}};
var link = htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, customAttributes);
return link;
}
所以在我看来我会这样:
@Html.NoFollowActionLink("Link Text", "MyAction", "MyController")
我希望能够呈现如下链接:
<a href="/MyController/MyAction" rel="nofollow">Link Text</a>
但我得到了:
<a href="/MyController/MyAction" values="System.Collections.Generic.Dictionary`2+ValueCollection[System.String,System.Object]" keys="System.Collections.Generic.Dictionary`2+KeyCollection[System.String,System.Object]" count="1">Link Text</a>
我已经尝试了将匿名类型转换为RouteValueDictionary的各种方法,添加到它然后将其传递到根ActionLink(...)方法或转换为Dictionary,或者使用HtmlHelper.AnonymousObjectToHtmlAttributes并执行相同但是似乎没什么用。
答案 0 :(得分:11)
您获得的结果是由此来源code:
引起的public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
{
return ActionLink(htmlHelper, linkText, actionName, controllerName, TypeHelper.ObjectToDictionary(routeValues), HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
正如您所见,内部调用了HtmlHelper.AnonymousObjectToHtmlAttributes
。这就是您传递values
对象时获得keys
和RouteValueDictionary
的原因。
只有两种方法匹配您的参数列表:
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes)
第二个重载对你的参数没有任何作用,只是传递它们。 您需要修改代码以调用另一个重载:
public static MvcHtmlString NoFollowActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues = null, object htmlAttributes = null)
{
var routeValuesDict = new RouteValueDictionary(routeValues);
var customAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
if (!customAttributes.ContainsKey("rel"))
customAttributes.Add("rel", "nofollow");
return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValuesDict, customAttributes);
}
当您将routeValues
作为RouteValueDictionary
传递时,会选择其他重载(RouteValueDictionary
正在实施IDictionary<string, object>
,所以没关系,并且返回的链接正确无误。
如果rel
对象中已存在htmlAttributes
属性,则会引发异常:
System.ArgumentException: An item with the same key has already been added.
答案 1 :(得分:1)
由于您想要更新收到的htmlAttributes对象以便添加新属性(rel),您需要将匿名htmlAttributes对象转换为IDictionary<string,object>
(因为您无法向匿名对象添加新属性) )。
这意味着您需要调用ActionLink
方法的this overload,这也需要将匿名routeValues
转换为RouteValueDictionary
。
您可以使用new RouteValueDictionary(routeValues)
轻松转换路线值。要转换html属性,您需要一些反射逻辑,例如as in this question。 (正如slawek在他的回答中已经提到的,你也可以利用RouteValueDictionary
已经实现了一个字典并以相同的方式转换htmlAttributes)
最后你的扩展名是这样的:
public static MvcHtmlString NoFollowActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues = null, object htmlAttributes = null)
{
var htmlAttributesDictionary = new Dictionary<string, object>();
if (htmlAttributes != null)
{
foreach (var prop in htmlAttributes.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
htmlAttributesDictionary.Add(prop.Name, prop.GetValue(htmlAttributes, null));
}
}
htmlAttributesDictionary["rel"] = "nofollow";
var routeValuesDictionary = new RouteValueDictionary(routeValues);
var link = htmlHelper.ActionLink(linkText, actionName, controllerName, routeValuesDictionary, htmlAttributesDictionary);
return link;
}
如果你这样称呼它:
@Html.NoFollowActionLink("Link Text", "MyAction", "MyController", new { urlParam="param1" }, new { foo = "dummy" })
您将获得以下html:
<a foo="dummy" href="/MyController/MyAction?urlParam=param1" rel="nofollow">Link Text</a>
请注意,因为在将原始属性添加到字典中之后添加/更新rel属性,即使调用者为属性指定了另一个值,它也将始终强制rel="nofollow"
。
希望它有所帮助!