发布:3.0.1

This commit is contained in:
若汝棋茗
2024-11-14 21:40:22 +08:00
parent fae01eb9e5
commit 3835e4822c
20 changed files with 400 additions and 64 deletions

View File

@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<ApplicationIcon>logo.ico</ApplicationIcon>
<Version>3.0.0</Version>
<Version>3.0.1</Version>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>

View File

@@ -18,21 +18,25 @@ using System.Linq;
namespace TouchSocket.Core.AspNetCore
{
/// <summary>
/// AspNetCoreContainer
/// AspNetCoreContainer 类实现了 IRegistrator、IResolver 和 IKeyedServiceProvider 接口,
/// 提供了一个容器解决方案,用于在 ASP.NET Core 应用中注册服务和解析服务。
/// 它旨在简化依赖注入过程,并支持 keyed 服务的获取。
/// </summary>
public class AspNetCoreContainer : IRegistrator, IResolver, IKeyedServiceProvider
{
private readonly IServiceCollection m_services;
private IServiceProvider m_serviceProvider;
/// <summary>
/// 获取当前对象的IServiceProvider实例。
/// </summary>
public IServiceProvider ServiceProvider { get => this.m_serviceProvider; }
//private AspNetCoreResolver m_resolver;
/// <summary>
/// 初始化一个IServiceCollection的容器
/// 初始化AspNetCoreContainer实例
/// </summary>
/// <param name="services"></param>
/// <param name="services">IServiceCollection实例用于注册服务。</param>
public AspNetCoreContainer(IServiceCollection services)
{
this.m_services = services ?? throw new ArgumentNullException(nameof(services));
@@ -41,9 +45,9 @@ namespace TouchSocket.Core.AspNetCore
{
return;
}
services.AddSingleton<IResolver>(privoder =>
services.AddSingleton<IResolver>(provider =>
{
this.m_serviceProvider ??= privoder;
this.m_serviceProvider ??= provider;
return this;
});
@@ -97,7 +101,7 @@ namespace TouchSocket.Core.AspNetCore
{
continue;
}
if (item.ServiceType == fromType&&item.ServiceKey?.ToString()==key)
if (item.ServiceType == fromType && item.ServiceKey?.ToString() == key)
{
return true;
}

View File

@@ -37,6 +37,11 @@ namespace TouchSocket.Core
}
/// <summary>
/// 将输入字符串转换为有效的标识符。
/// </summary>
/// <param name="input">输入字符串。</param>
/// <returns>转换后的标识符字符串。</returns>
public static string MakeIdentifier(string input)
{
// 替换非法字符

View File

@@ -25,31 +25,73 @@ namespace TouchSocket.Core
{
private static readonly ConcurrentDictionary<Type, Dictionary<string, Property>> m_typeToProperty = new ConcurrentDictionary<Type, Dictionary<string, Property>>();
/// <summary>
/// 将源对象映射到指定目标类型的新实例。
/// </summary>
/// <param name="source">源对象,其属性将被映射到目标类型。</param>
/// <param name="option">映射选项,用于自定义映射行为。</param>
/// <typeparam name="TTarget">要映射到的目标类型。</typeparam>
/// <returns>一个新创建的目标类型实例,其属性根据源对象的属性值进行映射。</returns>
public static TTarget Map<TTarget>(this object source, MapperOption option = default) where TTarget : class, new()
{
// 调用泛型方法 Map将源对象、目标类型和映射选项传递给它
// 由于目标类型的实例化和类型转换由 Map 方法内部处理,这里直接返回转换后的结果
return (TTarget)Map(source, typeof(TTarget), option);
}
/// <summary>
/// 扩展方法,用于将对象映射到相同类型的另一个对象。
/// </summary>
/// <param name="source">要映射的源对象。</param>
/// <param name="option">映射选项,用于定制映射行为。</param>
/// <typeparam name="TTarget">源对象和目标对象的类型。</typeparam>
/// <returns>返回映射后的目标对象。</returns>
public static TTarget Map<TTarget>(this TTarget source, MapperOption option = default) where TTarget : class, new()
{
// 调用泛型方法 Map将源对象映射为目标对象
return (TTarget)Map(source, typeof(TTarget), option);
}
/// <summary>
/// 扩展方法,用于将一个对象映射到另一个类型。
/// </summary>
/// <typeparam name="TSource">源对象的类型。</typeparam>
/// <typeparam name="TTarget">目标对象的类型,必须是引用类型且有默认构造函数。</typeparam>
/// <param name="source">要映射的源对象。</param>
/// <param name="option">映射选项,用于控制映射行为。</param>
/// <returns>返回映射后的目标对象实例。</returns>
public static TTarget Map<TSource, TTarget>(this TSource source, MapperOption option = default) where TTarget : class, new()
{
// 调用泛型映射方法,将源对象、目标类型和映射选项传递给它
// 由于目标类型在运行时才能确定,这里使用反射来动态调用合适的映射方法
return (TTarget)Map(source, typeof(TTarget), option);
}
/// <summary>
/// 将源对象映射到目标类型的实例。
/// </summary>
/// <param name="source">要映射的源对象。</param>
/// <param name="targetType">目标类型的 <see cref="Type"/>。</param>
/// <param name="option">映射选项,用于控制映射行为。</param>
/// <returns>映射后的目标类型实例。</returns>
public static object Map(this object source, Type targetType, MapperOption option = default)
{
// 使用 Activator.CreateInstance 创建目标类型的实例,并将源对象映射到该实例
return Map(source, Activator.CreateInstance(targetType), option);
}
/// <summary>
/// 将源对象的属性映射到目标对象的属性中。
/// </summary>
/// <param name="source">源对象,其属性将被映射。</param>
/// <param name="target">目标对象,将接收映射的属性值。</param>
/// <param name="option">映射选项,用于定制映射行为。</param>
/// <returns>返回映射后的目标对象。</returns>
public static object Map(this object source, object target, MapperOption option = default)
{
if (source is null)
@@ -109,35 +151,60 @@ namespace TouchSocket.Core
return target;
}
/// <summary>
/// 扩展方法,将一个泛型集合中的每个元素映射到另一个泛型类型的新集合。
/// </summary>
/// <param name="list">要映射的原始集合。</param>
/// <param name="option">映射选项,用于自定义映射行为。</param>
/// <typeparam name="T">原始集合中的元素类型。</typeparam>
/// <typeparam name="T1">目标集合中的元素类型。</typeparam>
/// <returns>一个新集合,包含原始集合中每个元素的映射结果。</returns>
public static IEnumerable<T1> MapList<T, T1>(this IEnumerable<T> list, MapperOption option = default) where T : class where T1 : class, new()
{
// 检查输入的集合是否为null如果是则抛出异常
if (list is null)
{
throw new ArgumentNullException(nameof(list));
}
// 初始化结果集合,用于存储映射后的元素
var result = new List<T1>();
// 遍历原始集合中的每个元素
foreach (var item in list)
{
// 将当前元素映射到目标类型,并将结果添加到结果集合中
result.Add(Map<T, T1>(item, option));
}
// 返回结果集合
return result;
}
/// <summary>
/// 将对象集合映射为指定类型的集合。
/// </summary>
/// <param name="list">待映射的对象集合。</param>
/// <param name="option">映射选项。</param>
/// <typeparam name="T1">目标类型。</typeparam>
/// <returns>映射后的指定类型的集合。</returns>
public static IEnumerable<T1> MapList<T1>(this IEnumerable<object> list, MapperOption option = default) where T1 : class, new()
{
// 检查输入集合是否为null如果是则抛出异常
if (list is null)
{
throw new ArgumentNullException(nameof(list));
}
// 初始化结果集合
var result = new List<T1>();
// 遍历输入集合中的每个对象
foreach (var item in list)
{
// 将当前对象映射为目标类型,并添加到结果集合中
result.Add(Map<T1>(item, option));
}
// 返回结果集合
return result;
}
}

View File

@@ -30,9 +30,9 @@ namespace TouchSocket.Core
/// <summary>
/// 获取已添加的指定名称的插件数量。
/// </summary>
/// <param name="interfeceType"></param>
/// <param name="pluginType"></param>
/// <returns></returns>
int GetPluginCount(Type interfeceType);
int GetPluginCount(Type pluginType);
/// <summary>
/// 所包含的所有插件。
@@ -44,24 +44,36 @@ namespace TouchSocket.Core
/// </summary>
/// <param name="plugin">插件</param>
/// <exception cref="ArgumentNullException"></exception>
void Add<[DynamicallyAccessedMembers(PluginManagerExtension.PluginAccessedMemberTypes)]TPlugin>(TPlugin plugin)where TPlugin:class,IPlugin;
void Add<[DynamicallyAccessedMembers(PluginManagerExtension.PluginAccessedMemberTypes)] TPlugin>(TPlugin plugin) where TPlugin : class, IPlugin;
void Add(Type interfeceType, Func<object, PluginEventArgs, Task> pluginInvokeHandler,Delegate sourceDelegate=default);
/// <summary>
/// 添加一个插件类型及其对应的调用处理程序。
/// </summary>
/// <param name="pluginType">插件的类型。</param>
/// <param name="pluginInvokeHandler">插件调用处理程序,当插件被调用时执行。</param>
/// <param name="sourceDelegate">可选的源委托,用于标识插件的来源。</param>
void Add(Type pluginType, Func<object, PluginEventArgs, Task> pluginInvokeHandler, Delegate sourceDelegate = default);
/// <summary>
/// 触发对应插件
/// </summary>
/// <param name="interfeceType"></param>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <param name="pluginType">插件接口类型</param>
/// <param name="sender">事件发送者</param>
/// <param name="e">事件参数</param>
/// <returns>表示在执行的插件中,是否处理<see cref="TouchSocketEventArgs.Handled"/>为<see langword="true"/>。</returns>
ValueTask<bool> RaiseAsync(Type interfeceType, object sender, PluginEventArgs e);
ValueTask<bool> RaiseAsync(Type pluginType, object sender, PluginEventArgs e);
/// <summary>
/// 移除指定的插件实例
/// </summary>
/// <param name="plugin">要移除的插件实例</param>
void Remove(IPlugin plugin);
void Remove(Type interfeceType, Delegate func);
//void Add<TSender, TEventArgs>(Type interfeceType, Func<TSender, TEventArgs, Task> func)
// where TSender:class
// where TEventArgs : PluginEventArgs;
/// <summary>
/// 根据插件类型和功能委托移除插件
/// </summary>
/// <param name="pluginType">要移除的插件类型</param>
/// <param name="func">代表要移除的功能的委托</param>
void Remove(Type pluginType, Delegate func);
}
}

View File

@@ -22,17 +22,39 @@ namespace TouchSocket.Core
/// </summary>
public static class PluginManagerExtension
{
/// <summary>
/// 插件访问成员类型,用于指定动态访问的成员类型。
/// </summary>
public const DynamicallyAccessedMemberTypes PluginAccessedMemberTypes = DynamicallyAccessedMemberTypes.All;
/// <summary>
/// 向插件管理器中添加一个指定类型的插件。
/// </summary>
/// <param name="pluginManager">插件管理器实例。</param>
/// <param name="func">一个函数,用于通过解析器创建插件实例。</param>
/// <typeparam name="TPlugin">要添加的插件类型。</typeparam>
/// <returns>返回添加到插件管理器中的插件实例。</returns>
public static TPlugin Add<[DynamicallyAccessedMembers(PluginAccessedMemberTypes)] TPlugin>(this IPluginManager pluginManager, Func<IResolver, TPlugin> func) where TPlugin : class, IPlugin
{
// 检查传入的函数是否为null并抛出异常
ThrowHelper.ThrowArgumentNullExceptionIf(func, nameof(func));
// 使用提供的函数和插件管理器的解析器来创建插件实例
var plugin = func.Invoke(pluginManager.Resolver);
// 将创建的插件实例添加到插件管理器中
pluginManager.Add(plugin);
// 返回创建的插件实例
return plugin;
}
/// <summary>
/// 扩展方法,用于向插件管理器添加插件类型。
/// </summary>
/// <param name="pluginManager">插件管理器实例,允许通过其调用扩展方法。</param>
/// <param name="pluginType">要添加的插件类型,该类型应包含特定的成员以供插件系统访问。</param>
/// <returns>返回添加插件类型后的插件管理器实例。</returns>
public static object Add(this IPluginManager pluginManager, [DynamicallyAccessedMembers(PluginAccessedMemberTypes)] Type pluginType)
{
if (pluginType.GetCustomAttribute<PluginOptionAttribute>() is PluginOptionAttribute optionAttribute)
@@ -81,64 +103,123 @@ namespace TouchSocket.Core
return plugin;
}
public static void Add<TSender, TEventArgs>(this IPluginManager pluginManager, Type interfeceType, Func<TSender, TEventArgs, Task> func) where TEventArgs : PluginEventArgs
/// <summary>
/// 扩展方法,用于向插件管理器添加一个新的事件处理函数。
/// </summary>
/// <param name="pluginManager">插件管理器接口,用于管理插件的加载和事件处理函数的注册。</param>
/// <param name="interfaceType">插件接口类型,用于指定该事件处理函数将关联的插件类型。</param>
/// <param name="func">异步事件处理函数,接受事件发送者和事件参数作为输入,并返回一个任务。</param>
/// <typeparam name="TSender">事件发送者的类型。</typeparam>
/// <typeparam name="TEventArgs">事件参数的类型必须继承自PluginEventArgs。</typeparam>
public static void Add<TSender, TEventArgs>(this IPluginManager pluginManager, Type interfaceType, Func<TSender, TEventArgs, Task> func) where TEventArgs : PluginEventArgs
{
// 创建一个新的任务,封装了传入的事件处理函数,以适应插件管理器所需的参数类型。
Task newFunc(object sender, PluginEventArgs e)
{
// 调用传入的事件处理函数,传入转换后的参数。
return func((TSender)sender, (TEventArgs)e);
}
pluginManager.Add(interfeceType, newFunc, func);
// 调用插件管理器的Add方法注册新的事件处理函数。
pluginManager.Add(interfaceType, newFunc, func);
}
public static void Add<TEventArgs>(this IPluginManager pluginManager, Type interfeceType, Func<TEventArgs, Task> func) where TEventArgs : PluginEventArgs
/// <summary>
/// 扩展方法,用于向插件管理器添加一个新的事件处理函数。
/// </summary>
/// <param name="pluginManager">插件管理器接口,用于添加事件处理函数。</param>
/// <param name="interfaceType">插件接口的类型,用于指定事件处理函数关联的插件类型。</param>
/// <param name="func">要添加的事件处理函数,当事件触发时将异步执行此函数。</param>
/// <typeparam name="TEventArgs">事件参数的类型必须继承自PluginEventArgs。</typeparam>
public static void Add<TEventArgs>(this IPluginManager pluginManager, Type interfaceType, Func<TEventArgs, Task> func) where TEventArgs : PluginEventArgs
{
// 创建一个新的任务,封装传入的事件处理函数,使其与插件管理器期望的签名匹配。
Task newFunc(object sender, PluginEventArgs e)
{
// 转换基础PluginEventArgs为具体的TEventArgs类型然后调用传入的事件处理函数。
return func((TEventArgs)e);
}
pluginManager.Add(interfeceType, newFunc, func);
// 调用插件管理器的Add方法注册新的事件处理函数。
pluginManager.Add(interfaceType, newFunc, func);
}
public static void Add(this IPluginManager pluginManager, Type interfeceType, Func<Task> func)
/// <summary>
/// 扩展方法,用于向插件管理器中添加一个新的插件。
/// </summary>
/// <param name="pluginManager">插件管理器实例,用于添加插件。</param>
/// <param name="interfaceType">插件需要实现的接口类型。</param>
/// <param name="func">插件的具体逻辑,作为一个异步任务执行。</param>
public static void Add(this IPluginManager pluginManager, Type interfaceType, Func<Task> func)
{
// 定义一个新的异步任务,封装传入的插件逻辑和插件链的继续执行
async Task newFunc(object sender, PluginEventArgs e)
{
// 执行传入的插件逻辑,不捕获当前上下文
await func().ConfigureAwait(false);
// 继续执行插件链中的下一个插件,不捕获当前上下文
await e.InvokeNext().ConfigureAwait(false);
}
pluginManager.Add(interfeceType, newFunc, func);
// 将封装后的插件逻辑添加到插件管理器中
pluginManager.Add(interfaceType, newFunc, func);
}
public static void Add<T>(this IPluginManager pluginManager, Type interfeceType, Action<T> action) where T : class
/// <summary>
/// 扩展方法,用于向插件管理器添加新插件。
/// </summary>
/// <param name="pluginManager">插件管理器接口,用于添加插件。</param>
/// <param name="interfaceType">插件需要实现的接口类型。</param>
/// <param name="action">插件被调用时执行的操作。</param>
/// <typeparam name="T">插件的类型,必须是类类型。</typeparam>
public static void Add<T>(this IPluginManager pluginManager, Type interfaceType, Action<T> action) where T : class
{
// 判断泛型类型T是否继承自PluginEventArgs
if (typeof(PluginEventArgs).IsAssignableFrom(typeof(T)))
{
// 如果T是PluginEventArgs的子类则定义一个新的异步任务处理方法
async Task newFunc(object sender, PluginEventArgs e)
{
// 执行传入的action这里将e转换为T类型
action(e as T);
// 调用下一个插件,确保插件链的执行顺序
await e.InvokeNext().ConfigureAwait(false);
}
pluginManager.Add(interfeceType, newFunc, action);
// 将新定义的处理方法添加到插件管理器中
pluginManager.Add(interfaceType, newFunc, action);
}
else
{
// 如果T不是PluginEventArgs的子类则定义另一个异步任务处理方法
async Task newFunc(object sender, PluginEventArgs e)
{
// 执行传入的action这里将sender转换为T类型
action((T)sender);
// 调用下一个插件,确保插件链的执行顺序
await e.InvokeNext().ConfigureAwait(false);
}
pluginManager.Add(interfeceType, newFunc, action);
// 将新定义的处理方法添加到插件管理器中
pluginManager.Add(interfaceType, newFunc, action);
}
}
public static void Add(this IPluginManager pluginManager, Type interfeceType, Action action)
/// <summary>
/// 扩展方法,用于向插件管理器中添加一个新的插件处理程序。
/// </summary>
/// <param name="pluginManager">插件管理器实例,允许通过扩展方法语法调用此方法。</param>
/// <param name="interfaceType">插件所实现的接口类型,用于标识和分类插件。</param>
/// <param name="action">插件处理程序将要执行的动作。</param>
public static void Add(this IPluginManager pluginManager, Type interfaceType, Action action)
{
// 创建一个新的异步处理程序,它将在插件事件被触发时执行指定的动作,
// 并在动作完成后调用事件的InvokeNext方法以继续执行下一个插件处理程序。
async Task newFunc(object sender, PluginEventArgs e)
{
action();
await e.InvokeNext().ConfigureAwait(false);
action(); // 执行插件处理程序指定的动作。
await e.InvokeNext().ConfigureAwait(false); // 继续执行下一个插件处理程序。
}
pluginManager.Add(interfeceType, newFunc, action);
// 调用插件管理器的Add方法注册新的异步处理程序和动作。
pluginManager.Add(interfaceType, newFunc, action);
}
}
}

View File

@@ -6,8 +6,12 @@ using System.Threading.Tasks;
namespace TouchSocket.Core
{
[AttributeUsage(AttributeTargets.Class| AttributeTargets.Struct| AttributeTargets.Method| AttributeTargets.Interface)]
/// <summary>
/// 定义一个动态方法的特性,可以指导源生代码生成器如何生成动态方法。便于在运行时动态调用。
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface)]
public sealed class DynamicMethodAttribute : Attribute
{
}
}

View File

@@ -8,6 +8,11 @@ using System.Threading.Tasks;
namespace TouchSocket.Core
{
/// <summary>
/// 定义一个类 JsonMemoryToClassSerializerFormatter用于将只读内存中的字节序列反序列化为指定的状态类。
/// 该类实现了 ISerializerFormatter 接口,特化于 ReadOnlyMemory{byte} 类型的输入和 TState 类型的输出。
/// </summary>
/// <typeparam name="TState">要反序列化的状态类类型。</typeparam>
public class JsonMemoryToClassSerializerFormatter<TState> : ISerializerFormatter<ReadOnlyMemory<byte>, TState>
{
/// <summary>
@@ -15,8 +20,10 @@ namespace TouchSocket.Core
/// </summary>
public JsonSerializerSettings JsonSettings { get; set; } = new JsonSerializerSettings();
/// <inheritdoc/>
public int Order { get; set; }
/// <inheritdoc/>
public bool TryDeserialize(TState state, in ReadOnlyMemory<byte> source, Type targetType, out object target)
{
try
@@ -31,6 +38,7 @@ namespace TouchSocket.Core
}
}
/// <inheritdoc/>
public bool TrySerialize(TState state, in object target, out ReadOnlyMemory<byte> source)
{
try

View File

@@ -30,8 +30,15 @@ namespace TouchSocket.Dmtp
registrator.RegisterSingleton<IDmtpRouteService, DmtpRouteService>();
}
public static void AddDmtpRouteService<TDmtpRouteService>(this IRegistrator registrator)where TDmtpRouteService :class, IDmtpRouteService
/// <summary>
/// 扩展方法用于在服务容器中注册DMTP路由服务的单例实例。
/// </summary>
/// <typeparam name="TDmtpRouteService">DMTP路由服务的具体类型。</typeparam>
/// <param name="registrator">服务注册器接口,用于在服务容器中注册服务。</param>
public static void AddDmtpRouteService<TDmtpRouteService>(this IRegistrator registrator)
where TDmtpRouteService : class, IDmtpRouteService
{
// 使用单例模式注册DMTP路由服务确保在整个应用生命周期中只创建一个实例。
registrator.RegisterSingleton<IDmtpRouteService, TDmtpRouteService>();
}
@@ -51,13 +58,16 @@ namespace TouchSocket.Dmtp
/// <summary>
/// 添加基于设定委托的Dmtp路由服务。
/// </summary>
/// <param name="registrator"></param>
/// <param name="action"></param>
/// <param name="registrator">服务注册器接口,用于注册服务。</param>
/// <param name="action">一个函数委托根据ID返回一个IDmtpActor实例。</param>
public static void AddDmtpRouteService(this IRegistrator registrator, Func<string, IDmtpActor> action)
{
// 调用重载版本的AddDmtpRouteService方法处理异步操作
AddDmtpRouteService(registrator, async (id) =>
{
// 完成一个已经完成的任务,用于简化异步操作
await EasyTask.CompletedTask;
// 调用传入的委托,并返回结果
return action.Invoke(id);
});
}

View File

@@ -26,8 +26,8 @@ namespace TouchSocket.Http
/// <param name="response">Http响应</param>
public HttpContext(HttpRequest request, HttpResponse response)
{
this.Request = request ?? throw new ArgumentNullException(nameof(request));
this.Response = response ?? throw new ArgumentNullException(nameof(response));
this.Request = request;
this.Response = response;
}
/// <summary>

View File

@@ -100,7 +100,7 @@ namespace TouchSocket.Http
/// 构建数据并回应。
/// <para>该方法仅在具有Client实例时有效。</para>
/// </summary>
public async Task AnswerAsync(CancellationToken token=default)
public async Task AnswerAsync(CancellationToken token = default)
{
this.ThrowIfResponsed();
@@ -142,7 +142,7 @@ namespace TouchSocket.Http
this.Responsed = true;
}
/// <summary>
/// 当传输模式是Chunk时用于结束传输。
@@ -219,7 +219,7 @@ namespace TouchSocket.Http
}
finally
{
}
}
else
@@ -258,6 +258,11 @@ namespace TouchSocket.Http
#region Write
/// <summary>
/// 异步写入指定的只读内存数据。
/// </summary>
/// <param name="memory">要写入的只读内存数据。</param>
/// <returns>一个任务,表示异步写入操作。</returns>
public async Task WriteAsync(ReadOnlyMemory<byte> memory)
{
this.ThrowIfResponsed();
@@ -371,10 +376,10 @@ namespace TouchSocket.Http
{
this.Headers.Add(HttpHeaders.TransferEncoding, "chunked");
}
foreach (var headerkey in this.Headers.Keys)
foreach (var headerKey in this.Headers.Keys)
{
stringBuilder.Append($"{headerkey}: ");
stringBuilder.Append(this.Headers[headerkey] + "\r\n");
stringBuilder.Append($"{headerKey}: ");
stringBuilder.Append(this.Headers[headerKey] + "\r\n");
}
stringBuilder.Append("\r\n");

View File

@@ -121,8 +121,18 @@ namespace TouchSocket.Http
#endregion Download
#region Upload
/// <summary>
/// 异步上传文件到指定URL。
/// </summary>
/// <param name="client">HttpClient实例用于发送HTTP请求。</param>
/// <param name="url">文件上传的URL地址。</param>
/// <param name="fileInfo">包含文件信息的FileInfo对象用于获取文件内容和属性。</param>
/// <param name="millisecondsTimeout">请求的超时时间默认为10秒。如果在此时间内未完成上传请求将被取消。</param>
/// <param name="token">用于取消操作的取消令牌。</param>
/// <typeparam name="TClient">客户端类型必须继承自HttpClientBase并实现IHttpClient接口。</typeparam>
public static async Task UploadFileAsync<TClient>(this TClient client, string url, FileInfo fileInfo, int millisecondsTimeout = 10 * 1000, CancellationToken token = default)
where TClient : HttpClientBase,IHttpClient
where TClient : HttpClientBase, IHttpClient
{
//创建一个请求
var request = new HttpRequest();

View File

@@ -35,7 +35,7 @@ namespace TouchSocket.Http
/// <returns>返回一个只读内存块,该内存块包含具体的字节内容。</returns>
/// <param name="httpBase"></param>
/// <param name="cancellationToken">一个CancellationToken对象用于取消异步操作。</param>
public static ReadOnlyMemory<byte> GetContent(this HttpBase httpBase,CancellationToken cancellationToken = default)
public static ReadOnlyMemory<byte> GetContent(this HttpBase httpBase, CancellationToken cancellationToken = default)
{
// 使用Task.Run来启动一个新的任务该任务将异步地获取内容。
// 这里使用GetFalseAwaitResult()方法来处理任务的结果,确保即使在同步上下文中也能正确处理异常。
@@ -166,9 +166,17 @@ namespace TouchSocket.Http
return string.Empty;
}
/// <summary>
/// 为HttpBase类型对象设置内容。
/// </summary>
/// <typeparam name="T">泛型参数T表示HttpBase类型或其派生类型。</typeparam>
/// <param name="httpBase">需要设置内容的HttpBase类型对象。</param>
/// <param name="content">要设置的内容类型为HttpContent。</param>
/// <returns>返回设置内容后的HttpBase对象。</returns>
public static T SetContent<T>(this T httpBase, HttpContent content) where T : HttpBase
{
httpBase.Content= content;
// 将传入的内容设置到HttpBase对象中
httpBase.Content = content;
// 返回处理后的HttpBase对象
return httpBase;
}
@@ -234,13 +242,27 @@ namespace TouchSocket.Http
return request;
}
/// <summary>
/// 将一个键值对集合按照application/x-www-form-urlencoded格式设置到HttpRequest的内容中
/// </summary>
/// <param name="request">待设置内容的HttpRequest对象</param>
/// <param name="nameValueCollection">包含键值对的集合,将被转换为查询字符串格式</param>
/// <typeparam name="TRequest">HttpRequest的类型使用泛型以支持所有HttpRequest的子类</typeparam>
public static void SetFormUrlEncodedContent<TRequest>(this TRequest request, IEnumerable<KeyValuePair<string, string>> nameValueCollection)
where TRequest : HttpRequest
{
// 将键值对集合转换为查询字符串格式,并设置为请求的内容
request.SetContent(string.Join("&", nameValueCollection.Select(a => $"{a.Key}={a.Value}")));
// 设置请求的内容类型为application/x-www-form-urlencoded
request.ContentType = "application/x-www-form-urlencoded";
}
/// <summary>
/// 异步获取HttpRequest的表单集合
/// </summary>
/// <param name="request">HttpRequest对象用于提取表单数据</param>
/// <typeparam name="TRequest">泛型参数限定为HttpRequest类型</typeparam>
/// <returns>返回一个任务该任务的结果是IFormCollection类型的表单集合</returns>
public static async Task<IFormCollection> GetFormCollectionAsync<TRequest>(this TRequest request) where TRequest : HttpRequest
{
// 检查请求中是否包含分隔符,这是判断是否存在多文件数据的依据。

View File

@@ -67,19 +67,41 @@ namespace TouchSocket.Http
/// <returns>返回一个任务对象,代表异步写入操作</returns>
protected abstract Task WriteContent(Func<ReadOnlyMemory<byte>, Task> writeFunc, CancellationToken token);
/// <summary>
/// 将字符串内容隐式转换为HttpContent对象使用UTF-8编码。
/// </summary>
/// <param name="content">要转换的字符串内容。</param>
/// <returns>一个新的StringHttpContent对象。</returns>
public static implicit operator HttpContent(string content)
{
return new StringHttpContent(content,Encoding.UTF8);
return new StringHttpContent(content, Encoding.UTF8);
}
/// <summary>
/// 将只读内存字节内容隐式转换为HttpContent对象。
/// </summary>
/// <param name="content">要转换的只读内存字节内容。</param>
/// <returns>一个新的ReadonlyMemoryHttpContent对象。</returns>
public static implicit operator HttpContent(ReadOnlyMemory<byte> content)
{
return new ReadonlyMemoryHttpContent(content);
}
/// <summary>
/// 将字节数组内容隐式转换为HttpContent对象。
/// </summary>
/// <param name="content">要转换的字节数组内容。</param>
/// <returns>一个新的ReadonlyMemoryHttpContent对象。</returns>
public static implicit operator HttpContent(byte[] content)
{
return new ReadonlyMemoryHttpContent(content);
}
/// <summary>
/// 将流内容隐式转换为HttpContent对象。
/// </summary>
/// <param name="content">要转换的流内容。</param>
/// <returns>一个新的StreamHttpContent对象。</returns>
public static implicit operator HttpContent(Stream content)
{
return new StreamHttpContent(content);

View File

@@ -14,6 +14,10 @@ namespace TouchSocket.Http
{
private readonly ReadOnlyMemory<byte> m_memory;
/// <summary>
/// 初始化 <see cref="ReadonlyMemoryHttpContent"/> 类的新实例。
/// </summary>
/// <param name="memory">要封装的只读内存。</param>
public ReadonlyMemoryHttpContent(ReadOnlyMemory<byte> memory)
{
this.m_memory = memory;
@@ -26,7 +30,7 @@ namespace TouchSocket.Http
{
return true;//直接构建成功也不用调用后续的WriteContent
}
if (byteBlock.FreeLength>this.m_memory.Length)
if (byteBlock.FreeLength > this.m_memory.Length)
{
//如果空闲空间足够构建成功也不用调用后续的WriteContent
byteBlock.Write(this.m_memory.Span);
@@ -40,7 +44,7 @@ namespace TouchSocket.Http
/// <inheritdoc/>
protected override void OnBuildingHeader(IHttpHeader header)
{
header.Add(HttpHeaders.ContentLength,this.m_memory.Length.ToString());
header.Add(HttpHeaders.ContentLength, this.m_memory.Length.ToString());
}
/// <inheritdoc/>

View File

@@ -9,29 +9,44 @@ using TouchSocket.Core;
namespace TouchSocket.Http
{
/// <summary>
/// 继承自HttpContent的类用于将Stream数据转换为可发送的HTTP内容。
/// </summary>
public class StreamHttpContent : HttpContent
{
private readonly int m_bufferLength;
private readonly int m_maxSpeed;
private readonly Stream m_stream;
/// <summary>
/// 初始化StreamHttpContent类的新实例。
/// </summary>
/// <param name="stream">要包装的流。</param>
/// <param name="bufferLength">读取数据时使用的缓冲区长度默认为64KB。</param>
/// <param name="maxSpeed">传输内容的最大速度默认为Int32最大值表示不限速。</param>
public StreamHttpContent(Stream stream, int bufferLength = 1024 * 64, int maxSpeed = int.MaxValue)
{
// 将提供的流分配给内部变量m_stream
this.m_stream = stream;
// 将提供的缓冲区长度分配给内部变量m_bufferLength
this.m_bufferLength = bufferLength;
// 将提供的最大速度分配给内部变量m_maxSpeed
this.m_maxSpeed = maxSpeed;
}
/// <inheritdoc/>
protected override bool OnBuildingContent<TByteBlock>(ref TByteBlock byteBlock)
{
return false;
}
/// <inheritdoc/>
protected override void OnBuildingHeader(IHttpHeader header)
{
header.Add(HttpHeaders.ContentLength, this.m_stream.Length.ToString());
}
/// <inheritdoc/>
protected override async Task WriteContent(Func<ReadOnlyMemory<byte>, Task> writeFunc, CancellationToken token)
{
Memory<byte> memory = new byte[this.m_bufferLength];

View File

@@ -6,11 +6,23 @@ using System.Threading.Tasks;
namespace TouchSocket.Http
{
/// <summary>
/// 表示以字符串形式存储的 HTTP 内容。
/// </summary>
/// <remarks>
/// 该类继承自 ReadonlyMemoryHttpContent用于处理只读的内存中 HTTP 内容。
/// 它将字符串内容转换为字节数组,并传递给基类以进行处理。
/// </remarks>
public class StringHttpContent : ReadonlyMemoryHttpContent
{
public StringHttpContent(string content,Encoding encoding) : base(encoding.GetBytes(content))
/// <summary>
/// 初始化 StringHttpContent 类的新实例。
/// </summary>
/// <param name="content">要包含的字符串内容。</param>
/// <param name="encoding">用于将字符串内容编码为字节数组的编码方式。</param>
public StringHttpContent(string content, Encoding encoding) : base(encoding.GetBytes(content))
{
// 构造函数将字符串内容和编码方式作为参数,将字符串内容转换为字节数组后传递给基类。
}
}
}

View File

@@ -6,16 +6,53 @@ using System.Threading.Tasks;
namespace TouchSocket.Http
{
/// <summary>
/// 表示一个键值对集合通常用于表示HTTP表单中的数据
/// </summary>
public interface IFormCollection : IEnumerable<KeyValuePair<string, string>>
{
/// <summary>
/// 获取集合中键值对的数量
/// </summary>
int Count { get; }
/// <summary>
/// 获取包含上传文件的集合
/// </summary>
IMultifileCollection Files { get; }
/// <summary>
/// 获取集合中所有键的集合
/// </summary>
ICollection<string> Keys { get; }
/// <summary>
/// 根据键获取对应的值
/// </summary>
/// <param name="key">要获取值的键</param>
/// <returns>与指定键关联的值</returns>
string this[string key] { get; }
/// <summary>
/// 根据键获取对应的值
/// </summary>
/// <param name="key">要获取的键</param>
/// <returns>与指定键关联的值</returns>
string Get(string key);
/// <summary>
/// 判断集合中是否包含指定的键
/// </summary>
/// <param name="key">要检查的键</param>
/// <returns>如果集合包含指定的键则返回true否则返回false</returns>
bool ContainsKey(string key);
/// <summary>
/// 尝试根据键获取对应的值
/// </summary>
/// <param name="key">要获取值的键</param>
/// <param name="value">与指定键关联的值如果键不存在则为null</param>
/// <returns>如果键存在于集合中则返回true否则返回false</returns>
bool TryGetValue(string key, out string value);
}
}

View File

@@ -6,7 +6,12 @@ using System.Threading.Tasks;
namespace TouchSocket.Http
{
public interface IMultifileCollection: IEnumerable<IFormFile>
/// <summary>
/// 定义一个多文件集合接口继承自IEnumerable{IFormFile}。
/// 此接口用于统一处理多个文件的集合,提供了遍历集合中每个文件的能力。
/// </summary>
public interface IMultifileCollection : IEnumerable<IFormFile>
{
}
}

View File

@@ -200,17 +200,30 @@ namespace TouchSocket.NamedPipe
return this.ProtectedResetIdAsync(newId);
}
/// <summary>
/// 中止当前操作,并安全地关闭相关资源。
/// </summary>
/// <param name="manual">指示中止操作是否是手动触发的。</param>
/// <param name="msg">中止操作的消息说明。</param>
protected void Abort(bool manual, string msg)
{
// 使用锁对象 m_lockForAbort 来防止并发访问,确保线程安全
lock (this.m_lockForAbort)
{
if (this.m_tryRemoveAction(this.Id, out _)&& this.m_online)
// 尝试从管理器中移除当前操作,如果成功且当前状态为在线,则进行中止操作
if (this.m_tryRemoveAction(this.Id, out _) && this.m_online)
{
// 设置在线状态为 false表示当前操作已离线
this.m_online = false;
// 安全地释放管道流资源,避免资源泄露
this.m_pipeStream.SafeDispose();
// 安全地释放保护数据处理适配器资源,避免资源泄露
this.ProtectedDataHandlingAdapter.SafeDispose();
// 启动一个新的任务来处理管道关闭后的操作,传递中止操作的参数
Task.Factory.StartNew(this.PrivateOnNamedPipeClosed, new ClosedEventArgs(manual, msg));
}
}