Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/293.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# ASP.NET核心UrlHelper自定义参数序列化_C#_Asp.net Core - Fatal编程技术网

C# ASP.NET核心UrlHelper自定义参数序列化

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

我想在ASP.NET Core中构建操作的url。有一种简便的方法可以做到这一点——使用
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类似,但具有更多的逻辑来处理通过路由值公开的任何接口。