编写自定义的模型绑定器

接手了一个重构旧项目的任务,后端是PHP的,前端用的几个零散的东西。重构的原则是 前端基本不动,后端改造成 dotnetcore 。

过程基本顺利,遇到的一个问题的解决方式觉得值得说一下。问题是这样:一个页面的某一个接收参数,有从A页面来的,也有B页面来的,但是A和B页面提交过来的格式是不一样的,A页面是正常的字符,B页面过来的是Unicode 编码,%u5ba2%u6237 的这种。
接收的地方大概是这样:

public IActionResult Search(string cust_id, string project_id)
{}

也就是这里的 cust_id 和 project_id 既可能是正常字符,也可能是 Unicode 编码。

编码的问题很好解决,在网上找了一段正则替换的:

public static string Unicode2String(this string source)
{
    return new Regex(@"%u([0-9A-F]{4})", RegexOptions.IgnoreCase | RegexOptions.Compiled).Replace(
                         source, x => string.Empty + Convert.ToChar(Convert.ToUInt16(x.Result("$1"), 16)));
}

用的时候调一下就可以了。

public IActionResult Search(string cust_id, string project_id)
{
    cust_id = cust_id.Unicode2String();
    project_id = project_id.Unicode2String();
    //....
}

但是后来发现,这样的地方很多,如果每个Action 都加这个东西,有点 Low 。自然就想到了用模型绑定器解决这个问题,就像
我们总用的 FromBody FromQuery 等等。记得以前用过 ModelBinder ,翻了翻以前的代码,写了一个类:

    public class UnicodeModelBinder : IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType != typeof(string))
                return Task.CompletedTask;
            var mn = bindingContext.FieldName;
            string result;
            if (bindingContext.ActionContext.HttpContext.Request.Query.ContainsKey(mn))
                result = bindingContext.ActionContext.HttpContext.Request.Query[mn];
            else
                result = null;
            if(result != null)
            {
                result = result.Unicode2String();
                bindingContext.Result = ModelBindingResult.Success(result);
            }
            return Task.CompletedTask;
        }
    }

功能很简单,就是判断是不是 string ,如果是 判断 Request.Query 是有没有,如果有,就用上面的方法转一下。
然后在 Action 上这样改:

public IActionResult Search([ModelBinder(BinderType = typeof(UnicodeModelBinder))]string cust_id,[ModelBinder(BinderType = typeof(UnicodeModelBinder))] string project_id)
{
    //删掉这句 cust_id = cust_id.Unicode2String();
    //删掉这句 project_id = project_id.Unicode2String();
    //....
}

问题解决了,但是这样有点丑,加的这个东西太长了。然后就想能不能直接定义一个 ModelBinder 这样的东西。
先来看看 ModelBinder 怎么写的。

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
    public class ModelBinderAttribute : Attribute, IBinderTypeProviderMetadata, IBindingSourceMetadata, IModelNameProvider
    {
        public ModelBinderAttribute();
        public ModelBinderAttribute(Type binderType);
        public Type BinderType { get; set; }
        public virtual BindingSource BindingSource { get; protected set; }
        public string Name { get; set; }
    }

那么写一个子类行不行?试试吧。
写了一个类 UnicodeAttribute 继承 ModelBinder,然后指定 BinderType = typeof(UnicodeModelBinder)。
结果发现不行,因为 BinderType 不是 virtual 的,没法 override 。
既然继承不行,那试试代理模式吧。写个代理模式的类,把 ModelBinder 代理一下,代码如下:

    public class UnicodeAttribute : Attribute, IBinderTypeProviderMetadata, IBindingSourceMetadata, IModelNameProvider
    {
        private readonly ModelBinderAttribute modelBinderAttribute = new ModelBinderAttribute() { BinderType = typeof(UnicodeModelBinder) };

        public BindingSource BindingSource => modelBinderAttribute.BindingSource;

        public string Name => modelBinderAttribute.Name;

        public Type BinderType => modelBinderAttribute.BinderType;
    }

然后Action 改成这样:

public IActionResult Search([Unicode]string cust_id,[Unicode] string project_id)
{
    //....
}

跑起来看一下,完美解决。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值