接手了一個重構舊項目的任務,後端是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 編碼。ide
編碼的問題很好解決,在網上找了一段正則替換的:this
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 ,翻了翻之前的代碼,寫了一個類:spa
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 怎麼寫的。code
[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 代理一下,代碼以下:blog
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) { //.... }
跑起來看一下,完美解決。