Ver código fonte

优化BodyOrDefaultModelBinder

懒得勤快 2 anos atrás
pai
commit
ce1baf4102

+ 1 - 1
BenchmarkTest/BenchmarkTest.csproj

@@ -7,7 +7,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="BenchmarkDotNet" Version="0.13.6" />
+    <PackageReference Include="BenchmarkDotNet" Version="0.13.7" />
   </ItemGroup>
 
   <ItemGroup>

+ 275 - 202
Masuit.Tools.Abstractions/Extensions/BaseType/IConvertibleExtensions.cs

@@ -1,211 +1,284 @@
 using System;
 using System.ComponentModel;
 using System.Globalization;
+using System.Linq;
 
 namespace Masuit.Tools;
 
 public static class IConvertibleExtensions
 {
-    public static bool IsNumeric(this Type type)
-    {
-        switch (Type.GetTypeCode(type))
-        {
-            case TypeCode.Byte:
-            case TypeCode.SByte:
-            case TypeCode.UInt16:
-            case TypeCode.UInt32:
-            case TypeCode.UInt64:
-            case TypeCode.Int16:
-            case TypeCode.Int32:
-            case TypeCode.Int64:
-            case TypeCode.Decimal:
-            case TypeCode.Double:
-            case TypeCode.Single:
-                return true;
-
-            default:
-                return false;
-        }
-    }
-
-    /// <summary>
-    /// 类型直转
-    /// </summary>
-    /// <typeparam name="T"></typeparam>
-    /// <param name="value"></param>
-    /// <returns></returns>
-
-    public static T ConvertTo<T>(this IConvertible value) where T : IConvertible
-    {
-        if (value != null)
-        {
-            var type = typeof(T);
-            if (value.GetType() == type)
-            {
-                return (T)value;
-            }
-
-            if (type.IsNumeric())
-            {
-                return (T)value.ToType(type, new NumberFormatInfo());
-            }
-
-            if (value == DBNull.Value)
-            {
-                return default;
-            }
-
-            if (type.IsEnum)
-            {
-                return (T)Enum.Parse(type, value.ToString(CultureInfo.InvariantCulture));
-            }
-
-            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
-            {
-                var underlyingType = Nullable.GetUnderlyingType(type);
-                return (T)(underlyingType!.IsEnum ? Enum.Parse(underlyingType, value.ToString(CultureInfo.CurrentCulture)) : Convert.ChangeType(value, underlyingType));
-            }
-
-            TypeConverter converter = TypeDescriptor.GetConverter(value);
-            if (converter != null)
-            {
-                if (converter.CanConvertTo(type))
-                {
-                    return (T)converter.ConvertTo(value, type);
-                }
-            }
-
-            converter = TypeDescriptor.GetConverter(type);
-            if (converter != null)
-            {
-                if (converter.CanConvertFrom(value.GetType()))
-                {
-                    return (T)converter.ConvertFrom(value);
-                }
-            }
-            return (T)Convert.ChangeType(value, type);
-        }
-
-        return (T)value;
-    }
-
-    /// <summary>
-    /// 类型直转
-    /// </summary>
-    /// <typeparam name="T"></typeparam>
-    /// <param name="value"></param>
-    /// <param name="defaultValue">转换失败的默认值</param>
-    /// <returns></returns>
-    public static T TryConvertTo<T>(this IConvertible value, T defaultValue = default) where T : IConvertible
-    {
-        try
-        {
-            return ConvertTo<T>(value);
-        }
-        catch
-        {
-            return defaultValue;
-        }
-    }
-
-    /// <summary>
-    /// 类型直转
-    /// </summary>
-    /// <typeparam name="T"></typeparam>
-    /// <param name="value"></param>
-    /// <param name="result">转换失败的默认值</param>
-    /// <returns></returns>
-    public static bool TryConvertTo<T>(this IConvertible value, out T result) where T : IConvertible
-    {
-        try
-        {
-            result = ConvertTo<T>(value);
-            return true;
-        }
-        catch
-        {
-            result = default;
-            return false;
-        }
-    }
-
-    /// <summary>
-    /// 类型直转
-    /// </summary>
-    /// <param name="value"></param>
-    /// <param name="type">目标类型</param>
-    /// <param name="result">转换失败的默认值</param>
-    /// <returns></returns>
-    public static bool TryConvertTo(this IConvertible value, Type type, out object result)
-    {
-        try
-        {
-            result = ConvertTo(value, type);
-            return true;
-        }
-        catch
-        {
-            result = default;
-            return false;
-        }
-    }
-
-    /// <summary>
-    /// 类型直转
-    /// </summary>
-    /// <param name="value"></param>
-    /// <param name="type">目标类型</param>
-    /// <returns></returns>
-    public static object ConvertTo(this IConvertible value, Type type)
-    {
-        if (value == null)
-        {
-            return default;
-        }
-
-        if (value.GetType() == type)
-        {
-            return value;
-        }
-
-        if (value == DBNull.Value)
-        {
-            return null;
-        }
-
-        if (type.IsEnum)
-        {
-            return Enum.Parse(type, value.ToString(CultureInfo.InvariantCulture));
-        }
-
-        if (type.IsNumeric())
-        {
-            return value.ToType(type, new NumberFormatInfo());
-        }
-
-        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
-        {
-            var underlyingType = Nullable.GetUnderlyingType(type);
-            return underlyingType!.IsEnum ? Enum.Parse(underlyingType, value.ToString(CultureInfo.CurrentCulture)) : Convert.ChangeType(value, underlyingType);
-        }
-
-        var converter = TypeDescriptor.GetConverter(value);
-        if (converter != null)
-        {
-            if (converter.CanConvertTo(type))
-            {
-                return converter.ConvertTo(value, type);
-            }
-        }
-
-        converter = TypeDescriptor.GetConverter(type);
-        if (converter != null)
-        {
-            if (converter.CanConvertFrom(value.GetType()))
-            {
-                return converter.ConvertFrom(value);
-            }
-        }
-        return Convert.ChangeType(value, type);
-    }
+	public static bool IsNumeric(this Type type)
+	{
+		switch (Type.GetTypeCode(type))
+		{
+			case TypeCode.Byte:
+			case TypeCode.SByte:
+			case TypeCode.UInt16:
+			case TypeCode.UInt32:
+			case TypeCode.UInt64:
+			case TypeCode.Int16:
+			case TypeCode.Int32:
+			case TypeCode.Int64:
+			case TypeCode.Decimal:
+			case TypeCode.Double:
+			case TypeCode.Single:
+				return true;
+
+			default:
+				return false;
+		}
+	}
+
+	/// <summary>
+	/// 类型直转
+	/// </summary>
+	/// <typeparam name="T"></typeparam>
+	/// <param name="value"></param>
+	/// <returns></returns>
+
+	public static T ConvertTo<T>(this IConvertible value) where T : IConvertible
+	{
+		if (value != null)
+		{
+			var type = typeof(T);
+			if (value.GetType() == type)
+			{
+				return (T)value;
+			}
+
+			if (type.IsNumeric())
+			{
+				return (T)value.ToType(type, new NumberFormatInfo());
+			}
+
+			if (value == DBNull.Value)
+			{
+				return default;
+			}
+
+			if (type.IsEnum)
+			{
+				return (T)Enum.Parse(type, value.ToString(CultureInfo.InvariantCulture));
+			}
+
+			if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
+			{
+				var underlyingType = Nullable.GetUnderlyingType(type);
+				return (T)(underlyingType!.IsEnum ? Enum.Parse(underlyingType, value.ToString(CultureInfo.CurrentCulture)) : Convert.ChangeType(value, underlyingType));
+			}
+
+			var converter = TypeDescriptor.GetConverter(value);
+			if (converter.CanConvertTo(type))
+			{
+				return (T)converter.ConvertTo(value, type);
+			}
+
+			converter = TypeDescriptor.GetConverter(type);
+			if (converter.CanConvertFrom(value.GetType()))
+			{
+				return (T)converter.ConvertFrom(value);
+			}
+
+			return (T)Convert.ChangeType(value, type);
+		}
+
+		return (T)value;
+	}
+
+	/// <summary>
+	/// 类型直转
+	/// </summary>
+	/// <typeparam name="T"></typeparam>
+	/// <param name="value"></param>
+	/// <param name="defaultValue">转换失败的默认值</param>
+	/// <returns></returns>
+	public static T TryConvertTo<T>(this IConvertible value, T defaultValue = default) where T : IConvertible
+	{
+		try
+		{
+			return ConvertTo<T>(value);
+		}
+		catch
+		{
+			return defaultValue;
+		}
+	}
+
+	/// <summary>
+	/// 类型直转
+	/// </summary>
+	/// <typeparam name="T"></typeparam>
+	/// <param name="value"></param>
+	/// <param name="result">转换失败的默认值</param>
+	/// <returns></returns>
+	public static bool TryConvertTo<T>(this IConvertible value, out T result) where T : IConvertible
+	{
+		try
+		{
+			result = ConvertTo<T>(value);
+			return true;
+		}
+		catch
+		{
+			result = default;
+			return false;
+		}
+	}
+
+	/// <summary>
+	/// 类型直转
+	/// </summary>
+	/// <param name="value"></param>
+	/// <param name="type">目标类型</param>
+	/// <param name="result">转换失败的默认值</param>
+	/// <returns></returns>
+	public static bool TryConvertTo(this IConvertible value, Type type, out object result)
+	{
+		try
+		{
+			result = ConvertTo(value, type);
+			return true;
+		}
+		catch
+		{
+			result = default;
+			return false;
+		}
+	}
+
+	/// <summary>
+	/// 类型直转
+	/// </summary>
+	/// <param name="value"></param>
+	/// <param name="type">目标类型</param>
+	/// <returns></returns>
+	public static object ConvertTo(this IConvertible value, Type type)
+	{
+		if (value == null)
+		{
+			return default;
+		}
+
+		if (value.GetType() == type)
+		{
+			return value;
+		}
+
+		if (value == DBNull.Value)
+		{
+			return null;
+		}
+
+		if (type.IsAssignableFrom(typeof(string)))
+		{
+			return value.ToString();
+		}
+
+		if (type.IsEnum)
+		{
+			return Enum.Parse(type, value.ToString(CultureInfo.InvariantCulture));
+		}
+
+		if (type.IsAssignableFrom(typeof(Guid)))
+		{
+			return Guid.Parse(value.ToString());
+		}
+
+		if (type.IsAssignableFrom(typeof(DateTime)))
+		{
+			return DateTime.Parse(value.ToString());
+		}
+
+		if (type.IsAssignableFrom(typeof(DateTimeOffset)))
+		{
+			return DateTimeOffset.Parse(value.ToString());
+		}
+
+		if (type.IsNumeric())
+		{
+			return value.ToType(type, new NumberFormatInfo());
+		}
+
+		if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
+		{
+			var underlyingType = Nullable.GetUnderlyingType(type);
+			return underlyingType!.IsEnum ? Enum.Parse(underlyingType, value.ToString(CultureInfo.CurrentCulture)) : Convert.ChangeType(value, underlyingType);
+		}
+
+		var converter = TypeDescriptor.GetConverter(value);
+		if (converter.CanConvertTo(type))
+		{
+			return converter.ConvertTo(value, type);
+		}
+
+		converter = TypeDescriptor.GetConverter(type);
+		return converter.CanConvertFrom(value.GetType()) ? converter.ConvertFrom(value) : Convert.ChangeType(value, type);
+	}
+
+	/// <summary>
+	/// 对象类型转换
+	/// </summary>
+	/// <param name="this">当前值</param>
+	/// <returns>转换后的对象</returns>
+	public static T ChangeTypeTo<T>(this object @this)
+	{
+		return (T)ChangeType(@this, typeof(T));
+	}
+
+	/// <summary>
+	/// 对象类型转换
+	/// </summary>
+	/// <param name="this">当前值</param>
+	/// <param name="type">指定类型的类型</param>
+	/// <returns>转换后的对象</returns>
+	public static object ChangeType(this object @this, Type type)
+	{
+		var currType = Nullable.GetUnderlyingType(@this.GetType()) ?? @this.GetType();
+		type = Nullable.GetUnderlyingType(type) ?? type;
+		if (@this == DBNull.Value)
+		{
+			if (!type.IsValueType)
+			{
+				return null;
+			}
+
+			throw new Exception("不能将null值转换为" + type.Name + "类型!");
+		}
+
+		if (currType == type)
+		{
+			return @this;
+		}
+
+		if (type.IsAssignableFrom(typeof(string)))
+		{
+			return @this.ToString();
+		}
+
+		if (type.IsEnum)
+		{
+			return Enum.Parse(type, @this.ToString(), true);
+		}
+
+		if (type.IsAssignableFrom(typeof(Guid)))
+		{
+			return Guid.Parse(@this.ToString());
+		}
+
+		if (!type.IsArray || !currType.IsArray)
+		{
+			return Convert.ChangeType(@this, type);
+		}
+
+		var length = ((Array)@this).Length;
+		var targetType = Type.GetType(type.FullName.Trim('[', ']'));
+		var array = Array.CreateInstance(targetType, length);
+		for (int j = 0; j < length; j++)
+		{
+			var tmp = ((Array)@this).GetValue(j);
+			array.SetValue(ChangeType(tmp, targetType), j);
+		}
+
+		return array;
+	}
 }

+ 34 - 0
Masuit.Tools.Abstractions/Extensions/BaseType/ObjectExtensions.cs

@@ -24,6 +24,40 @@ namespace Masuit.Tools
 			return type.IsValueType & type.IsPrimitive;
 		}
 
+		/// <summary>
+		/// 判断类型是否是常见的简单类型
+		/// </summary>
+		/// <param name="type"></param>
+		/// <returns></returns>
+		public static bool IsSimpleType(this Type type)
+		{
+			//IsPrimitive 判断是否为基础类型。
+			//基元类型为 Boolean、 Byte、 SByte、 Int16、 UInt16、 Int32、 UInt32、 Int64、 UInt64、 IntPtr、 UIntPtr、 Char、 Double 和 Single。
+			var t = Nullable.GetUnderlyingType(type) ?? type;
+			return t.IsPrimitive || t.IsEnum || t == typeof(decimal) || t == typeof(string) || t == typeof(Guid) || t == typeof(TimeSpan) || t == typeof(Uri);
+		}
+
+		/// <summary>
+		/// 是否是常见类型的 数组形式 类型
+		/// </summary>
+		/// <param name="type"></param>
+		/// <returns></returns>
+		public static bool IsSimpleArrayType(this Type type)
+		{
+			return type.IsArray && Type.GetType(type.FullName!.Trim('[', ']')).IsSimpleType();
+		}
+
+		/// <summary>
+		/// 是否是常见类型的 泛型形式 类型
+		/// </summary>
+		/// <param name="type"></param>
+		/// <returns></returns>
+		public static bool IsSimpleListType(this Type type)
+		{
+			type = Nullable.GetUnderlyingType(type) ?? type;
+			return type.IsGenericType && type.GetGenericArguments().Length == 1 && type.GetGenericArguments().FirstOrDefault().IsSimpleType();
+		}
+
 		public static bool IsDefaultValue(this object value)
 		{
 			if (value == default)

+ 1 - 1
Masuit.Tools.Abstractions/Masuit.Tools.Abstractions.csproj

@@ -3,7 +3,7 @@
         <TargetFrameworks>netstandard2.0;netstandard2.1;net461;net5;net6;net7</TargetFrameworks>
         <LangVersion>latest</LangVersion>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-        <Version>2.6.4</Version>
+        <Version>2.6.4.1</Version>
         <Authors>懒得勤快</Authors>
         <Description>新手友好的C#万能工具库,码数吐司库,Masuit.Tools基础公共库(适用于.NET4.6.1/.NET Standard2.0及以上项目),包含一些常用的操作类,大都是静态类,加密解密,反射操作,Excel简单导出,权重随机筛选算法,分布式短id,表达式树,linq扩展,文件压缩,多线程下载和FTP客户端,硬件信息,字符串扩展方法,日期时间扩展操作,中国农历,大文件拷贝,图像裁剪,验证码,断点续传,集合扩展等常用封装。
             官网教程:https://tools.masuit.org

+ 1 - 1
Masuit.Tools.AspNetCore/Masuit.Tools.AspNetCore.csproj

@@ -18,7 +18,7 @@
         <Product>Masuit.Tools.AspNetCore</Product>
         <PackageId>Masuit.Tools.AspNetCore</PackageId>
         <LangVersion>latest</LangVersion>
-        <Version>1.2.4</Version>
+        <Version>1.2.4.1</Version>
         <RepositoryType></RepositoryType>
         <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
         <FileVersion>1.1.9</FileVersion>

+ 48 - 0
Masuit.Tools.AspNetCore/ModelBinder/BindType.cs

@@ -0,0 +1,48 @@
+namespace Masuit.Tools.AspNetCore.ModelBinder;
+
+/// <summary>
+/// 取值方式
+/// </summary>
+[Flags]
+public enum BindType
+{
+	/// <summary>
+	/// 自动取值(1.取请求数据中的某个值,2.请求数据当成一个对象取值)
+	/// </summary>
+	Default = Body | Query | Form | Header | Cookie | Route | Services,
+
+	/// <summary>
+	/// 从查询字符串获取值
+	/// </summary>
+	Query = 1,
+
+	/// <summary>
+	/// 从请求正文中获取值
+	/// </summary>
+	Body = 2,
+
+	/// <summary>
+	/// 从已发布的表单字段中获取值
+	/// </summary>
+	Form = 4,
+
+	/// <summary>
+	/// 从 HTTP 标头中获取值
+	/// </summary>
+	Header = 8,
+
+	/// <summary>
+	/// 从 Cookie 中取值
+	/// </summary>
+	Cookie = 16,
+
+	/// <summary>
+	/// 从路由数据中获取值
+	/// </summary>
+	Route = 32,
+
+	/// <summary>
+	/// 从依赖关系注入容器中获取类型的实例
+	/// </summary>
+	Services = 64
+}

+ 12 - 29
Masuit.Tools.AspNetCore/ModelBinder/BodyOrDefaultModelBinderMiddleware.cs → Masuit.Tools.AspNetCore/ModelBinder/BodyOrDefaultBinderMiddleware.cs

@@ -5,30 +5,22 @@ using Newtonsoft.Json.Linq;
 
 namespace Masuit.Tools.AspNetCore.ModelBinder;
 
-public sealed class BodyOrDefaultModelBinderMiddleware
+public sealed class BodyOrDefaultBinderMiddleware
 {
 	private readonly RequestDelegate _next;
-	private readonly ILogger<BodyOrDefaultModelBinderMiddleware> _logger;
+	private readonly ILogger<BodyOrDefaultBinderMiddleware> _logger;
 
-	public BodyOrDefaultModelBinderMiddleware(RequestDelegate next, ILogger<BodyOrDefaultModelBinderMiddleware> logger)
+	public BodyOrDefaultBinderMiddleware(RequestDelegate next, ILogger<BodyOrDefaultBinderMiddleware> logger)
 	{
 		_next = next;
 		_logger = logger;
 	}
 
-	public static JObject JsonObject { get; private set; }
-
-	public static XDocument XmlObject { get; private set; }
-
 	public Task Invoke(HttpContext context)
 	{
-		JsonObject = null;
-		XmlObject = null;
-
 		var contentType = context.Request.ContentType;
 		string mediaType;
 		var charSet = "utf-8";
-
 		if (string.IsNullOrWhiteSpace(contentType))
 		{
 			//表单提交
@@ -36,16 +28,16 @@ public sealed class BodyOrDefaultModelBinderMiddleware
 		}
 		else
 		{
-			var cttType = new ContentType(contentType);
-			if (!string.IsNullOrWhiteSpace(cttType.CharSet))
+			var type = new ContentType(contentType);
+			if (!string.IsNullOrWhiteSpace(type.CharSet))
 			{
-				charSet = cttType.CharSet;
+				charSet = type.CharSet;
 			}
-			mediaType = cttType.MediaType.ToLower();
-		}
 
-		Encoding encoding = Encoding.GetEncoding(charSet);
+			mediaType = type.MediaType.ToLower();
+		}
 
+		var encoding = Encoding.GetEncoding(charSet);
 		if (mediaType == "application/x-www-form-urlencoded")
 		{
 			//普通表单提交
@@ -56,10 +48,7 @@ public sealed class BodyOrDefaultModelBinderMiddleware
 		}
 		else if (mediaType == "application/json")
 		{
-			#region json数据提交
-
 			var body = context.GetBodyString(encoding)?.Trim();
-
 			if (string.IsNullOrWhiteSpace(body))
 			{
 				return _next(context);
@@ -72,7 +61,7 @@ public sealed class BodyOrDefaultModelBinderMiddleware
 
 			try
 			{
-				JsonObject = JObject.Parse(body);
+				context.Items.AddOrUpdate("BodyOrDefaultModelBinder@JsonBody", _ => JObject.Parse(body), (_, _) => JObject.Parse(body));
 				return _next(context);
 			}
 			catch (Exception ex)
@@ -80,15 +69,10 @@ public sealed class BodyOrDefaultModelBinderMiddleware
 				_logger.LogError(ex, "Parsing json failed:" + body);
 				return _next(context);
 			}
-
-			#endregion json数据提交
 		}
 		else if (mediaType == "application/xml")
 		{
-			#region xml数据提交
-
 			var body = context.GetBodyString(encoding)?.Trim();
-
 			if (string.IsNullOrWhiteSpace(body))
 			{
 				return _next(context);
@@ -96,7 +80,7 @@ public sealed class BodyOrDefaultModelBinderMiddleware
 
 			try
 			{
-				XmlObject = XDocument.Parse(body);
+				context.Items.AddOrUpdate("BodyOrDefaultModelBinder@XmlBody", _ => XDocument.Parse(body), (_, _) => XDocument.Parse(body));
 				return _next(context);
 			}
 			catch (Exception ex)
@@ -104,9 +88,8 @@ public sealed class BodyOrDefaultModelBinderMiddleware
 				_logger.LogError(ex, "Parsing xml failed:" + body);
 				return _next(context);
 			}
-
-			#endregion xml数据提交
 		}
+
 		return _next(context);
 	}
 }

+ 0 - 243
Masuit.Tools.AspNetCore/ModelBinder/BodyOrDefaultModelBinderProviderSetup.cs

@@ -1,243 +0,0 @@
-using System.Net.Mime;
-using System.Text;
-using Microsoft.AspNetCore.Mvc.Controllers;
-using Microsoft.AspNetCore.Mvc.ModelBinding;
-using Microsoft.Extensions.Primitives;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-namespace Masuit.Tools.AspNetCore.ModelBinder;
-
-internal static class BodyOrDefaultModelBinderProviderSetup
-{
-	/// <summary>
-	/// 获取绑定参数对应的 Attribute
-	/// </summary>
-	/// <typeparam name="T">Attribute对象</typeparam>
-	/// <param name="bindingContext">绑定参数上下文</param>
-	/// <param name="parameterName">参数名称</param>
-	/// <returns></returns>
-	public static T GetAttribute<T>(this ModelBindingContext bindingContext, string parameterName = null) where T : Attribute
-	{
-		var fieldName = parameterName ?? bindingContext.FieldName;
-		var ctrlActionDesc = bindingContext.ActionContext.ActionDescriptor as ControllerActionDescriptor;
-		var fieldParameter = ctrlActionDesc!.MethodInfo.GetParameters().Single(p => p.Name == fieldName);
-		return fieldParameter.GetCustomAttributes(typeof(T), false).Single() as T;
-	}
-
-	/// <summary>
-	/// 判断该次请求体Body是否是Json内容类型
-	/// </summary>
-	/// <param name="httpContext"></param>
-	/// <param name="charSet"></param>
-	/// <returns></returns>
-	public static bool ContentTypeIsJson(this HttpContext httpContext, out string charSet)
-	{
-		string strContentType = httpContext.Request.ContentType;
-		if (string.IsNullOrEmpty(strContentType))
-		{
-			charSet = null;
-			return false;
-		}
-
-		var contentType = new ContentType(strContentType);
-		charSet = contentType.CharSet;
-		return contentType.MediaType.ToLower() == "application/json";
-	}
-
-	/// <summary>
-	/// 获取请求体Body字符串内容
-	/// </summary>
-	/// <param name="context"></param>
-	/// <param name="encoding"></param>
-	/// <returns></returns>
-	public static string GetBodyString(this HttpContext context, Encoding encoding)
-	{
-		context.Request.EnableBuffering(); //Ensure the HttpRequest.Body can be read multipletimes
-		int contentLen = 255;
-		if (context.Request.ContentLength != null)
-		{
-			contentLen = (int)context.Request.ContentLength;
-		}
-
-		var body = context.Request.Body;
-		string bodyText;
-		if (contentLen <= 0)
-		{
-			bodyText = "";
-		}
-		else
-		{
-			using var reader = new StreamReader(body, encoding, true, contentLen, true);
-			bodyText = reader.ReadToEndAsync().Result;
-		}
-
-		body.Position = 0;
-		return bodyText;
-	}
-
-	/// <summary>
-	/// 判断类型是否是常见的简单类型
-	/// </summary>
-	/// <param name="type"></param>
-	/// <returns></returns>
-	public static bool IsSimpleType(this Type type)
-	{
-		//IsPrimitive 判断是否为基础类型。
-		//基元类型为 Boolean、 Byte、 SByte、 Int16、 UInt16、 Int32、 UInt32、 Int64、 UInt64、 IntPtr、 UIntPtr、 Char、 Double 和 Single。
-		var t = Nullable.GetUnderlyingType(type) ?? type;
-		return t.IsPrimitive || t.IsEnum || t == typeof(decimal) || t == typeof(string) || t == typeof(Guid) || t == typeof(TimeSpan) || t == typeof(Uri);
-	}
-
-	/// <summary>
-	/// 是否是常见类型的 数组形式 类型
-	/// </summary>
-	/// <param name="type"></param>
-	/// <returns></returns>
-	public static bool IsSimpleArrayType(this Type type)
-	{
-		return type.IsArray && Type.GetType(type.FullName!.Trim('[', ']')).IsSimpleType();
-	}
-
-	/// <summary>
-	/// 是否是常见类型的 泛型形式 类型
-	/// </summary>
-	/// <param name="type"></param>
-	/// <returns></returns>
-	public static bool IsSimpleListType(this Type type)
-	{
-		type = Nullable.GetUnderlyingType(type) ?? type;
-		return type.IsGenericType && type.GetGenericArguments().Length == 1 && type.GetGenericArguments().FirstOrDefault().IsSimpleType();
-	}
-
-	/// <summary>
-	/// 转换为对应类型
-	/// </summary>
-	/// <param name="this"></param>
-	/// <param name="toType"></param>
-	/// <returns></returns>
-	public static object ConvertObject(this object @this, Type toType)
-	{
-		object targetValue;
-		if (@this is string strValue)
-		{
-			strValue = strValue.Trim();
-			if ((strValue.StartsWith("[") && strValue.EndsWith("]")) || strValue.StartsWith("{") && strValue.EndsWith("}"))
-			{
-				targetValue = JsonConvert.DeserializeObject(strValue, toType);
-			}
-			else if ((strValue.StartsWith("\"[") && strValue.EndsWith("]\"")) || strValue.StartsWith("\"{") && strValue.EndsWith("}\""))
-			{
-				// json字符串 又被 json序列化 的情况
-				var objects = JsonConvert.DeserializeObject(strValue);
-				targetValue = JsonConvert.SerializeObject(objects).ConvertObject(toType);
-			}
-			else
-			{
-				var text = JsonConvert.SerializeObject(@this);
-				targetValue = JsonConvert.DeserializeObject(text, toType);
-			}
-		}
-		else if (@this is StringValues values)
-		{
-			var text = values.ToString();
-			if (toType.IsSimpleArrayType() || toType.IsSimpleListType())
-			{
-				text = JsonConvert.SerializeObject(values);
-				targetValue = JsonConvert.DeserializeObject(text, toType);
-			}
-			else
-			{
-				text = JsonConvert.SerializeObject(text);
-				targetValue = JsonConvert.DeserializeObject(text, toType);
-			}
-		}
-		else if (@this is JToken)
-		{
-			var text = JsonConvert.SerializeObject(@this);
-			targetValue = JsonConvert.DeserializeObject(text, toType);
-		}
-		else
-		{
-			var text = JsonConvert.SerializeObject(@this);
-			targetValue = JsonConvert.DeserializeObject(text, toType);
-		}
-
-		return targetValue;
-	}
-
-	/// <summary>
-	/// 尝试设置默认值
-	/// </summary>
-	/// <param name="bindingContext"></param>
-	public static bool TrySetDefaultValue(this ModelBindingContext bindingContext)
-	{
-		var attr = bindingContext.GetAttribute<FromBodyOrDefaultAttribute>();
-
-		if (attr.DefaultValue != null)
-		{
-			var targetValue = attr.DefaultValue.ChangeType(bindingContext.ModelType);
-			bindingContext.Result = ModelBindingResult.Success(targetValue);
-			return true;
-		}
-
-		return false;
-	}
-
-	/// <summary>
-	/// 对象类型转换
-	/// </summary>
-	/// <param name="this">当前值</param>
-	/// <param name="toType">指定类型的类型</param>
-	/// <returns>转换后的对象</returns>
-	public static object ChangeType(this object @this, Type toType)
-	{
-		var currType = Nullable.GetUnderlyingType(@this.GetType()) ?? @this.GetType();
-		toType = Nullable.GetUnderlyingType(toType) ?? toType;
-		if (@this == DBNull.Value)
-		{
-			if (!toType.IsValueType)
-			{
-				return null;
-			}
-
-			throw new Exception("不能将null值转换为" + toType.Name + "类型!");
-		}
-
-		if (currType == toType)
-		{
-			return @this;
-		}
-
-		if (toType.IsAssignableFrom(typeof(string)))
-		{
-			return @this.ToString();
-		}
-
-		if (toType.IsEnum)
-		{
-			return Enum.Parse(toType, @this.ToString(), true);
-		}
-
-		if (toType.IsAssignableFrom(typeof(Guid)))
-		{
-			return Guid.Parse(@this.ToString());
-		}
-
-		if (!toType.IsArray || !currType.IsArray)
-		{
-			return Convert.ChangeType(@this, toType);
-		}
-
-		var length = ((Array)@this).Length;
-		var targetType = Type.GetType(toType.FullName.Trim('[', ']'));
-		var targetArr = Array.CreateInstance(targetType, length);
-		for (int j = 0; j < length; j++)
-		{
-			var tmp = ((Array)@this).GetValue(j);
-			targetArr.SetValue(ChangeType(tmp, targetType), j);
-		}
-
-		return targetArr;
-	}
-}

+ 7 - 49
Masuit.Tools.AspNetCore/ModelBinder/FromBodyOrDefaultAttribute.cs

@@ -34,11 +34,15 @@ namespace Masuit.Tools.AspNetCore.ModelBinder
 	[AttributeUsage(AttributeTargets.Parameter)]
 	public class FromBodyOrDefaultAttribute : ModelBinderAttribute
 	{
-		public FromBodyOrDefaultAttribute() : this(BindType.None, null, null)
+		public FromBodyOrDefaultAttribute() : this(BindType.Default, null, null)
 		{
 		}
 
-		public FromBodyOrDefaultAttribute(IConvertible defaultValue) : this(BindType.None, null, defaultValue)
+		public FromBodyOrDefaultAttribute(IConvertible defaultValue) : this(BindType.Default, null, defaultValue)
+		{
+		}
+
+		public FromBodyOrDefaultAttribute(BindType type) : this(type, null, null)
 		{
 		}
 
@@ -46,7 +50,7 @@ namespace Masuit.Tools.AspNetCore.ModelBinder
 		{
 		}
 
-		public FromBodyOrDefaultAttribute(BindType type, string fieldname, IConvertible defaultValue) : base(typeof(BodyOrDefaultModelBinder))
+		public FromBodyOrDefaultAttribute(BindType type, string fieldname, IConvertible defaultValue) : base(typeof(FromBodyOrDefaultModelBinder))
 		{
 			Type = type;
 			FieldName = fieldname;
@@ -62,50 +66,4 @@ namespace Masuit.Tools.AspNetCore.ModelBinder
 		/// </summary>
 		public BindType Type { get; set; }
 	}
-
-	/// <summary>
-	/// 枚举取值方式
-	/// </summary>
-	public enum BindType
-	{
-		/// <summary>
-		/// 无设定,自动取值(1.取请求数据中的某个值,2.请求数据当成一个对象取值)
-		/// </summary>
-		None,
-
-		/// <summary>
-		/// 从请求正文中获取值
-		/// </summary>
-		Body,
-
-		/// <summary>
-		/// 从查询字符串获取值
-		/// </summary>
-		Query,
-
-		/// <summary>
-		/// 从已发布的表单字段中获取值
-		/// </summary>
-		Form,
-
-		/// <summary>
-		/// 从 HTTP 标头中获取值
-		/// </summary>
-		Header,
-
-		/// <summary>
-		/// 从 Cookie 中取值
-		/// </summary>
-		Cookie,
-
-		/// <summary>
-		/// 从路由数据中获取值
-		/// </summary>
-		Route,
-
-		/// <summary>
-		/// 从依赖关系注入容器中获取类型的实例
-		/// </summary>
-		Services
-	}
 }

+ 68 - 57
Masuit.Tools.AspNetCore/ModelBinder/BodyOrDefaultModelBinder.cs → Masuit.Tools.AspNetCore/ModelBinder/FromBodyOrDefaultModelBinder.cs

@@ -1,23 +1,26 @@
 using System.Collections;
 using System.Net.Mime;
 using System.Reflection;
+using System.Xml.Linq;
+using Masuit.Tools.Systems;
 using Microsoft.AspNetCore.Mvc.ModelBinding;
 using Microsoft.Extensions.Primitives;
+using Newtonsoft.Json.Linq;
 
 namespace Masuit.Tools.AspNetCore.ModelBinder;
 
-public class BodyOrDefaultModelBinder : IModelBinder
+public class FromBodyOrDefaultModelBinder : IModelBinder
 {
-	private static readonly List<BindType> AutoTypes;
+	private static readonly List<BindType> BindTypes;
 
-	static BodyOrDefaultModelBinder()
+	static FromBodyOrDefaultModelBinder()
 	{
-		AutoTypes = Enum.GetNames(typeof(BindType)).Select(t => Enum.Parse<BindType>(t)).Where(t => t != BindType.None && t != BindType.Services).ToList();
+		BindTypes = Enum.GetNames(typeof(BindType)).Select(Enum.Parse<BindType>).Where(t => t != BindType.Default).ToList();
 	}
 
-	private readonly ILogger<BodyOrDefaultModelBinder> _logger;
+	private readonly ILogger<FromBodyOrDefaultModelBinder> _logger;
 
-	public BodyOrDefaultModelBinder(ILogger<BodyOrDefaultModelBinder> logger)
+	public FromBodyOrDefaultModelBinder(ILogger<FromBodyOrDefaultModelBinder> logger)
 	{
 		_logger = logger;
 	}
@@ -26,19 +29,19 @@ public class BodyOrDefaultModelBinder : IModelBinder
 	{
 		var context = bindingContext.HttpContext;
 		var attr = bindingContext.GetAttribute<FromBodyOrDefaultAttribute>();
-		var fieldName = attr?.FieldName ?? bindingContext.FieldName;
+		var field = attr?.FieldName ?? bindingContext.FieldName;
 		var modelType = bindingContext.ModelType;
-		object targetValue = null;
+		object value = null;
 		if (attr != null)
 		{
 			if (modelType.IsSimpleType() || modelType.IsSimpleArrayType() || modelType.IsSimpleListType())
 			{
-				if (attr.Type == BindType.None)
+				if (attr.Type == BindType.Default)
 				{
-					foreach (var type in AutoTypes)
+					foreach (var type in BindTypes)
 					{
-						targetValue = GetBindingValue(bindingContext, type, fieldName, modelType);
-						if (targetValue != null)
+						value = GetBindingValue(bindingContext, type, field, modelType);
+						if (value != null)
 						{
 							break;
 						}
@@ -46,22 +49,29 @@ public class BodyOrDefaultModelBinder : IModelBinder
 				}
 				else
 				{
-					targetValue = GetBindingValue(bindingContext, attr.Type, fieldName, modelType);
+					foreach (var type in attr.Type.Split())
+					{
+						value = GetBindingValue(bindingContext, type, field, modelType);
+						if (value != null)
+						{
+							break;
+						}
+					}
 				}
 			}
 			else
 			{
-				if (BodyOrDefaultModelBinderMiddleware.JsonObject != null)
+				if (bindingContext.HttpContext.Items.TryGetValue("BodyOrDefaultModelBinder@JsonBody", out var obj) && obj is JObject json)
 				{
 					if (modelType.IsArray || modelType.IsGenericType && modelType.GenericTypeArguments.Length == 1)
 					{
-						if (BodyOrDefaultModelBinderMiddleware.JsonObject.TryGetValue(fieldName, StringComparison.OrdinalIgnoreCase, out var jtoken))
+						if (json.TryGetValue(field, StringComparison.OrdinalIgnoreCase, out var jtoken))
 						{
-							targetValue = jtoken.ToObject(modelType);
+							value = jtoken.ToObject(modelType);
 						}
 						else
 						{
-							_logger.LogWarning($"TraceIdentifier:{context.TraceIdentifier},AutoBinderMiddleware从{BodyOrDefaultModelBinderMiddleware.JsonObject}中获取{fieldName}失败!");
+							_logger.LogWarning($"TraceIdentifier:{context.TraceIdentifier},BodyOrDefaultModelBinder从{json}中获取{field}失败!");
 						}
 					}
 					else
@@ -69,31 +79,31 @@ public class BodyOrDefaultModelBinder : IModelBinder
 						// 可能是 字典或者实体 类型,尝试将modeltype 当初整个请求参数对象
 						try
 						{
-							targetValue = BodyOrDefaultModelBinderMiddleware.JsonObject.ToObject(modelType);
+							value = json.ToObject(modelType);
 						}
 						catch (Exception e)
 						{
-							_logger.LogError(e, e.Message, BodyOrDefaultModelBinderMiddleware.JsonObject.ToString());
+							_logger.LogError(e, e.Message, json.ToString());
 						}
 					}
 				}
 
-				if (targetValue == null)
+				if (value == null)
 				{
 					var (requestData, keys) = GetRequestData(bindingContext, modelType);
 					if (keys.Any())
 					{
-						var setObj = Activator.CreateInstance(modelType);
+						var instance = Activator.CreateInstance(modelType);
 						switch (requestData)
 						{
 							case IEnumerable<KeyValuePair<string, StringValues>> stringValues:
 								{
 									foreach (var item in stringValues)
 									{
-										var prop = modelType.GetProperty(item.Key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
-										if (prop != null)
+										var property = modelType.GetProperty(item.Key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
+										if (property != null)
 										{
-											prop.SetValue(setObj, item.Value.ConvertObject(prop.PropertyType));
+											property.SetValue(instance, item.Value.ConvertObject(property.PropertyType));
 										}
 									}
 
@@ -104,10 +114,10 @@ public class BodyOrDefaultModelBinder : IModelBinder
 									//处理Cookie
 									foreach (var item in strs)
 									{
-										var prop = modelType.GetProperty(item.Key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
-										if (prop != null)
+										var property = modelType.GetProperty(item.Key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
+										if (property != null)
 										{
-											prop.SetValue(setObj, item.Value.ConvertObject(prop.PropertyType));
+											property.SetValue(instance, item.Value.ConvertObject(property.PropertyType));
 										}
 									}
 
@@ -118,10 +128,10 @@ public class BodyOrDefaultModelBinder : IModelBinder
 									//处理路由
 									foreach (var item in objects)
 									{
-										var prop = modelType.GetProperty(item.Key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
-										if (prop != null)
+										var property = modelType.GetProperty(item.Key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
+										if (property != null)
 										{
-											prop.SetValue(setObj, item.Value.ConvertObject(prop.PropertyType));
+											property.SetValue(instance, item.Value.ConvertObject(property.PropertyType));
 										}
 									}
 
@@ -129,20 +139,20 @@ public class BodyOrDefaultModelBinder : IModelBinder
 								}
 						}
 
-						targetValue = setObj;
+						value = instance;
 					}
 				}
 			}
 
-			if (targetValue == null && attr.DefaultValue != null)
+			if (value == null && attr.DefaultValue != null)
 			{
-				targetValue = attr.DefaultValue.ChangeType(modelType);
+				value = attr.DefaultValue.ChangeType(modelType);
 			}
 		}
 
-		if (targetValue != null)
+		if (value != null)
 		{
-			bindingContext.Result = ModelBindingResult.Success(targetValue);
+			bindingContext.Result = ModelBindingResult.Success(value);
 		}
 
 		return Task.CompletedTask;
@@ -181,7 +191,6 @@ public class BodyOrDefaultModelBinder : IModelBinder
 	/// <param name="bindType"></param>
 	/// <param name="fieldName"></param>
 	/// <param name="modelType"></param>
-	/// <returns></returns>
 	private object GetBindingValue(ModelBindingContext bindingContext, BindType bindType, string fieldName, Type modelType)
 	{
 		var context = bindingContext.HttpContext;
@@ -190,8 +199,8 @@ public class BodyOrDefaultModelBinder : IModelBinder
 		{
 			try
 			{
-				var cttType = new ContentType(context.Request.ContentType);
-				mediaType = cttType.MediaType.ToLower();
+				var contentType = new ContentType(context.Request.ContentType);
+				mediaType = contentType.MediaType.ToLower();
 			}
 			catch (Exception ex)
 			{
@@ -207,8 +216,7 @@ public class BodyOrDefaultModelBinder : IModelBinder
 				{
 					case "application/json":
 						{
-							var jsonObj = BodyOrDefaultModelBinderMiddleware.JsonObject;
-							if (jsonObj != null && jsonObj.TryGetValue(fieldName, StringComparison.OrdinalIgnoreCase, out var values))
+							if (bindingContext.HttpContext.Items.TryGetValue("BodyOrDefaultModelBinder@JsonBody", out var obj) && obj is JObject json && json.TryGetValue(fieldName, StringComparison.OrdinalIgnoreCase, out var values))
 							{
 								targetValue = values.ConvertObject(modelType);
 							}
@@ -217,25 +225,24 @@ public class BodyOrDefaultModelBinder : IModelBinder
 						break;
 
 					case "application/xml":
-
-						//var xmlObj = AutoBinderMiddleware.XmlObject;
-
-						//if (xmlObj != null)
-						//{
-						//    var xmlElt = xmlObj.Element(fieldName);
-
-						//    if (xmlElt != null)
-						//    {
-						//    }
-						//}
-						break;
+						{
+							if (bindingContext.HttpContext.Items.TryGetValue("BodyOrDefaultModelBinder@XmlBody", out var obj) && obj is XDocument xml)
+							{
+								var xmlElt = xml.Element(fieldName);
+								if (xmlElt != null)
+								{
+									targetValue = xmlElt.Value.ConvertObject(modelType);
+								}
+							}
+							break;
+						}
 				}
 
 				break;
 
 			case BindType.Query:
 				{
-					if (context.Request.Query != null && context.Request.Query.Count > 0 && context.Request.Query.TryGetValue(fieldName, out var values))
+					if (context.Request.Query is { Count: > 0 } && context.Request.Query.TryGetValue(fieldName, out var values))
 					{
 						targetValue = values.ConvertObject(modelType);
 					}
@@ -245,7 +252,7 @@ public class BodyOrDefaultModelBinder : IModelBinder
 
 			case BindType.Form:
 				{
-					if (context.Request.HasFormContentType && context.Request.Form.Count > 0 && context.Request.Form.TryGetValue(fieldName, out StringValues values))
+					if (context.Request is { HasFormContentType: true, Form.Count: > 0 } && context.Request.Form.TryGetValue(fieldName, out var values))
 					{
 						targetValue = values.ConvertObject(modelType);
 					}
@@ -255,7 +262,7 @@ public class BodyOrDefaultModelBinder : IModelBinder
 
 			case BindType.Header:
 				{
-					if (context.Request.Headers != null && context.Request.Headers.Count > 0 && context.Request.Headers.TryGetValue(fieldName, out var values))
+					if (context.Request.Headers is { Count: > 0 } && context.Request.Headers.TryGetValue(fieldName, out var values))
 					{
 						targetValue = values.ConvertObject(modelType);
 					}
@@ -265,7 +272,7 @@ public class BodyOrDefaultModelBinder : IModelBinder
 
 			case BindType.Cookie:
 				{
-					if (context.Request.Cookies != null && context.Request.Cookies.Count > 0 && context.Request.Cookies.TryGetValue(fieldName, out var values))
+					if (context.Request.Cookies is { Count: > 0 } && context.Request.Cookies.TryGetValue(fieldName, out var values))
 					{
 						targetValue = values.ConvertObject(modelType);
 					}
@@ -275,13 +282,17 @@ public class BodyOrDefaultModelBinder : IModelBinder
 
 			case BindType.Route:
 				{
-					if (bindingContext.ActionContext.RouteData.Values != null && bindingContext.ActionContext.RouteData.Values.Count > 0 && bindingContext.ActionContext.RouteData.Values.TryGetValue(fieldName, out var values))
+					if (bindingContext.ActionContext.RouteData.Values is { Count: > 0 } && bindingContext.ActionContext.RouteData.Values.TryGetValue(fieldName, out var values))
 					{
 						targetValue = values.ConvertObject(modelType);
 					}
 				}
 
 				break;
+
+			case BindType.Services:
+				targetValue = bindingContext.ActionContext.HttpContext.RequestServices.GetRequiredService(modelType);
+				break;
 		}
 
 		return targetValue;

+ 2 - 2
Masuit.Tools.AspNetCore/ModelBinder/BodyOrDefaultModelBinderMiddlewareExtensions.cs → Masuit.Tools.AspNetCore/ModelBinder/MiddlewareExtensions.cs

@@ -1,6 +1,6 @@
 namespace Masuit.Tools.AspNetCore.ModelBinder;
 
-public static class BodyOrDefaultModelBinderMiddlewareExtensions
+public static class MiddlewareExtensions
 {
 	/// <summary>
 	/// 使用自动参数绑定中间件
@@ -9,6 +9,6 @@ public static class BodyOrDefaultModelBinderMiddlewareExtensions
 	/// <returns></returns>
 	public static IApplicationBuilder UseBodyOrDefaultModelBinder(this IApplicationBuilder appBuilder)
 	{
-		return appBuilder.UseMiddleware<BodyOrDefaultModelBinderMiddleware>();
+		return appBuilder.UseMiddleware<BodyOrDefaultBinderMiddleware>();
 	}
 }

+ 159 - 0
Masuit.Tools.AspNetCore/ModelBinder/ModelBindingContextExtension.cs

@@ -0,0 +1,159 @@
+using System.Net.Mime;
+using System.Text;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Microsoft.Extensions.Primitives;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Masuit.Tools.AspNetCore.ModelBinder;
+
+internal static class ModelBindingContextExtension
+{
+	/// <summary>
+	/// 获取绑定参数对应的 Attribute
+	/// </summary>
+	/// <typeparam name="T">Attribute对象</typeparam>
+	/// <param name="bindingContext">绑定参数上下文</param>
+	/// <param name="parameterName">参数名称</param>
+	/// <returns></returns>
+	public static T GetAttribute<T>(this ModelBindingContext bindingContext, string parameterName = null) where T : Attribute
+	{
+		var fieldName = parameterName ?? bindingContext.FieldName;
+		var ctrlActionDesc = bindingContext.ActionContext.ActionDescriptor as ControllerActionDescriptor;
+		var fieldParameter = ctrlActionDesc!.MethodInfo.GetParameters().Single(p => p.Name == fieldName);
+		return fieldParameter.GetCustomAttributes(typeof(T), false).Single() as T;
+	}
+
+	/// <summary>
+	/// 判断该次请求体Body是否是Json内容类型
+	/// </summary>
+	/// <param name="httpContext"></param>
+	/// <param name="charSet"></param>
+	/// <returns></returns>
+	public static bool IsJsonContent(this HttpContext httpContext, out string charSet)
+	{
+		string strContentType = httpContext.Request.ContentType;
+		if (string.IsNullOrEmpty(strContentType))
+		{
+			charSet = null;
+			return false;
+		}
+
+		var contentType = new ContentType(strContentType);
+		charSet = contentType.CharSet;
+		return contentType.MediaType.ToLower() == "application/json";
+	}
+
+	/// <summary>
+	/// 获取请求体Body字符串内容
+	/// </summary>
+	/// <param name="context"></param>
+	/// <param name="encoding"></param>
+	/// <returns></returns>
+	public static string GetBodyString(this HttpContext context, Encoding encoding)
+	{
+		context.Request.EnableBuffering(); //Ensure the HttpRequest.Body can be read multipletimes
+		int contentLen = 255;
+		if (context.Request.ContentLength != null)
+		{
+			contentLen = (int)context.Request.ContentLength;
+		}
+
+		var body = context.Request.Body;
+		string bodyText;
+		if (contentLen <= 0)
+		{
+			bodyText = "";
+		}
+		else
+		{
+			using var reader = new StreamReader(body, encoding, true, contentLen, true);
+			bodyText = reader.ReadToEndAsync().Result;
+		}
+
+		body.Position = 0;
+		return bodyText;
+	}
+
+	/// <summary>
+	/// 尝试设置默认值
+	/// </summary>
+	/// <param name="bindingContext"></param>
+	public static bool TrySetDefaultValue(this ModelBindingContext bindingContext)
+	{
+		var attr = bindingContext.GetAttribute<FromBodyOrDefaultAttribute>();
+		if (attr.DefaultValue != null)
+		{
+			var targetValue = attr.DefaultValue.ChangeType(bindingContext.ModelType);
+			bindingContext.Result = ModelBindingResult.Success(targetValue);
+			return true;
+		}
+
+		return false;
+	}
+
+	/// <summary>
+	/// 转换为对应类型
+	/// </summary>
+	/// <param name="this"></param>
+	public static T ConvertObjectTo<T>(this object @this)
+	{
+		return (T)ConvertObject(@this, typeof(T));
+	}
+
+	/// <summary>
+	/// 转换为对应类型
+	/// </summary>
+	/// <param name="this"></param>
+	/// <param name="type"></param>
+	public static object ConvertObject(this object @this, Type type)
+	{
+		object value;
+		if (@this is string str)
+		{
+			str = str.Trim();
+			if ((str.StartsWith("[") && str.EndsWith("]")) || str.StartsWith("{") && str.EndsWith("}"))
+			{
+				value = JsonConvert.DeserializeObject(str, type);
+			}
+			else if ((str.StartsWith("\"[") && str.EndsWith("]\"")) || str.StartsWith("\"{") && str.EndsWith("}\""))
+			{
+				// json字符串 又被 json序列化 的情况
+				var objects = JsonConvert.DeserializeObject(str);
+				value = JsonConvert.SerializeObject(objects).ConvertObject(type);
+			}
+			else
+			{
+				var text = JsonConvert.SerializeObject(@this);
+				value = JsonConvert.DeserializeObject(text, type);
+			}
+		}
+		else if (@this is StringValues values)
+		{
+			var text = values.ToString();
+			if (type.IsSimpleArrayType() || type.IsSimpleListType())
+			{
+				text = JsonConvert.SerializeObject(values);
+				value = JsonConvert.DeserializeObject(text, type);
+			}
+			else
+			{
+				text = JsonConvert.SerializeObject(text);
+				value = JsonConvert.DeserializeObject(text, type);
+			}
+		}
+		else if (@this is JToken)
+		{
+			var text = JsonConvert.SerializeObject(@this);
+			value = JsonConvert.DeserializeObject(text, type);
+		}
+		else
+		{
+			var text = JsonConvert.SerializeObject(@this);
+			value = JsonConvert.DeserializeObject(text, type);
+		}
+
+		return value;
+	}
+}

+ 1 - 1
Masuit.Tools.Core/Masuit.Tools.Core.csproj

@@ -6,7 +6,7 @@
 官网教程:https://tools.masuit.org
 github:https://github.com/ldqk/Masuit.Tools
         </Description>
-        <Version>2.6.4</Version>
+        <Version>2.6.4.1</Version>
         <Copyright>Copyright © 懒得勤快</Copyright>
         <PackageProjectUrl>https://github.com/ldqk/Masuit.Tools</PackageProjectUrl>
         <PackageTags>Masuit.Tools,工具库,Utility,Crypt,Extensions</PackageTags>

+ 1 - 1
Masuit.Tools.Net45/package.nuspec

@@ -2,7 +2,7 @@
 <package>
   <metadata>
     <id>Masuit.Tools.Net45</id>
-    <version>2.6.3.1</version>
+    <version>2.6.4.1</version>
     <title>Masuit.Tools</title>
     <authors>懒得勤快</authors>
     <owners>masuit.com</owners>

+ 1 - 1
Masuit.Tools.NoSQL.MongoDBClient/Masuit.Tools.NoSQL.MongoDBClient.csproj

@@ -38,7 +38,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="MongoDB.Driver" Version="2.20.0" />
+    <PackageReference Include="MongoDB.Driver" Version="2.21.0" />
   </ItemGroup>
 
 </Project>

+ 1 - 1
Masuit.Tools/package.nuspec

@@ -2,7 +2,7 @@
 <package>
   <metadata>
     <id>Masuit.Tools.Net</id>
-    <version>2.6.3.1</version>
+    <version>2.6.4.1</version>
     <title>Masuit.Tools</title>
     <authors>懒得勤快</authors>
     <owners>masuit.com</owners>

+ 6 - 2
NetCoreTest/Controllers/HomeController.cs

@@ -9,9 +9,13 @@ public class HomeController : Controller
 {
 	[HttpPost("test")]
 	[ProducesResponseType(typeof(MyClass), (int)HttpStatusCode.OK)]
-	public async Task<ActionResult> Test([FromBodyOrDefault] MyClass mc)
+	public async Task<ActionResult> Test([FromBodyOrDefault] MyClass mc, [FromBodyOrDefault(BindType.Body | BindType.Query)] string name)
 	{
-		return Ok(mc);
+		return Ok(new
+		{
+			name,
+			mc
+		});
 	}
 }
 

+ 9 - 1
README.md

@@ -866,7 +866,7 @@ FileStream fs = new FileStream(@"D:\boot.vmdk", FileMode.OpenOrCreate, FileAcces
 memoryStream.SaveFile("filename"); // 将内存流转储成文件
 ```
 
-### 34.数值转换
+### 34.类型转换
 
 ```csharp
 1.2345678901.Digits8(); // 将小数截断为8位
@@ -874,6 +874,14 @@ memoryStream.SaveFile("filename"); // 将内存流转储成文件
 1.23.ConvertTo<T>(); // 小数转T基本类型
 bool b=1.23.TryConvertTo<T>(out result); // 小数转T基本类型
 var num=1.2345.ToDecimal(2); //转decimal并保留两位小数
+
+1.23.ChangeTypeTo<T>(); //小数转T基本类型,ConvertTo和ChangeTypeTo的区别在于:ConvertTo只适用于基元类型的互转,ChangeTypeTo不仅适用于基元类型的互转还支持数组、字符串的转换(Parse),ConvertTo的性能更高
+
+type.IsPrimitive(); // 判断类型是否是值类型
+type.IsSimpleType(); // 判断类型是否是常见的简单类型,基元类型为 Boolean、 Byte、 SByte、 Int16、 UInt16、 Int32、 UInt32、 Int64、 UInt64、 IntPtr、 UIntPtr、 Char、 Double 、 Single、枚举、Nullable<T>。
+type.IsSimpleArrayType(); // 判断类型是否是常见类型的 数组形式 类型
+type.IsSimpleListType(); // 判断类型是否是常见类型的 泛型形式 类型
+
 ```
 
 ### 35.INI配置文件操作(仅支持Windows)

+ 1 - 1
Test/Masuit.Tools.Abstractions.Test/Masuit.Tools.Abstractions.Test.csproj

@@ -13,7 +13,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
     <PackageReference Include="xunit" Version="2.5.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
       <PrivateAssets>all</PrivateAssets>

+ 2 - 2
Test/Masuit.Tools.Core.Test/Masuit.Tools.Core.Test.csproj

@@ -9,9 +9,9 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="7.0.9" />
+    <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="7.0.10" />
     <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
     <PackageReference Include="xunit" Version="2.5.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
       <PrivateAssets>all</PrivateAssets>

+ 1 - 1
Test/Masuit.Tools.Test/Masuit.Tools.Test.csproj

@@ -94,7 +94,7 @@
       <Version>2.0.0</Version>
     </PackageReference>
     <PackageReference Include="Moq">
-      <Version>4.18.4</Version>
+      <Version>4.20.2</Version>
     </PackageReference>
     <PackageReference Include="MSTest.TestAdapter">
       <Version>3.1.1</Version>