环境:Windows 10,Visual Studio 2017,ASP.NET Core,.NET Core 2.2
————————————————————————————————————————————————————
最近在做一个项目升级,要把之前.net framework升级到.net core,有用到Action参数绑定(DefaultModelBinder)这个类去做一些东西,因为.net core里面已经没有 DefaultModelBinder 了,所以要改用 IModelBinder。
继承IModelBinder接口,在进行Action参数绑定的时候,每多一个参数,就会执行一次BindModelAsync,但是除了第一次之外就无法通过bindingContext.ActionContext.HttpContext.Request.Body获取传进来的数据,所以要把数据保存起来。(我开发的时候观察到的情况是这样的,不保证正确)
在BindModelAsync里面可以通过ModelBindingContext 获得很多东西,比如:HttpContext、Files、ContentType等。
在做完我们自己的事情后,通过bindingContext.Result = ModelBindingResult.Success(obj);返回数据。
详细的看代码。
IModelBinder接口,自定义参数绑定实现代码
using DevMate.Common;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;
using System.Threading.Tasks;
namespace WSM.Manage.Common
{
public class CustomModelBinder : IModelBinder
{
private readonly object _locker = new object();
private const string cachekey = "JsonModel";
private int paramIndex = 0;
private const string paramIndexKey = "ParamIndex";
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var request = bindingContext.ActionContext.HttpContext.Request;
if (request.HttpContext.Request.ContentType.ToLower() != "application/json" && request.Form.Files.Count > 0)
return Task.CompletedTask;
JObject data = request.HttpContext.Items[cachekey] as JObject;
if (_data == null)
{
using (var sr = new StreamReader(request.Body))
{
var json = sr.ReadToEnd();
data = JsonConvert.DeserializeObject<JObject>(json);
request.HttpContext.Items[cachekey] = data;
}
}
string Sign = data["Sign"] == null ? null : _data["Sign"].ToString();
if (string.IsNullOrEmpty(Sign))
return Task.CompletedTask;
paramIndex = request.HttpContext.Items[paramIndexKey] == null ? 0 : int.Parse("0" + request.HttpContext.Items[paramIndexKey]);
string key = "param";
lock (_locker)
{
key = "param" + paramIndex;
paramIndex++;
request.HttpContext.Items[paramIndexKey] = paramIndex;
}
object obj = null;
string jsonField = DataConvert.ToString(data[key]);
if (bindingContext.ModelType == typeof(JArray))
{
obj = DataConvert.ToJArray(jsonField);
}
else if (bindingContext.ModelType.IsClass || bindingContext.ModelType.IsArray || bindingContext.ModelType.IsGenericType)
{
obj = JsonConvert.DeserializeObject(jsonField, bindingContext.ModelType);
}
else
{
obj = data[key].ToObject(bindingContext.ModelType);
}
bindingContext.Result = ModelBindingResult.Success(obj);
return Task.CompletedTask;
}
}
}
IModelBinderProvider
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace WSM.Manage.Common
{
public class CustomModelBinderProvider: IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
return new CustomModelBinder();
}
}
}
绑定到ModelBinderProviders,放在第一个
services.AddMvc(options =>
{
options.ModelBinderProviders.Insert(0, new CustomModelBinderProvider());
});
———————————————————————————————————————————————————————
!重大失误
上面说第二次及以后进来拿不到Body的数据,这是不对的,不是拿不到数据,而是第一次读取字节流的时候读到字节流最后的位置,再次读取字节流的时候就拿不到数据,需要重置一下。
———————————————————————————————————————————————————————
2020年7月29日更新
补充一点,GET请求方式的处理方法,可以通过Request.Method判断是那种请求方式,(ModelBindingContext)bindingContext.OriginalModelName 可以获得当前参数名,request.Query[bindingContext.OriginalModelName] 这样就能拿到GET请求传进来的参数值。
private void HandleGetRequest(ModelBindingContext bindingContext)
{
HttpRequest request = bindingContext.ActionContext.HttpContext.Request;
if (request.Query.Count == 0)
{
return;
}
bindingContext.Result = ModelBindingResult.Success(request.Query[bindingContext.OriginalModelName] + "");
}