❤️ 关注 Furion 微信公众号有惊喜哦!
🫠 遇到问题
问题反馈
到 Furion 开源仓库反馈
成为赞助商
支持 Furion 的开源事业
Skip to main content

1.6 更新日志

Furion 框架升级/发版规则

升级前重点关注可能造成【破坏性】的标签类型

版本号规则:主版本号.次版本号.修订版本号

  • 只要【确认】为框架 bug,则当天修复,当天发版,修订版本号 加 1
  • 只要 .NET SDK 版本更新,则当天升级,当天发版,修订版本号 加 1
  • 如果 .csproj 文件有变更,则当天发版,修订版本号 加 1
  • 如果新增 拓展包,为了版本号统一,则当天发版,修订版本号 加 1
  • 如果涉及到代码重构,则当天发版,次版本号 加 1,修订版本号 清 0
  • 如果 .NET SDK 主版本号升级,则当天发版,主版本号 加 1

如有意外不能当天发版,则会在 Issue 中说明具体发版时间,正常不会超过 3 天。

v4.8.8(当前版本)

版本细节
  • 新特性

    •   定时任务看板列表支持作业分组名排序 4.8.8.43 ⏱️2023.09.14 #I7YQ9V
    •   验证特性 [DataValidation] 支持 [Display][DisplayName] 特性设置 {0} 4.8.8.42 ⏱️2023.09.01 #I7XB3T
    •   监听日志 LoggingMonitor 支持配置日志输出级别 4.8.8.41 ⏱️2023.08.25 #I7SRTP
    •   多语言支持 L.GetString(name, culture) 获取指定区域翻译 4.8.8.41 ⏱️2023.08.04 044b0ed
    •   粘土对象 .ConvertTo 支持自定义值提供器 4.8.8.40 ⏱️2023.08.03 70d5888
    •   规范化文档枚举支持 [EnumToNumber] 特性配置生成前端枚举定义代码是字符串值还是整数值类型,默认为字符串值 4.8.8.35 ⏱️2023.07.06 #I7IZ7S
    •   定时任务作业计划 OnChanged 事件处理 4.8.8.29 ⏱️2023.06.25 e4c4cf1
    •   Swagger 分组信息可在任意配置文件中通过 [openapi:分组名] 进行配置 4.8.8.26 ⏱️2023.06.20 a70eed3
    •   TP.WrapperRectangle 绘制矩形日志模板 4.8.8.25 ⏱️2023.06.14 60ffd76
    •   IServiceScope.CreateDefaultHttpContext 拓展方法 4.8.8.24 ⏱️2023.06.07 11a55e1
    •   配置模块 IgnoreConfigurationFiles 支持完整的文件通配符 4.8.8.22 ⏱️2023.05.25 #I78ABL
    •   定时任务支持二级虚拟目录 VisualPath 配置部署 4.8.8.20 ⏱️2023.05.18 #I740IA
    •   监听日志 LoggingMonitor 支持 Razor Pages 4.8.8.16 ⏱️2023.05.15 #I7332C
    •   定时任务作业处理程序工厂 IJobFactory 支持 4.8.8.13 ⏱️2023.05.08 ad58dd3
    •   AES 支持对文件(含超大文件)进行加解密 4.8.8.11 ⏱️2023.05.05 1d2265b
    •   动态 WebAPI 支持 text/plain 格式的 Body 参数 4.8.8.9 ⏱️2023.05.04 b49fe50
    •   插件化 IDynamicApiRuntimeChangeProvider 接口,可在运行时动态添加 WebAPI/Controller 4.8.8.8 ⏱️2023.05.04 322ea59
查看变化

在一些特定的需求中,我们需要在运行时动态编译代码,如动态编写 WebAPI,之后能够在不重启主机服务的情况下即可有效。比如这里动态添加 SomeClass 动态 WebAPI,然后在 Swagger/路由系统 中立即有效:

using Furion;
using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;

namespace YourProject.Application;

public class PluginApiServices : IDynamicApiController
{
private readonly IDynamicApiRuntimeChangeProvider _provider;
public PluginApiServices(IDynamicApiRuntimeChangeProvider provider)
{
_provider = provider;
}

/// <summary>
/// 动态添加 WebAPI/Controller
/// </summary>
/// <param name="csharpCode"></param>
/// <param name="assemblyName">可自行指定程序集名称</param>
/// <returns></returns>
public string Compile([FromBody] string csharpCode, [FromQuery] string assemblyName = default)
{
// 编译 C# 代码并返回动态程序集
var dynamicAssembly = App.CompileCSharpClassCode(csharpCode, assemblyName);

// 将程序集添加进动态 WebAPI 应用部件
_provider.AddAssembliesWithNotifyChanges(dynamicAssembly);

// 返回动态程序集名称
return dynamicAssembly.GetName().Name;
}

/// <summary>
/// 移除动态程序集 WebAPI/Controller
/// </summary>
public void Remove(string assemblyName)
{
_provider.RemoveAssembliesWithNotifyChanges(assemblyName);
}
}

这时只需要请求 api/plugin-api/compile 接口同时设置请求 Content-Typetext/plain,接下来传入 C# 代码字符串 即可,如:

动态C#代码字符串
using Furion.DynamicApiController;

namespace YourProject.Application;

public class SomeClass : IDynamicApiController
{
public string GetName()
{
return nameof(Furion);
}
}

之后刷新浏览器即可看到最新的 API

还可以在运行时动态卸载,使用 DELETE 请求 api/plugin-api 即可。

    •   定时任务 Schedular.CompileCSharpClassCode(code) 支持动态编译作业处理程序代码 4.8.8.7 ⏱️2023.04.30 fe1e8a1
    •   App.CompileCSharpClassCode(code) 动态编译类定义代码 4.8.8.7 ⏱️2023.04.30 fe1e8a1
    •   粘土对象支持结构 struct 对象类型 4.8.8.7 ⏱️2023.04.30 a0fa3aa
    •   定时任务支持配置 IJob 执行异常 FallbackAsync 回退策略 4.8.8.6 ⏱️2023.04.25 7671489
    •   定时任务支持在非 IOC/DI 项目类型中使用 4.8.8.5 ⏱️2023.04.24 #I6YJNB
    •   RSA 支持对超长字符(超 245 位)进行分段加解密 4.8.8.2 ⏱️2023.04.19 !788 感谢 @YaChengMu
    •   System.Text.JsonNewtonsoft.Json 对粘土对象 Clay 支持 4.8.8.1 ⏱️2023.04.18 #I6WKRZ
    •   粘土对象可反射转换成特定 IEnumerable<T> 类型:clay.ConvertTo<T>() 4.8.8 ⏱️2023.04.13 5d54a65
    •   Serve.IdleHost 支持返回 httphttps 协议 Web 地址(端口) 4.8.8 ⏱️2023.04.13 fdf7885
  • 突破性变化

    •   定时任务看板 SyncRate 配置,前后端采用最新的 SSE 推送技术替代 4.8.8.29 ⏱️2023.06.25 e4c4cf1
    •   监听日志 WriteFilterConfigureLoggerActionExecutingContextActionExecutedContext 类型为 FilterContext 4.8.8.16 ⏱️2023.05.15 #I7332C
    •   IJsonSerializerProvider 序列化接口,添加 Deserialize 反序列化方法 4.8.8.15 ⏱️2023.05.15 !815 感谢 @YaChengMu
查看变化

添加 25-32行 接口方法:

namespace Furion.JsonSerialization;

/// <summary>
/// Json 序列化提供器
/// </summary>
public interface IJsonSerializerProvider
{
/// <summary>
/// 序列化对象
/// </summary>
/// <param name="value"></param>
/// <param name="jsonSerializerOptions"></param>
/// <returns></returns>
string Serialize(object value, object jsonSerializerOptions = default);

/// <summary>
/// 反序列化字符串
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="json"></param>
/// <param name="jsonSerializerOptions"></param>
/// <returns></returns>
T Deserialize<T>(string json, object jsonSerializerOptions = default);

/// <summary>
/// 反序列化字符串
/// </summary>
/// <param name="json"></param>
/// <param name="returnType"></param>
/// <param name="jsonSerializerOptions"></param>
/// <returns></returns>
object Deserialize(string json, Type returnType, object jsonSerializerOptions = default);

/// <summary>
/// 返回读取全局配置的 JSON 选项
/// </summary>
/// <returns></returns>
object GetSerializerOptions();
}

如果使用 Newtonsoft.Json 则只需添加以下实现即可:

/// <summary>
/// 反序列化字符串
/// </summary>
/// <param name="json"></param>
/// <param name="returnType"></param>
/// <param name="jsonSerializerOptions"></param>
/// <returns></returns>
public object Deserialize(string json, Type returnType, object jsonSerializerOptions = null)
{
return JsonConvert.DeserializeObject(json, returnType, (jsonSerializerOptions ?? GetSerializerOptions()) as JsonSerializerSettings);
}
  • 问题修复

    •   模板引擎高并发读取缓存模板出现线程占用问题 4.8.8.43 ⏱️2023.09.14 #I80ZKB
    •   使用刷新 Token 也能通过鉴权检查严重安全 Bug 4.8.8.42 ⏱️2023.08.28 #I7TII4
    •   粘土对象不支持枚举类型问题 4.8.8.41 ⏱️2023.08.25 #I7VDDL
    •   定时任务因上一版本修改 4e2615b 导致自定义作业触发器异常问题 4.8.8.36 ⏱️2023.07.06 #I7J59D
    •   审计日志解析 DateTime 类型参数不是本地时间问题 4.8.8.33 ⏱️2023.06.29 #I7GW32
    •   定时任务因上一版本修改 4e2615b 导致 Cron 解析异常问题 4.8.8.32 ⏱️2023.06.28 #I7GQ5I
    •   定时任务设置额外数据不支持 long/int64 类型参数问题 4.8.8.31 ⏱️2023.06.28 4e2615b
    •   定时任务休眠毫秒数大于 int.MaxValue 时出现 ArgumentOutOfRangeException 4.8.8.27 ⏱️2023.06.21 #I7F6ZT
    •   Cron 表达式步长解析器错误 4.8.8.25 ⏱️2023.06.14 #I7D9XU
    •   修复 ExpandoObject.ToDictionary() 转换异常 4.8.8.25 ⏱️2023.06.14 #I7BY0P
    •   配置友好异常 FriendlyExceptionSettings:DefaultErrorMessage 无效问题 4.8.8.23 ⏱️2023.05.31 #I79LIG
    •   Swagger 进行分组后 Tags 不能进行分组过滤问题 4.8.8.22 ⏱️2023.05.25 #I78A55
    •  9d8cb82 代码提交导致命名服务解析异常问题 4.8.8.21 ⏱️2023.05.18 #I76JZR
    •  9d8cb82 代码提交导致服务 AOP 异常拦截问题 4.8.8.17 ⏱️2023.05.15 #I73A8E
    •   动态 WebAPI 自定义路由模板参数和自动拼接参数冲突问题 4.8.8.15 ⏱️2023.05.15 #I72ZZ2
    •   远程请求在被请求端返回非 200 状态码但实际请求已处理也抛异常问题 4.8.8.14 ⏱️2023.05.12 b14a51f
    •   App.CompileCSharpClassCode(code) 运行时添加匿名程序集编译异常问题 4.8.8.8 ⏱️2023.05.04 322ea59
    •   LoggingMonitor 打印泛型类型如果存在多个泛型参数问题 4.8.8.8 ⏱️2023.05.04 8d9cb74
    •   脱敏处理如果字典存在重复词导致异常问题 4.8.8.4 ⏱️2023.04.23 #I6Y19K
    •   远程请求 Body 参数为粘土对象 Clay 类型序列化有误 4.8.8.1 ⏱️2023.04.18 #I6WKRZ
    •   Serve.IdleHost 获取随机端口的本地地址带 $ 符号问题 4.8.8 ⏱️2023.04.13 ed6f292
  • 其他更改

    •   [UnitofWork] 支持在 Class 中指定,解决 Pages 应用警告问题 4.8.8.42 ⏱️2023.09.01 #I7X51E
    •   取消远程请求 GET/HEAD 不能传递 Body 的限制 4.8.8.39 ⏱️2023.08.02 8113460
    •   规范化文档枚举生成 json 格式,由 int32 改为 string 4.8.8.34 ⏱️2023.07.02 #I7HOPR
    •   规范化文档默认 Title 解析规则,不再自动添加空格 4.8.8.26 ⏱️2023.06.20 24b7a47
    •   组件 Component 模式支持 [DependsOn] 支持继承 4.8.8.16 ⏱️2023.05.15 #I733RF
    •   定时任务 GC 回收逻辑,避免高频添加作业导致 尾延迟 问题 4.8.8.3 ⏱️2023.04.21 #I6XIV8
    •   定时任务日志设计,减少不必要的日志输出 4.8.8.3 ⏱️2023.04.21 #I6XI2L
  • 文档

    •   Jwt 身份验证过程监听文档
    •   事件总线 Redis 集成文档
    •   粘土对象文档、虚拟文件系统文档、序列化文档、事件总线文档、远程请求文档、数据加密文档、安全授权文档、动态 WebAPI 文档、定时任务文档、JSON 序列化文档、App 静态类文档、规范化文档、配置文档
  • 贡献者


v4.8.7(已发布,.NET8)

.NET8 Preview.1 发布

🚀🎉🔥 2023 年 02 月 22 日,微软发布了 .NET8 首个预览版。

https://devblogs.microsoft.com/dotnet/announcing-dotnet-8-preview-1/

Furion 第一时间完成了适配,v4 版本开始一套代码支持 .NET5-.NET8/N,支持所有 Furion 版本升级

版本细节
  • 新特性

    •   定时任务看板支持自定义刷新频率 SyncRate 功能 4.8.7.43 ⏱️2023.04.12 703b465
    •   Serve.GetIdleHost([host]) 静态方法,可获取一个指定主机的 Web 地址(端口) 4.8.7.43 ⏱️2023.04.12 fdf788
    •   粘土对象可配置访问不存在 Key 时是抛异常还是返回 null 4.8.7.40 ⏱️2023.04.10 e994d53
    •   定时任务看板支持完全自定义 RequestPath 入口地址功能 4.8.7.34 ⏱️2023.04.04 24736f6
    •   App.GetServices(type)App.GetServices<T>() 获取服务实例集合 4.8.7.33 ⏱️2023.04.03 c3e9957
    •   远程请求 [HttpMethod]ToSaveAsync 下载远程文件并保存到磁盘方法 4.8.7.32 ⏱️2023.04.02 bfd02c1
    •   定时任务一系列 .AlterTo 修改作业触发器触发时间便捷方法 4.8.7.31 ⏱️2023.03.31 0349017
    •   多语言支持 DateTime 时间格式化配置节点 DateTimeFormatCulture 4.8.7.31 ⏱️2023.03.31 #I6RUOU
    •   Serve.IdleHost 静态属性,可获取一个随机空闲 Web 主机地址(端口) 4.8.7.29 ⏱️2023.03.30 e425063
    •   WinForm/WPF 静态方法 Serve.RunNative() 可配置是否启用 Web 主机功能 4.8.7.26 ⏱️2023.03.29 #I6R97L
    •   WinForm/WPF 支持依赖注入的 Native.CreateInstance<T>() 静态方法 4.8.7.23 ⏱️2023.03.27 53d51c3
    •   WinForm/WPF 快速注册静态方法:Serve.RunNative() 4.8.7.23 ⏱️2023.03.27 53d51c3
    •   远程请求支持 Content-Typetext/htmltext/plain 处理 4.8.7.22 ⏱️2023.03.27 #I6QMLR
    •   粘土对象可转换成 IEnumerable<T> 对象并实现 Lambda/Linq 操作 4.8.7.19 ⏱️2023.03.22 2b14ed9
查看变化
dynamic clay = Clay.Parse("{\"Foo\":\"json\",\"Bar\":100,\"Nest\":{\"Foobar\":true},\"Arr\":[\"NOR\",\"XOR\"]}");

// 将 clay.Arr 转换成 IEnumerable<dynamic>
IEnumerable<dynamic> query = clay.Arr.AsEnumerator<dynamic>();

// 实现 Lambda/Linq 操作
var result = query.Where(u => u.StartsWith("N"))
.Select(u => new
{
Name = u
})
.ToList();
    •   Crontab.IsValid(...) 静态方法,判断 Cron 表达式是否有效 4.8.7.17 ⏱️2023.03.20 #I6OHO4
    •   日志配置 WithStackFrame,可控制是否输出产生日志的程序集,类型和具体方法 4.8.7.16 ⏱️2023.03.19 5ad6ae2
查看变化

启用 WithStackFrame 日志配置后,可输出程序集,类型,方法签名信息。

// 控制台日志
services.AddConsoleFormatter(options =>
{
options.WithStackFrame = true;
});

// 文件日志
services.AddFileLogging(options =>
{
options.WithStackFrame = true;
});

// 数据库日志
services.AddDatabaseLogging(options =>
{
options.WithStackFrame = true;
});

日志输出如下:

info: 2023-03-17 18:25:06.7988349 +08:00 星期五 L System.Logging.EventBusService[0] #1
[Furion.dll] async Task Furion.EventBus.EventBusHostedService.ExecuteAsync(CancellationToken stoppingToken)
EventBus hosted service is running.
info: 2023-03-17 18:25:08.1393952 +08:00 星期五 L Microsoft.Hosting.Lifetime[14] #1
[System.Private.CoreLib.dll] void System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start<TStateMachine>(ref TStateMachine stateMachine)
Now listening on: https://localhost:5001
info: 2023-03-17 18:25:08.1620391 +08:00 星期五 L Microsoft.Hosting.Lifetime[14] #1
[System.Private.CoreLib.dll] void System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start<TStateMachine>(ref TStateMachine stateMachine)
Now listening on: http://localhost:5000
info: 2023-03-17 18:25:08.1972456 +08:00 星期五 L Microsoft.Hosting.Lifetime[0] #1
[Microsoft.Extensions.Hosting.dll] void Microsoft.Extensions.Hosting.Internal.ConsoleLifetime.OnApplicationStarted()
Application started. Press Ctrl+C to shut down.
info: 2023-03-17 18:25:08.2456579 +08:00 星期五 L Microsoft.Hosting.Lifetime[0] #1
[Microsoft.Extensions.Hosting.dll] void Microsoft.Extensions.Hosting.Internal.ConsoleLifetime.OnApplicationStarted()
Hosting environment: Development
info: 2023-03-17 18:25:08.2746134 +08:00 星期五 L Microsoft.Hosting.Lifetime[0] #1
[System.Private.CoreLib.dll] void System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(bool throwOnFirstException)
Content root path: D:\Workplaces\OpenSources\Furion\samples\Furion.Web.Entry
info: 2023-03-17 18:25:18.1917784 +08:00 星期五 L Furion.Application.TestLoggerServices[0] #16
[Furion.Application.dll] void Furion.Application.TestLoggerServices.测试日志()
我是一个日志 20

这样就清楚地知道日志是哪个程序集、哪个类型、哪个方法输出的了。

    •   定时任务看板 UI 作业列表 最近执行时间 列和优化显示效果 4.8.7.12 ⏱️2023.03.15 26462a8 cb5dd17
    •   定时任务作业计划/工厂立即执行 RunJob 方法 4.8.7.11 ⏱️2023.03.15 #I6LD9X
    •   定时任务看板 UI 提供立即执行功能 4.8.7.11 ⏱️2023.03.15 #I6LD9X
    •   远程请求 HttpRequestMessage 拓展方法 AppendHeaders 4.8.7.10 ⏱️2023.03.14 #I6MVHT
    •   定时任务作业执行上下文 JobExecutionContext 服务提供器 ServiceProvider 属性 4.8.7.10 ⏱️2023.03.14 02586f8
    •   定时任务 HTTP 作业,支持定时请求互联网 URL 地址 4.8.7.7 ⏱️2023.03.11 01d4466
查看变化
services.AddSchedule(options =>
{
options.AddHttpJob(request =>
{
request.RequestUri = "https://www.chinadot.net";
request.HttpMethod = HttpMethod.Get;
// request.Body = "{}"; // 设置请求报文体
}, Triggers.PeriodSeconds(5));
});
    •   定时任务作业触发器 Trigger 执行结果 Result 和执行耗时 ElapsedTime 属性 4.8.7.7 ⏱️2023.03.11 01d4466
    •   定时任务作业看板支持查看作业触发器执行结果 Result 和执行耗时 ElapsedTime 属性 4.8.7.7 ⏱️2023.03.11 01d4466
    •   定时任务休眠时长和唤醒时机日志输出 4.8.7.6 ⏱️2023.03.08 #I6LANE
    •   Sql 高级拦截支持返回 IEnumerable<T>T[] 类型值 4.8.7.5 ⏱️2023.03.07 f2ca2d3
查看变化

过去版本如果返回对象类型只支持 List<T>TTuple<>,现已支持 IEnumerable<T>T[]Tuple<> 混合体。

public interface ISql : ISqlDispatchProxy
{
[SqlExecute("select * from person")]
Person[] GetPersons();

[SqlExecute("select * from person")]
IEnumerable<Person> GetPersons2();

// 更复杂的组合
[SqlExecute(@"
select * from person where id = 1;
select * from person;
select * from person where id > 0;
select * from person where id > 0;
")]
(Person, List<Person>, Person[], IEnumerable<Person>) GetPersons();
}
    •   .m3u8.ts 文件类型 MIME 支持 4.8.7.5 ⏱️2023.03.07 #I6KKEM
    •   审计日志 LoggingMonitor 支持对参数贴 [SuppressMonitor] 特性跳过记录 4.8.7.3 ⏱️2023.03.01 #I6IVGW
    •   审计日志 LoggingMonitor 监听 TraceIdThreadIdAccept-Language 4.8.7.1 ⏱️2023.02.27 df35201
    •   规范化结果 UnifyContext.GetSerializerSettings(string) 静态方法 4.8.7.1 ⏱️2023.02.27 #I6HM7T
  • 突破性变化

    •   定时任务动态作业 DynamicJob 委托/方法签名 4.8.7.10 ⏱️2023.03.14 6d56b53
查看变化

减少记忆负担,统一动态作业和普通作业的 ExecuteAsync 方法签名,故做出调整。

由:

options.AddJob((serviceProvider, context, stoppingToken) =>
{
serviceProvider.GetLogger().LogInformation($"{context}");
return Task.CompletedTask;
}, Triggers.PeriodSeconds(5));

调整为:

options.AddJob((context, stoppingToken) =>
{
context.ServiceProvider.GetLogger().LogInformation($"{context}");
return Task.CompletedTask;
}, Triggers.PeriodSeconds(5));
    •   适配 .NET8 Preview.1 4.8.7 ⏱️2023.02.22
    •   脚手架支持创建 .NET8 Preview.1 项目 4.8.7 ⏱️2023.02.22
  • 问题修复

    •   远程请求获取 Cookies 时如果包含相同 Key 异常问题 4.8.7.44 ⏱️2023.04.12 #I6V3T7
    •   粘土对象转换为 Dictionary<string, object> 类型异常 4.8.7.41 ⏱️2023.04.11 f96baeb
    •   TP.Wrapper 静态类不能准确识别多行内容问题 4.8.7.40 ⏱️2023.04.10 #I6UAC8
    •   粘土对象不支持运行时动态设置携带特殊字符的 Key4.8.7.39 ⏱️2023.04.10 6572515
    •   视图引擎模型为匿名泛型集合类型时出现类型转换异常 4.8.7.38 ⏱️2023.04.07 !773
    •   定时任务通过作业 Id 删除作业不能删除作业触发器问题 4.8.7.35 ⏱️2023.04.05 312ca35
    •   动态 WebAPI 去除叠词类型命名如 ServiceService 前后缀异常问题 4.8.7.32 ⏱️2023.04.02 #I6SB3Z
    •  4.8.7.22 版本导致动态 WebAPI 类型注释丢失问题 4.8.7.27 ⏱️2023.03.29 #I6QM23
    •   粘土对象遍历对象键值对因 4.8.7.19 版本更新导致异常 4.8.7.25 ⏱️2023.03.28 #I6R4ZU
    •   Swagger UI 不显示 ControllerBase 派生类注释 4.8.7.22 ⏱️2023.03.27 #I6QM23
    •   日志输出 JSON 格式漏掉了 UseUtcTimestampTraceId 键值 4.8.7.21 ⏱️2023.03.27 5c90e65
    •   启用规范化结果后导致 WebSocket 连接断开时出现异常 4.8.7.20 ⏱️2023.03.23 #I6PI5E
    •   定时任务作业状态为 积压:0归档:6 时调用立即执行后不能恢复上一次状态 4.8.7.18 ⏱️2023.03.21 6f5aae8
    •   使用达梦数据库执行 sql 不能自动修复命令参数前缀 4.8.7.18 ⏱️2023.03.21 #I6OK4T
    •   Cron 表达式 * 符号解析器不够严谨,如:*1111aaaaa 也被解析为 * 4.8.7.17 ⏱️2023.03.20 #I6OHO4
    •   定时任务更新作业 null 值默认被跳过问题 4.8.7.17 ⏱️2023.03.20 #I6OHO4
    •   视图引擎不支持强制转换的 (object)model 类型 4.8.7.16 ⏱️2023.03.19 #I6O3BD
    •   启用请求 Body 重复读且在授权之前读取导致非 GET/HEAD/OPTION 请求异常 4.8.7.15 ⏱️2023.03.19 #I6NX9E
    •   定时任务生成 SQL 语句没有处理 ' 转义问题 4.8.7.15 ⏱️2023.03.19 #I6NXKA
    •   数据验证 ValiationTypes.GUID_OR_UUID 不支持大写问题 4.8.7.14 ⏱️2023.03.16 #I6NP22
    •   Blazor 脚手架出现 blazor.server.js 不能加载问题(4044.8.7.13 ⏱️2023.03.16 #I6NOBQ
    •   定时任务服务在停止进程时会卡住 30秒 问题 4.8.7.8 ⏱️2023.03.13 #I6MI9I #I6MHOU
    •   定时任务看板删除不存在的作业触发器出现空异常 4.8.7.7 ⏱️2023.03.11 01d4466
    •   日志消息没有处理 \n 换行符对齐问题 4.8.7.6 ⏱️2023.03.10 759bcc5
    •   审计日志 LoggingMonitor 对特定参数贴有 [FromServices] 特性依旧记录问题 4.8.7.3 ⏱️2023.03.01 17b134e
    •   Swagger 接口排序同时指定 TagOrder 之后无效 4.8.7.2 ⏱️2023.03.01 #I6IQDI #I6IP66
  • 其他更改

    •   Blazor + WebAPI 脚手架模板,默认添加授权支持 4.8.7.37 ⏱️2023.04.07 #I6OM8O 544f80d
    •   定时任务动态委托作业持久化逻辑,采用不触发持久化操作 4.8.7.36 ⏱️2023.04.06 7bb58b6
    •   多语言中间件 app.UseAppLocalization() 添加 Action<options> 委托参数 4.8.7.30 ⏱️2023.03.31 #I6RUOU
    •   定时任务 Http 作业 HttpMethod 属性拼写错成 HttpMedhod 4.8.7.24 ⏱️2023.03.28 !756
    •   粘土对象 number 类型处理,若含 .double 类型,否则转 long 类型 4.8.7.24 ⏱️2023.03.28 e82e883
    •   视图引擎默认程序集,追加 System.Collections 程序集 4.8.7.16 ⏱️2023.03.18 #I6O3BD
    •   定时任务配置选项 BuilSqlType 属性命为 BuildSqlType 4.8.7.11 ⏱️2023.03.15 92117b8
    •   定时任务查看作业触发器运行记录由保存 10条 改为 5条 4.8.7.7 ⏱️2023.03.07 01d4466
    •   脚手架模板,默认启用主流文件类型 MIME 支持 4.8.7.5 ⏱️2023.03.07 e35cdab
    •   审计日志 LoggingMonitor 返回值泛型字符串显示格式 4.8.7.1 ⏱️2023.02.27 df35201
  • 文档

    •   发布桌面程序 文档
    •   Native 全局静态类文档
    •   ASP.NET 8 集成 文档
    •   .NET7 升级 .NET8 文档
    •   定时任务文档、中间件文档、规范化结果文档、动态 WebAPI 文档、日志记录文档、事件总线文档、虚拟文件系统文档、Sql 高级代理文档、数据库实体文档、任务队列文档、跨域文档、配置选项文档、安全授权、脚手架文档、粘土对象文档、多语言文档
  • 贡献者


v4.8.6(已发布)

版本细节
  • 新特性

    •   审计日志 LoggingMonitor 支持配置序列化属性命名规则 4.8.6.12 ⏱️2023.02.21 #I6GPUP
    •   Swagger 请求授权失败后自动退出授权状态 4.8.6.12 ⏱️2023.02.20 !717
    •   粘土对象支持任何字符作为 JSON/XML4.8.6.9 ⏱️2023.02.19 f99aee8 #note_16329657
    •   动态 WebAPI 自动检查路由是否包含重复参数,如果有自动修正而不是抛异常 4.8.6.5 ⏱️2023.02.17 5f15ea1
查看变化

Furion 4.8.6.5 之前,下列代码会抛出异常The route parameter name 'roleid' appears more than one time in the route template.

原因是生成的路由包含了多个 {roleId}/api/with-class/system/role/deptTree/{roleId}/{roleId}

public class WithClass : IDynamicApiController
{
[HttpGet("system/role/deptTree/{roleId}")] // 过去版本抛异常,Furion 4.8.6.5+ 正常~
public string GetResult2(string roleId)
{
return nameof(Furion);
}
}

新版本 Furion 4.8.6.5+ 修正了该错误,自动移除后面重复的路由参数且不再抛异常,也就是最终生成路由为:/api/with-class/system/role/deptTree/{roleId}

    •   byte[] 类型 MD5 加密/比较重载方法 4.8.6.3 ⏱️2023.02.15 #I6F1NT
    •   动态 WebAPI 支持 [RouteConstraint(":*")] 路由约束 4.8.6.2 ⏱️2023.02.10 #I6E6JA
    •   Swagger 启用登录后配置 CheckUrl 可获取本地存储的 Authorization 请求报文头 4.8.6.2 ⏱️2023.02.10 #I6E3LB
    •   多语言支持 .json 文件配置方式(推荐) 4.8.6 ⏱️2023.02.08 #I6DL71 #I5DXKP
    •   定时任务 IScheduler.[Try]UpdateDetail(builder => {})IScheduler.[Try]UpdateTrigger(triggerId, builder => {}) 重载方法 4.8.6 ⏱️2023.02.08 6e43a54
查看变化
  • 更新作业信息
// 返回 ScheduleResult 类型
var scheduleResult = Scheduler.TryUpdateDetail(jobBuilder =>
{
jobBuilder.SetDescription("~~~");
}, out var jobDetail);

// 无返回值
scheduler.UpdateDetail(jobBuilder =>
{
jobBuilder.SetDescription("~~~");
});
  • 更新作业触发器
// 返回 ScheduleResult 类型
var scheduleResult = scheduler.TryUpdateTrigger("triggerId", triggerBuilder =>
{
triggerBuilder.SetDescription("~~");
}, out var trigger);

// 无返回值
scheduler.UpdateTrigger("triggerId", triggerBuilder =>
{
triggerBuilder.SetDescription("~~");
});
    •   审计日志 LoggingMonitor 支持 [DisplayName] 特性解析和 Title 属性记录 4.8.5.10 ⏱️2023.02.07 #I6DHMF
    •   远程请求配置 SetHttpVersion(version) 配置,可配置 HTTP 请求版本,默认为 1.1 4.8.5.8 ⏱️2023.02.06 #I6D64H
    •   动态 WebAPI 支持更加强大的路由组合功能 4.8.5.7 ⏱️2023.02.03 #I6CLPT
查看变化
using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;

namespace WebApplication38;

[Route("api/[controller]")]
[Route("api2/[controller]")]
public class Test1Service : IDynamicApiController
{
[HttpGet("test")]
[HttpPost]
[AcceptVerbs("PUT", "PATCH")]
public async Task GetTestName()
{
await Task.CompletedTask;
}
}

public class Test2Service : IDynamicApiController
{
[HttpGet("/root/test")]
[HttpGet("test")]
[HttpGet(Name = "other-test")]
[HttpGet("template-test", Name = "other-test")]
[HttpPost]
[AcceptVerbs("PUT", "PATCH")]
public async Task GetTestName()
{
await Task.CompletedTask;
}
}

[Route("api/[controller]")]
[Route("api2/[controller]/second")]
[Route("api3/[controller]/three")]
public class Test3Service : IDynamicApiController
{
[HttpGet]
[HttpGet("get/[action]")]
[HttpPost]
[HttpPost("post/cus-version")]
public string GetVersion()
{
return "1.0.0";
}
}
    •   定时任务 Dashboard 可自定义入口地址 /schedule 4.8.5.6 ⏱️2023.02.02 c5639f5
    •   App.GetServiceLifetime(type) 获取服务注册生命周期类型 4.8.5.3 ⏱️2023.01.31 4a573a8
    •   审计日志 LoggingMonitor 记录 HTTP 响应状态码 4.8.5.2 ⏱️2023.01.30 abb4cbd
    •   定时任务执行上下文 RunId 属性,用于标识单次作业触发器执行 4.8.5.1 ⏱️2023.01.30 1aac470
  • 突破性变化

    •   适配 .NET7.0.3.NET6.0.14 4.8.6.3 ⏱️2023.02.15 eecbf83
    •   动态 WebAPI 生成路由 [HttpMethod(template)] 规则 4.8.5.7 ⏱️2023.02.03 #I6CLPT
查看变化

在过去,TestMethod 生成路由为:/mytest

// 注意这里没有 [Route] 特性
public class ClassService: IDynamicApiController
{
[HttpPost("mytest")]
public void TestMethod()
{
}
}

新版本:TestMethod 生成路由为:/api/class/mytestTestMethod2 生成路由为:/mytest

// 注意这里没有 [Route] 特性
public class ClassService: IDynamicApiController
{
[HttpPost("mytest")]
public void TestMethod()
{
}

[HttpPost("/mytest")]
public void TestMethod2()
{
}
}

也就是新版本如果不需要自动添加前缀,需在前面添加 /,旧版本不需要。

  • 问题修复

    •   规范化结果不带 mini-profiler 版本启动登录 UI 后不能传递 headers 问题 4.8.6.11 ⏱️2023.02.20 #I6G8IR
    •   Serve.Run()#I6G02W 更改导致不配置端口时出现异常无法启动问题 4.8.6.10 ⏱️2023.02.20 #I6G6AR
    •   动态 WebAPI 不支持嵌套继承 [Route] 特性问题 4.8.6.8 ⏱️2023.02.18 #I6CLPT
查看变化

过去版本生成错误重复路由,如:api/system/SystemDictionary/api/system/SystemDictionary/Add,现已修正。

public class WithClass : IDynamicApiController
{
[Route("Add")]
public void Add()
{

}

[Route("Edit")]
public void Edit()
{

}
}

[Route("api/system/SystemDictionary")]
public class SystemService : WithClass
{
public void Some()
{
}
}
    •   Serve.Run(urls: "端口") 设置端口在 .NET6/7 下发布后始终是 80 端口问题 4.8.6.6 ⏱️2023.02.18 #I6G02W
    •   粘土对象不支持 中文 作为 JSON/XML 键问题 4.8.6.6 ⏱️2023.02.18 4961e01
    •   远程请求代理模式配置了 WithEncodeUrl = false 无效问题 4.8.6.4 ⏱️2023.02.16 89639ba
    •   动态 WebAPI 自定义 [HttpMethod(template)] 之后生成错误路由 4.8.6.1 ⏱️2023.02.08 59fe53b
    •   由于 #I6D64H 导致远程请求出现 Specified method is not supported. 问题 4.8.5.9 ⏱️2023.02.07 #I6DEEE #I6D64H
    •   优化远程请求 ReadAsStringAsync 底层方法,尝试修复 Error while copying content to a stream. 错误 4.8.5.8 ⏱️2023.02.06 #I6D64H
    •   规范化结果不支持 OData 协议控制器 4.8.5.5 ⏱️2023.02.01 !571
    •   启用 Swagger 登录功能之后不能触发响应拦截器 4.8.5.5 ⏱️2023.02.01 #I6C9A2 !702 !703
    •   在数据库日志的 IDatabaseLoggingWriter 实现类中依赖注入 ILogger<> 导致死循环 4.8.5.4 ⏱️2023.02.01 #I6C6QU
    •   Furion.Xunit/Furion.Pure.Xunit 单元测试依赖注入单例服务时不是同一实例问题 4.8.5.3 ⏱️2023.01.31 305511e
    •   数据库日志提供程序在应用程序终止时出现空异常问题 4.8.5 ⏱️2023.01.28 #I6AZ8Y
    •   实体拓展方式操作数据库出现空异常问题 4.8.5 ⏱️2023.01.28 #I6AXU6
  • 其他更改

    •   脱敏处理 sensitive-words.txt 嵌入文件支持 UTF8 BOM 编码,感谢 @man119 4.8.6.7 ⏱️2023.02.18 #I6G1JN
    •   Serve.Run() 迷你主机默认添加 JSON 中文乱码处理 4.8.6.3 ⏱️2023.02.15 86b5f9f
  • 文档

    •   多语言 .json 配置方式文档
    •   日志文档、定时任务文档、动态 WebAPI 文档,规范化结果文档,App 静态类文档,Oops 静态类文档、虚拟文件系统文档 !704,远程请求文档,序列化文档、入门文档、脱敏模块文档
  • 贡献者


v4.8.4(已发布,全新定时任务)

更好的 Furion,更好的自己

在过去两年,实现 Furion 从无到有,编写文档已逾三百万字,过程心酸开源人自知。

这一路日夜兼程,嘲讽批评常伴眼耳,即便辛苦无奈、想过放弃,但为了那微不足道的成就感依然努力着。

当然,也收获了不少... 越来越多拥趸者,越发精湛技术能力,更高层次思维模式,还有许多跨界跨行朋友。

在 《开源指北》中,我曾说道:“开源如同人的脸,好坏一面便知,缺点可能会受到嘲讽批评,优点也会收获赞扬尊重。别担心,他们正在塑造更好的你。”

.NET 要在国内真正发展起来,必须得有一些追逐梦想的人在做着不计付出的事情,而我希望自己能贡献一份微薄之力。所以,这一次重新起航,重塑 Furion 重塑自己。也许未来在某个 IT 圈但凡有人谈起 .NET 还能瞟到 Furion 的身影。

版本细节
  • 新特性

    •   🎉 全新的定时任务模块 4.8.0查看源码
    •   🎉 全新的 Cron 表达式模块 4.8.0查看源码
    •   🎉 全新的任务队列模块 4.8.3查看源码
    •   视图引擎支持无命名空间的强类型 4.8.4.16 ⏱️2023.01.15 #I6ABN3 #I6A7SI 076bb17
    •   视图引擎支持匿名类型模型带集合类型属性 @foreach 遍历 4.8.4.15 ⏱️2023.01.13 #I6A7SI
    •   Swagger 支持复制路由地址功能 4.8.4.13 ⏱️2023.01.11 #I5VNJI
    •   动态 WebAPI 方法支持通过 [ActionName(名称)][HttpMethod(Name=名称)] 指定路由名称 4.8.4.12 ⏱️2023.01.10 #I69AOJ f699540
    •   BadPageResult.Status401Unauthorized 等常见状态码 401,403,404,500 静态属性 4.8.4.11 ⏱️2023.01.09 #I69KQF
    •   crontab.GetSleepTimeSpan(baseTime) 实例方法 4.8.4.10 ⏱️2023.01.09 #I69HM4
    •   Enqueue/EnqueueAsync 支持 Cron 表达式 实例重载方法 4.8.4.10 ⏱️2023.01.09 #I69HM4
    •   *.bcmap.properties 文件类型 MIME 支持 4.8.4.9 ⏱️2023.01.06 !694
    •   定时任务 Dashboard 查看作业触发器最近运行记录功能 4.8.4.3 ⏱️2023.01.03 e7d24d8
    •   定时任务作业触发器 trigger.GetTimelines() 获取最近 10 条运行记录列表 4.8.4.3 ⏱️2023.01.03 e7d24d8
    •   定时任务 Dashboard 看板 4.8.4 ⏱️2022.12.30 d3f9669
    •   定时任务 IScheduler.GetEnumerable() 方法,可将作业计划转换成可枚举字典 4.8.4 ⏱️2022.12.30 4d5235c
    •   L.SetCurrentUICulture(culture)L.GetCurrentUICulture() 静态方法,可在运行时动态修改当前线程区域性 4.8.3.10 ⏱️2022.12.23 #I66JWA
    •   L.SetCulture(culture, immediately) 方法重载,可配置运行时修改多语言立即有效 4.8.3.10 ⏱️2022.12.23 #I66JWA
    •   定时任务配置选项 options.JobDetail.LogEnabled 配置,可自动输出执行日志 4.8.3.7 ⏱️2022.12.14 58d2c20
    •   ValidationTypes 更多常见验证格式(手机机身码类型统一社会信用代码GUID/UUIDbase644.8.3.6 ⏱️2022.12.13 3680d7a
    •   定时任务 IScheduler 对象每次操作后自动刷新和提供手动刷新 Reload() 方法 4.8.3.3 ⏱️2022.12.09 #I65EQ1
    •   定时任务间隔分钟作业触发器 Triggers.PeriodMinutes(5)[PeriodMinutes(5)] 特性 4.8.2.8 ⏱️2022.12.01 8e1f06f
    •   定时任务工作日作业触发器 Triggers.Workday()[Workday] 特性 4.8.2.6 ⏱️2022.11.30 28b2d20
    •   定时任务作业校对功能,可对误差进行校正 4.8.2.6 ⏱️2022.11.30 f725a25
    •   Crontab.ParseAt(..) 静态方法 4.8.2.6 ⏱️2022.11.30 035cc23
    •   Crontab 所有 Macro At 静态方法 4.8.2.6 ⏱️2022.11.30 a15b69d
    •   Crontab.Workday 表示周一至周五的 Macro 静态属性 4.8.2.6 ⏱️2022.11.30 a15b69d
    •   定时任务 Triggers 所有带 AtCron 表达式触发器构建器及特性 4.8.2.5 ⏱️2022.11.29 #I63PLR
    •   App.GetThreadId()App.GetTraceId() 获取线程 Id 和请求 TraceId 4.8.2.4 ⏱️2022.11.29 910fc1f
    •   App.GetExecutionTime(() => { /*Your Code*/ }) 获取代码执行耗时 4.8.2.4 ⏱️2022.11.29 5ab4b19
    •   定时任务批量添加 SchedulerBuilder 作业功能 4.8.2.4 ⏱️2022.11.29 5faa67b
    •   定时任务 BuildSqlType 配置,可设置生成不同数据库类型的 SQL 语句 4.8.2.3 ⏱️2022.11.29 293f9bc !675
    •   JobDetailTrigger 自定义 ConvertToSQL 输出 SQL 配置 4.8.2 ⏱️2022.11.27 0bb9d8f
    •   动态作业处理程序委托支持 4.8.1.8 ⏱️2022.11.27 e02266c
    •   作业触发器 ResetOnlyOnce 属性,支持只运行一次的作业重新启动服务重复执行 4.8.1.5 ⏱️2022.11.25 a8be728
    •   事件总线支持简单的 Order 编排规则 4.8.0 833c0d4
    •   远程请求代理模式对于基元类型参数支持自动获取参数名 4.8.0 #I60OT6
    •   动态 WebAPI 自动识别方法的接口参数是否是服务,如果是自动添加 [FromServices] 特性 4.8.0 fae60a9
    •   远程请求 [QueryString] 特性添加时间格式化 Format 属性 4.8.1.2 !670
    •   Serve.Run 模式的 .ConfigureServices 方法 4.8.0 023391b
    •   Serve.RunGeneric 通用泛型主机方法 4.8.0 6865f3d
    •   Serve.Run()additional 参数 4.8.0 023391b
查看变化

极速入门现在可以便捷注册服务,写测试例子的时候非常有用,无需编写 Startup.cs

Serve.Run(additional: services =>
{
services.AddRemoteRequest();
});

// 通用泛型主机方式
Serve.RunGeneric(additional: services =>
{
services.AddRemoteRequest();
});

// 还可以省去 additional
Serve.Run(services =>
{
services.AddRemoteRequest();
});

// 通用泛型主机方式
Serve.RunGeneric(services =>
{
services.AddRemoteRequest();
});
    •   Serve.Run 主机返回值 IHost 4.8.0 #I61XHV
查看变化

Winfom/WPF 应用程序中,我们希望关闭窗体或退出应用程序时,能够关闭 Serve 主机:

using Microsoft.Extensions.Hosting;

public partial class App : Application
{
private readonly IHost _host;
public App()
{
_host = Serve.Run(silence: true);
}

protected override void OnExit(ExitEventArgs e)
{
_host.StopAsync();
_host.Dispose();

base.OnExit(e);
}
}
    •   日志 JSON 自动美化格式化器 LoggerFormatter.JsonIndented 4.8.0 7b9268c
    •   LoggingMonitorJsonIndented 配置,可配置是否美化 JSON 4.8.0 7b9268c
查看变化

默认情况下,配置输出 JSON 格式化 LoggerFormatter.Json 会浓缩到一行显示。

新版本支持 LoggerFormatter.JsonIndented 美化 JSON 配置:

// 控制台日志
services.AddConsoleFormatter(options =>
{
options.MessageFormat = LoggerFormatter.JsonIndented;
});

// 文件日志
services.AddFileLogging("mytemplate.log", options =>
{
options.MessageFormat = LoggerFormatter.JsonIndented;
});

// 数据库日志
services.AddDatabaseLogging<DatabaseLoggingWriter>(options =>
{
options.MessageFormat = LoggerFormatter.JsonIndented;
});

// LoggingMonitor 日志
services.AddMonitorLogging(options =>
{
options.JsonIndented = true;
});
    •   日志模块是否输出 TraceId,同一个请求的日志 TraceId 一致 4.8.1.3 #I62VGG
查看变化

在生产环境中,日志的输出是非常频繁的,但是很难从日志文件中判断哪些日志是属于同一个请求输出的,所以新增 WithTraceId 配置。

// 控制台日志
services.AddConsoleFormatter(options =>
{
options.WithTraceId = true;
});

// 文件日志
services.AddFileLogging("mytemplate.log", options =>
{
options.WithTraceId = true;
});

// 数据库日志
services.AddDatabaseLogging<DatabaseLoggingWriter>(options =>
{
options.WithTraceId = true;
});

输出日志如下:

info: 2022-11-24 14:34:55.1717549 +08:00 星期四 L System.Logging.EventBusService[0] #1
EventBus Hosted Service is running.
info: 2022-11-24 14:34:55.2504015 +08:00 星期四 L System.Logging.ScheduleService[0] #1
Schedule Hosted Service is running.
info: 2022-11-24 14:34:56.4280796 +08:00 星期四 L Microsoft.Hosting.Lifetime[14] #1
Now listening on: https://localhost:5001
info: 2022-11-24 14:34:56.4331170 +08:00 星期四 L Microsoft.Hosting.Lifetime[14] #1
Now listening on: http://localhost:5000
info: 2022-11-24 14:34:56.4384567 +08:00 星期四 L Microsoft.Hosting.Lifetime[0] #1
Application started. Press Ctrl+C to shut down.
info: 2022-11-24 14:34:56.4408766 +08:00 星期四 L Microsoft.Hosting.Lifetime[0] #1
Hosting environment: Development
info: 2022-11-24 14:34:56.4427659 +08:00 星期四 L Microsoft.Hosting.Lifetime[0] #1
Content root path: D:\Workplaces\OpenSources\Furion\samples\Furion.Web.Entry
info: 2022-11-24 14:35:06.8507338 +08:00 Thursday L Furion.Application.TestLoggerServices[0] #17 '00-48df9ac5c8280de2f301faa44a23a10c-b75678d9f3883b0b-00'
我是一个日志 20
info: 2022-11-24 14:35:16.0373384 +08:00 星期四 L Furion.Application.TestLoggerServices[0] #17 '00-ff4fb15d6ff41a0411784e66400f0dfd-962bc25eff788b25-00'
我是一个日志 20
  • 突破性变化

    •   旧版本定时任务为 弃用 状态(2022 年 12 月 31 日彻底删除),如需取消警告在 .csproj 中添加 <NoWarn>0618</NoWarn> 4.8.0 旧版本文档
    •   JSON 静态类所有方法的 serviceProvider 参数(无破坏) 4.8.0 962fb16
    •   Http 静态类所有方法的 serviceProvider 参数(无破坏) 4.8.0 962fb16
    •   定时任务底层所有代码,日志,注释,文档 4.8.1.10 ⏱️2022.12.05
  • 问题修复

    •   定时任务 StartAll 出现个别作业显示 无触发时间 的状态 4.8.4.14 ⏱️2023.01.12 #I6A08X
    •   动态 WebAPI 配置 [Consumes] 特性后 Swagger 不显示问题 4.8.4.12 ⏱️2023.01.10 daf25f8
    •   定时任务停止作业触发器后运行记录不能写入最新记录问题 4.8.4.8 ⏱️2023.01.05 d4c553f
    •   数据库日志注册在一些特殊情况下丢失日志上下文问题 4.8.4.6 ⏱️2023.01.04 #I68PDF
    •   定时任务使用 Furion.Pure 包访问 Dashboard 出现 404 问题 4.8.4.2 ⏱️2023.01.02 21977b7
    •   在类中贴 [SuppressMonitor] 特性但 LoggingMonitor 依然输出问题 4.8.4 ⏱️2022.12.30 #I6882I
    •   远程请求配置 WithEncodeUrl(false)application/x-www-form-urlencoded 请求类型无效 4.8.4 ⏱️2022.12.30 #I682DX
    •   LoggingMonitor 序列化 IQueryable<>OData 返回值类型出现死循环问题 4.8.3.4 ⏱️2022.12.10 7e8c9d0
    •   定时任务通过 scheduler.RemoveTrigger(triggerId) 报异常问题 4.8.3.3 ⏱️2022.12.09 #I65EQ1
    •   定时任务作业触发器配置了 EndTimeStartTime 之后 Status 没有对应上 4.8.3.1 ⏱️2022.12.09 52a5506
    •   定时任务通过 scheduler.AddTrigger(triggerBuilder) 无效的问题 4.8.3.1 ⏱️2022.12.09 #I65EQ1
    •   作业拥有多个触发器时暂停作业后依然存在个别未暂停的清空(并发问题) 4.8.2.12 ⏱️2022.12.07 #I655W9
    •   通过 Ctrl + C 终止应用程序后获取 TraceId 出现对象已释放异常 4.8.1.12 ⏱️2022.12.07 55c3e49
    •   cli.ps1 脚本不支持 EFCore 7.0 问题 4.8.1.12 ⏱️2022.12.07 !676
    •   EFCore 实体监听器 IEntityChangedListener 问题 4.8.1.7 ⏱️2022.11.26 #I61CTI
    •   定时任务生成的 SQL 语句不支持 MySQL 问题 4.8.1.7 ⏱️2022.11.26 #I638ZC
    •   运行时启动/暂停作业无效问题 4.8.1.6 ⏱️2022.11.25 #I6368M
    •   作业触发器不符合下一次执行规律但 NextRunTime 不为 null 情况 4.8.1.5 ⏱️2022.11.25 a8be728
    •  .NET6/7 降级回 .NET5 找不到 .AddDateOnlyConverters().AddTimeOnlyConverters() 拓展方法问题 4.8.0 cdddf8d
    •   Retry.InvokeAsync 方法如果不传入 fallbackPolicy 参数报空异常问题 4.8.0 21af847
    •   动态 WebAPI 不支持在 .NET7 不声明 [FromServices] 自动注入问题 4.8.0 #I62HP1
    •   远程请求 GetAsStreamAsync()System.InvalidOperationException: Response Content-Length mismatch 异常问题 4.8.1 #I62QY4
    •   LoggingMonitor 配置 WriteFilter 不起作用问题 4.8.1.2 #I62P52 90bcfda
    •   EFCore 个别关系型数据库 PostgreSQL/SqlServer/MySql 出现短暂不能连接问题 4.8.1.3 2c530ef
    •   日志模块因 v4.8.0+ 版本导致写入数据库日志空异常问题 4.8.2.1 ⏱️2022.11.28 8d9d72b
  • 其他更改

    •   定时任务调度器时间精度,控制持续执行一年误差在 100ms 以内 4.8.2.9 ⏱️2022.12.01 334d089
    •   定时任务作业计划工厂 GetNextRunJobs() 方法逻辑 4.8.2.7 ⏱️2022.11.30 #I63VS2
    •   LoggingMonitor 解析授权逻辑,如果接口未授权则不打印授权信息 4.8.2.1 ⏱️2022.11.28 #I63D2E
  • 文档

    •   新版本定时任务文档
    •   Cron 表达式解析文档
    •   任务队列文档
    •   Schedular 全局静态类文档
    •   TaskQueued 全局静态类文档
    •   作业执行器实现超时文档 4.8.3.8 ⏱️2022.12.20
    •   作业触发器 ResetOnlyOnce 文档 4.8.1.5 ⏱️2022.11.25 a8be728
    •   通过 Roslyn 动态编译代码创建 IJob 类型文档 4.8.1.5 ⏱️2022.11.25 2c5e5be
    •   自定义 JobDetailTrigger 输出 SQL 文档 4.8.2 ⏱️2022.11.27 0bb9d8f
    •   远程请求 [QueryString] 配置时间类型 Format 格式化文档 4.8.1.2 ⏱️2022.11.25 !673
    •   Serve.Run() 入门文档文档、安全授权文档、前端接口代理文档、事件总线文档、日志文档、Worker Service 文档、数据库实体触发器文档、App 静态类文档、包管理工具文档

v4.7.9(已发布,.NET7)

.NET7 发布

🚀🎉🔥 2022 年 11 月 08 日,微软发布了 .NET7 首个正式版。

Furion 第一时间完成了适配,v4 版本开始一套代码支持 .NET5-.NET7/N,支持所有 Furion 版本升级

版本细节
  • 新特性

    •   日志模块时间格式化默认输出 毫秒 部分,针对并发比较高的场景 4.7.0 c0dc36c
    •   写入数据库日志死循环输出检测机制 4.7.0 30dea0c
    •   LoggingMonitor 输出 系统信息.NET 架构基础框架 4.7.1 aeda902
    •   远程请求 .SetQueries(obj, ignoreNullValue) 重载方法 4.7.3 #I5Z8KC
    •   远程请求 .GetCookies().GetSetCookies() 拓展方法 4.7.5 #I5ZY1L
    •   事件总线 .ReplaceStorerOrFallback 自定义事件源存储器方法,可在自定义初始失败时回退到默认值 4.7.6 #I602NU
    •   LoggingMonitor 输出 启动信息Cookies请求端源 信息 4.7.7 3037b04
    •   JSON 序列化 DateOnlyTimeOnly 类型转换器:.AddDateOnlyConverters().AddTimeOnlyConverters() 4.7.9 !657 47a5fcb
    •   HttpContext.ReadBodyContentAsync() 拓展方法重复读取 Body 内容 4.7.9 #I60IYU
  • 突破性变化

    •   所有脚手架支持 -f 指定 .NET 版本 4.7.6 #I603AZ
查看变化
# 创建 .NET5 版本
dotnet new furionapi -n 项目名称 -f net5

# 创建 .NET6 版本
dotnet new furionapi -n 项目名称 -f net6

# 创建 .NET7 版本
dotnet new furionapi -n 项目名称 -f net7
    •   适配 .NET 6.0.11.NET 7 4.7.5 7df3195
    •   所有脚手架至 .NET 7 4.7.5 7df3195
    •   LogContext 类型的所有方法至 Furion.Logging 命名空间下,解决空异常问题 4.7.3 #I5YOT3
查看变化

由:

var value = logContext.Get("Key");  // 过去如果 logContext == null 报错

改为:

using Furion.Logging;

var value = logContext.Get("Key"); // 新版本不会报错,且 value = null
    •   旧版本定时任务为 弃用 状态(一周内发布新版),如需取消警告在 .csproj 中添加 <NoWarn>0618</NoWarn> 4.7.9 0ff3ac0
  • 问题修复

    •   生成 JWT Token 时间戳和自动刷新逻辑在高并发下检查有效性不够精确问题,原因是时间戳丢掉了毫秒部分 4.7.0 3c0c017
    •   IDatabaseLoggingWriter 实现类中输出日志导致死循环问题 4.7.0 30dea0c
    •   规范化结果 OnResponseStatusCodes 方法在 Response 已完成写入时设置出现异常问题 4.7.2 #I5YBHL
    •   L.SetCulture("zh-CN");Response 已完成写入时设置出现异常问题 4.7.2 #I5YBHL
    •   动态 WebAPI 在类上配置 [Route] 特性且包含 [action] 模板导致生成错误接口路径 4.7.2 #I5YEZQ
    •   启用二级虚拟目录 AppSettings:VirtualPath 导致 swaggerminiprofile 加载失败 4.7.3 #I5Z8RM
    •   LoggingMonitor 监听带有 [FromServices] 的方法参数或接口类型参数出错 4.7.7 3037b04
    •   HttpRequest 通过 .ReadBodyContentAsync() 读取不到 Body 问题 4.7.9 #I60IYU
  • 其他更改

    •   JSON Schema 配置,新增日志更多参数提醒 4.7.0 74bee56
    •   日志记录时间格式默认输出带 7位 的毫秒值 4.7.1 aeda902
    •   所有脚手架默认启用 单文件/独立部署 配置 4.7.7 1277f53
  • 文档

    •   IIS 回收问题解决方案文档
    •   远程请求获取 Cookies 文档
    •   LoggingMonitor 写入数据库文档
    •   JSON 序列化 DateOnlyTimeOnly 类型处理文档
    •   HttpContext 读取 Body 内容文档
    •   PM2 配置文件 json 部署文档
    •   日志记录文档、定时任务文档、远程请求文档、脚手架文档
  • 特别贡献


v4.6.9(已发布)

版本细节
  • 新特性

    •   LoggingMonitor 支持 FileResult 类型监听 4.6.0 bf9c0b1
    •   LogMessage 结构 UseUtcTimestamp 字段,解释日志记录时间格式是 UTC 还是 LOCAL 时间 4.6.1 aab0371
    •   事件总线模块重试失败后支持回调 4.6.1 #I5UVMV
    •   LoggingMonitor 支持序列化忽略指定属性名或属性类型 4.6.1 81c6343
    •   long 序列化丢精度的 JsonConvert 内置转换器,.AddLongTypeConverters() 4.6.5 #I5VJHC aded58d
    •   app.EnableBuffering() 拓展,解决 Request.Body 不能重复读问题 4.6.5 aded58d
    •   支持特别接口使用特定的序列化规则 4.6.6 797b0bf
    •   LoggingMonitor 自动解析 JWT 时间戳为时间格式 4.6.8 9e31b0b
  • 突破性变化

    •   适配 .NET 6.0.10.NET 7 RC2 4.6.2 6bb2fad
    •   内置 Microsoft.AspNetCore.Mvc.NewtonsoftJson 拓展,原因是太多人使用了 4.6.5 aded58d
    •   "some log".SetCategory(name) 拓展方法 4.6.0 ec4838c
    •   DateOnlyJsonConverterDateOnlyOffsetJsonConverter 处理 4.6.5 aded58d
    •   事件总线触发处理程序的逻辑,由过去的 foreach 改为 Parallel.ForEach,吞吐量提升近 4 倍 4.6.4 7384c9c
    •   .AddDateFormatString() 名称为 .AddDateTimeTypeConverters() 4.6.5 aded58d
    •   重构日志模块设置上下文数据功能 4.6.0 1c198ee
查看变化

由于过去版本设置日志上下文有多线程异常和堆内存溢出风险,所以重新设计了日志上下文的写法。

由:

_logger.ScopeContext(ctx => ctx.Set("Name", "Furion").Set("UserId", 10))
.LogInformation("我是一个日志 {id}", 20);

改为:

using (var scope = _logger.ScopeContext(ctx => ctx.Set("Name", "Furion").Set("UserId", 10)))
{
_logger.LogInformation("我是一个日志 {id}", 20);
}

// 也可以简写
using var scope = _logger.ScopeContext(ctx => ctx.Set("Name", "Furion").Set("UserId", 10));
_logger.LogInformation("我是一个日志 {id}", 20);
    •   远程请求 .SetTimeout[Timeout] 配置方法,采用全局统一配置 4.6.4 7384c9c
查看变化

默认情况下,HttpClient 请求超时时间为 100秒,可根据实际情况进行设置:

// 配置默认 HttpClient
options.AddHttpClient(string.Empty, c =>
{
c.Timeout = TimeSpan.FromMinutes(2);
});

// 配置特定客户端
options.AddHttpClient("github", c =>
{
c.Timeout = TimeSpan.FromMinutes(2);
});
  • 问题修复

    •   4.5.9+ 版本新增的 IncludeScopes 配置导致日志上下文失效 4.6.0 4a76841
    •   多个 sql 共用 DbParameters 出现冲突问题 4.6.0 #I5UO2H
    •   高频率写入日志导致堆内存溢出的异常问题 4.6.0 #I5UJRS
    •   框架内部所有使用 .CreateLogger 创建的日志对象无法应用上下文问题 4.6.0 ec4838c
    •   远程请求不能在 Worker Serivce 中进行构造函数注入,原因是注册为 Scope 范围作用域 4.6.3 974f835
    •   个别服务器的 SQL Server 不支持 TLS 1.2 协议问题 4.6.3 974f835
    •   .ToDictionary() 拓展不支持 JObject 类型问题 4.6.5 #I5VJHC a11bf8d
    •   LoggingMonitor 处理 long 类型丢精度问题 4.6.5 #I5VJHC aded58d
    •   动态 WebAPIclass 类型上贴 [ApiDescriptionSettings(false)] 导致接口 404 问题 4.6.7 #I5WQ18
    •   超高频率下发送事件总线消息,但是 GC 来不及回收导致内存和 CPU 爆掉问题 4.6.8 dbc7935
    •   JWT 模块自动刷新 Token 达到临界值时导致自动刷新失败,并返回错误的 401 状态码 4.6.8 #I5WXHZ
    •   自动生成 vue/react/angular 客户端工具库错误处理 Token 问题 4.6.8 #I5WXHZ
    •   远程请求没有正确处理 数组和集合 类型的 url 参数 4.6.9 #I5XIQ4
    •   自定义 Tenant 实体且包含 TenantId 属性且没有继承 EntityBase/Entity 基类出现 The entity type 'Tenant' requires a primary key to be defined 4.6.9 #I4UM3E
  • 其他更改

    •   LoggingMonitor 返回值类型是 泛型 时获取 FullName 带程序集签名问题 4.6.2 f0aaec6
    •   优化远程请求性能,添加复用池的机制,避免频繁销毁创建 4.6.4 7384c9c
  • 文档

    •   远程请求设置客户端生命周期配置文档和新超时配置文档
    •   JSON 序列化处理 long 类型说明文档
    •   JSON 反序列化 DateTimeOffset 类型个别格式出错问题解决方案文档
    •   Worker Service 实现 串行 操作文档
    •   关闭 .NET Core 底层日志和远程请求日志文档
    •   规范化结果支持特定接口配置独立序列化配置文档
    •   日志记录文档、事件总线文档、数据库入门文档、JSON 序列化文档、远程请求文档、安全授权文档、生成前端请求代理文档

v4.5.9(已发布)

版本细节
  • 新特性

    •   .AddConsoleFormatter() 拓展简化控制台日志模板配置 4.5.0 #I5TCMO
    •   控制台和文件日志时间默认显示 星期几 4.5.1 #I5TKL5
    •   控制台和文件日志支持配置 options.DateFormat 日期格式化 4.5.1 #I5TKL5
    •   控制台日志带颜色输出,比如高亮 日志级别 4.5.1 #I5TKL5
    •   控制台格式化配置 options.WriteHandler 完全自定义配置 4.5.2 7fb3036
    •   日志输出 JSON 格式化配置 4.5.2 #I5TWC1 #I5OUT1
    •   数据库日志写入独立日志模板配置、独立日期格式配置 4.5.2 #I5TWC1
    •   LogMessage 结构类 LogDateTimeThreadIdState 属性 4.5.2 #I5TWC1
    •   LoggingMonitor 可配置 JsonWriterOptions 属性 4.5.4 #I5U375
    •   Log.ScopeContext"some log".ScopeContext 拓展 4.5.4 8129693
    •   HttpContext.SetTokensOfResponseHeaders 拓展 4.5.7 3775e65
    •   新增远程请求支持 Stream 文件格式上传 4.5.8 #I5UF3I
    •   日志模块可配置是否启用上下文功能 IncludeScopes 属性 4.5.9 #I5UJRS
    •   LoggingMonitor 日志筛选 WriteFilter 配置 4.5.9 6f06f12
  • 突破性变化

    •   😊 Furion 框架文档地址为 http://furion.baiqian.ltd 4.5.4 2e3d80e
    •   LoggingMonitor 底层逻辑,移除原来的 .ScopeContext 存储监听信息设计 4.5.2 #I5TWC1
    •   主机未启动时构建服务的操作权限,此操作会导致内存激增,受影响方法: App.GetOptions 系列和 App.GetServiceScoped.Create 4.5.4 #I5U0A4 8129693
查看变化

近期发现许多开发者在主机还未启动时解析服务,这是非常不正确的行为,会导致启动时内存激增甚至溢出,常见的错误有:

  • 在启动的时候通过 Scoped.Create 创建作用域
  • 在启动的时候通过 App.GetOptions 获取选项对象
  • 在启动的时候通过 App.GetService<T> 解析服务

正确的做法是,启动的时候禁止使用 Scoped.CreateApp.GetService<T>

如需启动时获取配置应该通过:App.GetConfig<TOptions>("配置节点", true) 替代 App.GetOptions<TOptions>()

  • 问题修复

    •   字符串日志拓展带泛型方法不能正确显示 CategoryName 日志类别 4.5.0 #I5TBKL
    •   控制台日志设置了 .ScopeContext 无效问题 4.5.2 7fb3036
    •   LoggingMonitor 同时配置了局部和全局日志监听触发两次问题 4.5.2 a1a97e8
    •   v4.4.8+ 版本更新导致远程请求在个别情况下出现并发问题 4.5.2 #I5TWL3
    •   LoggingMonitor 配置了 ReturnValueThreshold 之后 Json 被截断引发有效性检测异常 4.5.4 #I5U375
    •   LoggingMonitor 不支持 DataTableDataSetTuple 等类型问题 4.5.5 #I5U3VO
    •   v4.5.2+ 版本升级后出现启动时使用 App.GetOptons<TOptions> 异常问题 4.5.6 #I5U4OC f9a6587
    •   app.UseInject(action) 导致死循环 4.5.7 !608
    •   LoggingMonitor 报空引用异常问题 4.5.8 #I5UGCA !610
    •   并发情况下设置日志上下文出现偶然性空引用问题 4.5.9 #I5UJRS
  • 其他更改

    •   文件日志默认模板,默认对日志时间进行格式化并显示星期几 4.5.1 #I5TKL5
    •   脚手架代码,默认启用 services.AddConsoleFormatter() 4.5.1 #I5TLI6
    •   Serve.Run() 代码,默认启用 services.AddConsoleFormatter() 4.5.1 #I5TLI6
    •   减少 MiniProfile 不必要的监听,只在 Swagger 页面请求才监听 4.5.7 697ef51
    •   日志模块所有日志时间默认为 24小时制,过去是 12小时制 4.5.9 !612
  • 文档

    •   选项监听出现触发多次的解决方案 #I5T9PR
    •   日志记录文档、动态 WebAPI 文档、选项文档、HttpContext 文档、远程请求文档
  • 本期亮点

    1. 支持日志配置 JSON 格式化输出
查看变化
// 控制台
services.AddConsoleFormatter(options =>
{
options.MessageFormat = LoggerFormatter.Json;
});

// 文件
services.AddFileLogging("mytemplate.log", options =>
{
options.MessageFormat = LoggerFormatter.Json;
});

// 数据库
services.AddDatabaseLogging<DatabaseLoggingWriter>(options =>
{
options.MessageFormat = LoggerFormatter.Json;
});
    1. 支持 LoggingMonitor 输出 JSON 格式
查看变化
  1. 全局/局部启用 Json 输出配置
// 全局
services.AddMonitorLogging(options =>
{
options.JsonBehavior = Furion.Logging.JsonBehavior.OnlyJson;
});

// 局部
[LoggingMonitor(JsonBehavior = Furion.Logging.JsonBehavior.OnlyJson)]
关于 JsonBehavior

只有设置为 JsonBehavior.OnlyJson 时才不会输出美观的日志。

  1. 写入存储介质
using Furion.Logging;

namespace Your.Core;

public class DatabaseLoggingWriter : IDatabaseLoggingWriter
{
// 支持构造函数注入任何实例,会自动释放任何服务,比如注入 IRepository,或者 SqlSugarClient
public DatabaseLoggingWriter()
{
}

public void Write(LogMessage logMsg, bool flush)
{
// 如果 JsonBehavior 配置为 OnlyJson 或者 All,那么 Context 就包含 loggingMonitor 的值
// 如果 JsonBehavior 配置为 OnlyJson,那么可直接通过 logMsg.Message 获取结果就是 json 格式
if (logMsg.LogName == "System.Logging.LoggingMonitor")
{
var jsonString = logMsg.Context.Get("loggingMonitor");
}

// 这里写你任何插入数据库的操作,无需 try catch
}
}

Json 输出格式如下:

{
"controllerName": "test-logger",
"controllerTypeName": "TestLoggerServices",
"actionName": "person",
"actionTypeName": "GetPerson",
"areaName": null,
"displayName": "Furion.Application.TestLoggerServices.GetPerson (Furion.Application)",
"localIPv4": "0.0.0.1",
"remoteIPv4": "0.0.0.1",
"httpMethod": "GET",
"requestUrl": "https://localhost:5001/api/test-logger/person/2",
"refererUrl": "https://localhost:5001/api/index.html?urls.primaryName=数据库操作演示",
"environment": "Development",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.53",
"requestHeaderAuthorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjEsIkFjY291bnQiOiJhZG1pbiIsImlhdCI6MTY2NDQ1MDUwNSwibmJmIjoxNjY0NDUwNTA1LCJleHAiOjE2NjQ0NTE3MDUsImlzcyI6ImRvdG5ldGNoaW5hIiwiYXVkIjoicG93ZXJieSBGdXJpb24ifQ.-xocNcDQGoXClceoVU5QAHIkTcOZ7ZXo0hEbzghDfFI",
"timeOperationElapsedMilliseconds": 55,
"authorizationClaims": [
{
"type": "UserId",
"valueType": "integer",
"value": "1"
},
{
"type": "Account",
"valueType": "string",
"value": "admin"
},
{
"type": "iat",
"valueType": "integer",
"value": "1664450505"
},
{
"type": "nbf",
"valueType": "integer",
"value": "1664450505"
},
{
"type": "exp",
"valueType": "integer",
"value": "1664451705"
},
{
"type": "iss",
"valueType": "string",
"value": "dotnetchina"
},
{
"type": "aud",
"valueType": "string",
"value": "powerby Furion"
}
],
"parameters": [
{
"name": "id",
"type": "System.Int32",
"value": 2
}
],
"returnInformation": {
"type": "Furion.UnifyResult.RESTfulResult`1[[System.Object, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]",
"actType": "Furion.Application.Persons.PersonDto",
"value": {
"StatusCode": 200,
"Data": {
"Id": 2,
"Name": null,
"Age": 0,
"Address": null,
"PhoneNumber": null,
"QQ": null,
"CreatedTime": "0001-01-01T00:00:00+00:00",
"Childrens": null,
"Posts": null
},
"Succeeded": true,
"Errors": null,
"Extras": null,
"Timestamp": 1664450517341
}
},
"exception": {
"type": "System.DivideByZeroException",
"message": "Attempted to divide by zero.",
"stackTrace": " at Furion.Application.TestLoggerServices.测试日志监听8(Int32 id) in D:\\Workplaces\\OpenSources\\Furion\\samples\\Furion.Application\\TestLoggerServices.cs:line 78\r\n at lambda_method103(Closure , Object , Object[] )\r\n at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)\r\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)\r\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)"
},
"validation": {
"errorCode": null,
"originErrorCode": null,
"message": "出错了啊。。。。"
}
}
    1. 支持远程请求上传文件 Stream
查看变化
  1. 单文件上传
  • 字符串方式
var fileStream = new FileStream("image.png", FileMode.Open);

var result = await "https://localhost:44316/api/test-module/upload-file"
.SetContentType("multipart/form-data")
.SetFiles(HttpFile.Create("file", fileStream, "image.png")).PostAsync();

var fileName = await result.Content.ReadAsStringAsync();

await fileStream.DisposeAsync();
  • 代理方式
var fileStream = new FileStream("image.png", FileMode.Open);

var result = await _http.TestSingleFileProxyAsync(HttpFile.Create("file", fileStream, "image.png"));
var fileName = await result.Content.ReadAsStringAsync();

await fileStream.DisposeAsync();
  1. 多文件上传
  • 字符串方式
var fileStream = new FileStream("image.png", FileMode.Open);

var result = await "https://localhost:44316/api/test-module/upload-muliti-file"
.SetContentType("multipart/form-data")
.SetFiles(HttpFile.CreateMultiple("files", (fileStream, "image1.png"), (fileStream, "image2.png"))).PostAsync();
var fileName = await result.Content.ReadAsStringAsync();

await fileStream.DisposeAsync();
  • 代理方式
var fileStream = new FileStream("image.png", FileMode.Open);

var result = await _http.TestMultiFileProxyAsync(HttpFile.CreateMultiple("files", (fileStream, "image1.png"), (fileStream, "image2.png")));
var fileName = await result.Content.ReadAsStringAsync();

await fileStream.DisposeAsync();
  1. 还支持 BytesStream 混合
var fileStream = new FileStream("image.png", FileMode.Open);
var bytes = File.ReadAllBytes("image.png");

var httpFile = new HttpFile
{
Name = name,
Bytes = bytes,
FileStream = fileStream,
FileName = fileName
};

var result = await "https://localhost:44316/api/test-module/upload-file"
.SetContentType("multipart/form-data")
.SetFiles(httpFile).PostAsync();

var fileName = await result.Content.ReadAsStringAsync();

await fileStream.DisposeAsync();
    1. LoggingMonitor 全局过滤
查看变化
services.AddMonitorLogging(options =>
{
options.WriteFilter = (context) =>
{
// 获取控制器/操作描述器
var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;

// 你的逻辑....,不需要拦截返回 false,否则 true

return true;
};
});

v4.4.9(已发布)

版本细节
  • 新特性

    •   友好异常可控制是否输出错误日志配置 LogError: true 4.4.0 #I5PKJH
    •   DateOnlyJsonConverterDateOnlyOffsetJsonConverter 序列化转换器 !565
    •   事件总线 LogEnabled 配置,可控制是否输出服务日志 #I5QLY5
    •   可实现任何多套规范化结果功能,支持特定控制器,特定方法 #I5QZ37
    •   ILoggerFactory 日志工厂动态批量添加文件日志拓展 #I5R9PO
    •   App.GetCommandLineConfiguration(args) 解析命令行参数静态方法 803542c
    •   Sql 代理支持返回受影响行数 #I5REJ9
    •   任意自定义日志文件名支持滚动日志删除功能 #I5RFBQ
    •   .pcd 图片类型 MIMEimage/x-photo-cd 支持 5fafc84
    •   默认日志输出当前线程 Environment.CurrentManagedThreadId b8fe2cd
    •   app.UseInject(Action<UseInjectOptions>) 重载方法,简化配置 4.4.8 0b645fe
  • 突破性变化

    •   框架适配 .NET 6.0.9.NET 7.0 RC1 be5b40 1eee77b
    •   远程请求 .SetBodyBytes.SetFiles #I5PMS5 #I5PIYI
    •   FS.InitialContentTypeProvider() 名称为 FS.GetFileExtensionContentTypeProvider() 5fafc84
    •   远程请求 [BodyBytes] 设计,采用 HttpFile 方式 #I5PMS5 #I5PIYI
查看变化
public interface IHttp : IHttpDispatchProxy
{
[Post("http://furion.baiqian.ltd/upload", ContentType = "multipart/form-data")]
Task<HttpResponseMessage> PostXXXAsync(HttpFile file);

// 支持多个文件
[Post("http://furion.baiqian.ltd/upload", ContentType = "multipart/form-data")]
Task<HttpResponseMessage> PostXXXAsync(HttpFile[] files);

// 支持多个文件
[Post("http://furion.baiqian.ltd/upload", ContentType = "multipart/form-data")]
Task<HttpResponseMessage> PostXXXAsync(IList<HttpFile> files);
}
// bytes 可以通过 File.ReadAllBytes(文件路径) 获取
var res = await "http://furion.baiqian.ltd/upload".SetContentType("multipart/form-data")
.SetFiles(HttpFile.Create("file", bytes, "image.png")).PostAsync();

// 支持多个文件
var res = await "http://furion.baiqian.ltd/upload".SetContentType("multipart/form-data")
.SetFiles(HttpFile.CreateMultiple("files", (bytes, "image1.png"), (bytes, "image2.png"))).PostAsync();
    •   所有的 AddInjectUseInject 参数设计 #I5QCF0
查看变化
public void ConfigureServices(IServiceCollection services)
{
services.AddInject(options =>
{
options.ConfigureSwaggerGen(gen =>
{
// ...
});
});
}
 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseInject(configure: options =>
{
options.ConfigureSwagger(swg =>
{
// ...
});

options.ConfigureSwaggerUI(ui =>
{
// ...
});
});
}
    •   远程请求所有 xxxAsStreamAsync 返回值 #I5QVEB
查看变化

由:

var stream = await "http://furion.baiqian.ltd/".GetAsStreamAsync();

改为:

var (stream, encoding) = await "http://furion.baiqian.ltd/".GetAsStreamAsync();
    •   .Inject() 支持配置更多参数,开放底层更多权限 4.4.9 1182283
查看变化
.Inject((builder, options) => {
options.ConfigureAppConfiguration((context, config) =>
{

});
options.ConfigureServices((context, services) =>
{

});
});
  • 问题修复

    •   远程请求代理模式非泛型参数导致数组溢出问题 #I5Q3SN
    •   LoggingMonitor 客户端 IP 记录错误 #I5QCU1 !562
    •   远程请求响应报文中包含 charset=gbk 进行序列化后乱码问题 #I5QVEB
    •   文件日志断电时丢失日志问题 db7d51b
    •   动态 WebAPI 或控制台贴了 [ApiDescriptionSettings(Tag = "")] 标签之后导致注释丢失 #I5REVF #I5RE4J
    •   启用数据库日志但是没有配置配置文件出现空异常问题 33817be
    •   控制台日志过滤无法过滤默认主机日志问题 33817be
    •   脚手架错误的日志配置问题 33817be
    •   高频压测情况下写日志并设置日志上下文导致并发更新出现 System.AggregateException 异常问题 #I5RFBQ
    •   日志文件名因 WindowsLinux 路径分隔符不一致导致日志文件创建失败问题,Linux 只支持 / 不支持 \ #I5RFBQ
    •   Oops.Oh/Bah 设置 .WithData 之后无效问题 !580
    •   基于 Redis 重写事件存储器序列化 IEventSource 实例异常问题 4.4.7 3e45020
    •   使用 Log 静态类超高频率下写日志导致 CPU 激增问题 4.4.7 #I5SDK5
    •   远程请求超高频率下发送请求导致 CPU 激增问题和异常问题 4.4.8 #I5SJJR
    •   集成第三方配置中心时获取的不是最新数据问题 4.4.9 2cdef6b
  • 其他更改

    •   JWTEncryption 静态类,支持无需注册 services.AddJwt() 使用 #I5PPKE #I5POLZ
    •   事件总线默认日志类名为 System.Logging.EventBusService #I5QLY5
  • 文档

    •   .NET6 升级 .NET7 文档
    •   ASP.NET 7 集成文档
    •   集成第三方配置中心文档 4.4.9
    •   第三方事件总线和 Furion 集成文档 4.4.9
    •   事件总线集成 Kafka 文档 #I5P5UG
    •   友好异常文档、日志记录文档、远程请求文档、依赖注入文档、即时通讯文档、事件总线文档、Worker Service 文档、单元测试文档、入门指南文档、数据库新增文档

v4.3.9(已发布)

版本细节
  • 新特性

    •   AppSettings 配置的 ExcludeAssemblies 属性,支持忽略指定程序集扫描 7b7747f
    •   Oops.OhOops.Bah 支持设置额外数据 .WithData(data) #I5O38E
    •   定时任务 Crontab.GetSleepMilliseconds(baseTime) 获取下一个发生时间的时间差 d024fae
    •   友好异常默认打印异常日志,避免生产环境漏掉重要异常信息 6e3a5bd
    •   日志静态类 Log.CreateLoggerFactory() 静态方法 75c672a
    •   多语言 SharedResource 模式,避免硬编程 18e80c7
    •   事件总线 MessageCenter 静态类,解决从 Fur v1.x 版本升级问题 a29fc7c
    •   组件化 IWebComponent 模式,支持 .NET5+ 08a44c3
    •   远程请求设置自己的 HttpClient 功能 #I5PBR3 !545
    •   LoggingMonitor 支持添加更多自定义配置 #I5PEPA
    •