C# ASP.NET核心UrlHelper自定义参数序列化
我想在ASP.NET Core中构建操作的url。有一种简便的方法可以做到这一点——使用C# ASP.NET核心UrlHelper自定义参数序列化,c#,asp.net-core,C#,Asp.net Core,我想在ASP.NET Core中构建操作的url。有一种简便的方法可以做到这一点——使用IUrlHelper接口,任何控制器都可以通过Url属性访问该接口。不幸的是,UrlHelper使用ToString()方法进行参数序列化,我不知道如何为特定类型自定义序列化。这会导致请求模型中的自定义类型出现问题: public class ComplexFilter { public string A { get; set; } public string B { get; set; }
IUrlHelper
接口,任何控制器都可以通过Url
属性访问该接口。不幸的是,UrlHelper
使用ToString()
方法进行参数序列化,我不知道如何为特定类型自定义序列化。这会导致请求模型中的自定义类型出现问题:
public class ComplexFilter {
public string A { get; set; }
public string B { get; set; }
public override string ToString() {
return $"[A = {A}, B = {B}]";
}
}
public class WebRequest {
[FromRoute]
public string Id { get;set; }
[FromQuery]
public ComplexFilter Filter { get;set; }
}
我想使用WebRequest
类作为控制器操作的参数,其url结构如下search/{id}?filter=a-B
:
[HttpGet("search/{id}")]
public ActionResult Search(WebRequest request) {
return Ok();
}
我不想重写我的ComplexFilter
的ToString()
方法,因为它用于应用程序的其他部分,并且具有更友好的记录和调试格式。
在这种情况下,有两个问题:
ComplexFilter
。这可以在TypeDescriptor
的帮助下完成:TypeDescriptor.AddAttributes(typeof(ComplexFilter),newTypeConverterAttribute(typeof(ComplexFilterTypeConverter))代码>
ComplexFilter
以生成链接。如果我简单地调用Url.Action(“Search”,newwebrequest{Id=“Id”,Filter=newcomplexfilter{A=“A”,B=“B”}})
,那么我会得到以下Url:/Action/Id?Filter=[A=\'A\',B=\'B\']
,但我想得到/Action/Id?Filter=A-B
因此,我的问题是-如何自定义UrlBuilder参数序列化行为,为
CustomFilter
类添加自定义序列化逻辑?默认的UrlHelper
(实现IUrlHelper
)将您的值对象转换为键值对列表,其中ToString()
将用于转换属性值。我怀疑在将链接生成委托给某个内部LinkGenerator
(在asp.net核心中使用的新概念)之前,它是否会在内部使用RouteValueDictionary
转换values对象。我没有参考源代码,所以这只是猜测。基本上,除了使用ToString()
而使用其他方法解析属性值之外,没有其他方法可以让它不使用。因此,您可以依赖自定义的UrlHelper
以下解决方案需要您做两件事:
- 将values对象转换为
IDictionary
,在该对象中,您可以使用自己选择的方法来转换属性值,而不是使用默认的ToString()
- 创建一个自定义的
UrlHelper
来覆盖操作
方法,以便在调用基本方法之前应用上面的自定义转换。为方便起见,这是可选的。否则,您需要调用一个方法来应用上述转换,然后再将其传递给默认的UrlHelper.Action
为了帮助识别哪种类型支持在所需逻辑中生成字符串值的自定义方法,可以定义必须由该类型实现的特定接口。我将该接口命名为ILinkGeneratedString
。以下是完整的代码:
public interface ILinkGeneratedString
{
string LinkGeneratedString { get; }
}
//an extension method used to convert property value to a string
public static class UrlValuesExtensions
{
public static object ToUrlValues(this object o)
{
if (o == null || o is RouteValueDictionary) return o;
var values = new Dictionary<string, string>();
//only public readable properties are read
//this excludes the indexers (a special type of property) as well
foreach(var prop in o.GetType().GetProperties()
.Where(p => p.CanRead && p.GetIndexParameters().Length == 0))
{
var pv = prop.GetValue(o);
var value = pv == null ? "" :
pv is ILinkGeneratedString ls ? ls.LinkGeneratedString : pv.ToString();
values[prop.Name] = value;
}
return values;
}
}
//the custom UrlHelper
public class CustomUrlHelper : UrlHelper
{
public CustomUrlHelper(ActionContext actionContext) : base(actionContext)
{
}
public override string Action(UrlActionContext actionContext)
{
//apply the conversion here
actionContext.Values = actionContext?.Values?.ToUrlValues();
return base.Action(actionContext);
}
}
//we need a custom IUrlHelperFactory as well
public class CustomUrlHelperFactory : IUrlHelperFactory
{
public IUrlHelper GetUrlHelper(ActionContext context)
{
return new CustomUrlHelper(context);
}
}
要将自定义的ToString()
用于生成链接的ComplexFilter
,您需要实现ILinkGeneratedString
(正如我前面提到的那样):
services.AddSingleton<IUrlHelperFactory, CustomUrlHelperFactory>();
public class ComplexFilter : ILinkGeneratedString {
public string A { get; set; }
public string B { get; set; }
public override string ToString() {
return $"[A = {A}, B = {B}]";
}
public string LinkGeneratedString => $"{A}-{B}";
}
对于需要类似自定义链接生成的任何其他类型,只需实现ILinkGeneratedString
。这样很方便。谢谢你的回答。我决定使用更简单的方法,引入facade
dto-WebComplexFilter
,它有必要的ToString()
操作符和一对隐式转换操作符,用于从/转换为ComplexFilter
。此外,我非常惊讶,在这种情况下,ASP-NET-Core
无法从外部配置序列化行为。不客气,我认为我在这里介绍的方法基本上就是如何扩展它。默认实现使用ToString
,但该方法在您的情况下不可用,因此需要另一种方法(如我的实现中的LinkGeneratedString
)。在添加这样的自定义UrlHelper后,它的工作方式与默认UrlHelper类似,但具有更多的逻辑来处理通过路由值公开的任何接口。