1.6 更新日志
Furion
框架升级/发版规则升级前重点关注可能造成【破坏性】的标签类型:、、、
版本号规则:主版本号.次版本号.修订版本号
- 只要【确认】为框架
bug
,则当天修复,当天发版,修订版本号加 1
。 - 只要
.NET SDK
版本更新,则当天升级,当天发版,修订版本号加 1
。 - 如果
.csproj
文件有变更,则当天发版,修订版本号加 1
。 - 如果新增
拓展包
,为了版本号统一,则当天发版,修订版本号加 1
。 - 如果涉及到代码重构,则当天发版,次版本号
加 1
,修订版本号清 0
。 - 如果
.NET SDK
主版本号升级,则当天发版,主版本号加 1
。
如有意外不能当天发版,则会在 Issue
中说明具体发版时间,正常不会超过 3
天。
v4.5.2(当前版本)
v4.5.2
版本细节:https://gitee.com/dotnetchina/Furion/issues/I5TXON 2022.09.30v4.5.1
版本细节:https://gitee.com/dotnetchina/Furion/issues/I5TLI6 2022.09.28v4.5.0
版本细节:https://gitee.com/dotnetchina/Furion/issues/I5TD4X 2022.09.27
新特性
-
.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
结构类LogDateTime
,ThreadId
,State
属性 4.5.2 #I5TWC1 -
LoggingMonitor
可配置JsonWriterOptions
属性 4.5.3 #I5U375
-
突破性变化
-
LoggingMonitor
底层逻辑,移除原来的.ScopeContext
存储监听信息设计 4.5.2 #I5TWC1
-
问题修复
其他更改
文档
- 选项监听出现触发多次的解决方案 #I5T9PR
- 日志记录文档、动态 WebAPI 文档
本期亮点
- 支持日志配置
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;});
- 支持
LoggingMonitor
输出JSON
格式
- 支持
查看更改
- 全局/局部启用
Json
输出配置
// 全局services.AddMonitorLogging(options =>{ options.JsonBehavior = Furion.Logging.JsonBehavior.OnlyJson;});// 局部[LoggingMonitor(JsonBehavior = Furion.Logging.JsonBehavior.OnlyJson)]
JsonBehavior
只有设置为 JsonBehavior.OnlyJson
时才不会输出美观的日志。
- 写入存储介质
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": "出错了啊。。。。" }}
v4.4.9(已发布)
v4.4.9
版本细节:https://gitee.com/dotnetchina/Furion/issues/I5SP37v4.4.8
版本细节:https://gitee.com/dotnetchina/Furion/issues/I5SKUEv4.4.7
版本细节:https://gitee.com/dotnetchina/Furion/issues/I5SEFEv4.4.6
版本细节:https://gitee.com/dotnetchina/Furion/issues/I5RSFDv4.4.5
版本细节:https://gitee.com/dotnetchina/Furion/issues/I5RHQXv4.4.4
版本细节:https://gitee.com/dotnetchina/Furion/issues/I5R5TIv4.4.3
版本细节:https://gitee.com/dotnetchina/Furion/issues/I5QVH3v4.4.2
版本细节:https://gitee.com/dotnetchina/Furion/issues/I5QDHXv4.4.1
版本细节:https://gitee.com/dotnetchina/Furion/issues/I5Q3SXv4.4.0
版本细节:https://gitee.com/dotnetchina/Furion/issues/I5PQHR
新特性
- 友好异常可控制是否输出错误日志配置
LogError: true
4.4.0 #I5PKJH -
DateOnlyJsonConverter
和DateOnlyOffsetJsonConverter
序列化转换器 !565 - 事件总线
LogEnabled
配置,可控制是否输出服务日志 #I5QLY5 - 可实现任何多套规范化结果功能,支持特定控制器,特定方法 #I5QZ37
-
ILoggerFactory
日志工厂动态批量添加文件日志拓展 #I5R9PO -
App.GetCommandLineConfiguration(args)
解析命令行参数静态方法 803542c -
Sql
代理支持返回受影响行数 #I5REJ9 - 任意自定义日志文件名支持滚动日志删除功能 #I5RFBQ
-
.pcd
图片类型MIME
为image/x-photo-cd
支持 5fafc84 - 默认日志输出当前线程
Environment.CurrentManagedThreadId
b8fe2cd -
app.UseInject(Action<UseInjectOptions>)
重载方法,简化配置 4.4.8 0b645fe
- 友好异常可控制是否输出错误日志配置
突破性变化
查看更改
public interface IHttp : IHttpDispatchProxy{ [Post("https://www.furion.icu/upload", ContentType = "multipart/form-data")] Task<HttpResponseMessage> PostXXXAsync(HttpFile file); // 支持多个文件 [Post("https://www.furion.icu/upload", ContentType = "multipart/form-data")] Task<HttpResponseMessage> PostXXXAsync(HttpFile[] files); // 支持多个文件 [Post("https://www.furion.icu/upload", ContentType = "multipart/form-data")] Task<HttpResponseMessage> PostXXXAsync(IList<HttpFile> files);}
// bytes 可以通过 File.ReadAllBytes(文件路径) 获取var res = await "https://www.furion.icu/upload".SetContentType("multipart/form-data") .SetFiles(HttpFile.Create("file", bytes, "image.png")).PostAsync();// 支持多个文件var res = await "https://www.furion.icu/upload".SetContentType("multipart/form-data") .SetFiles(HttpFile.CreateMultiple("files", (bytes, "image1.png"), (bytes, "image2.png"))).PostAsync();
- 所有的
AddInject
和UseInject
参数设计 #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 "https://www.furion.icu/".GetAsStreamAsync();
改为:
var (stream, encoding) = await "https://www.furion.icu/".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 - 日志文件名因
Windows
和Linux
路径分隔符不一致导致日志文件创建失败问题,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
其他更改
文档
-
.NET6
升级.NET7
文档 -
ASP.NET 7
集成文档 - 集成第三方配置中心文档 4.4.9
- 第三方事件总线和
Furion
集成文档 4.4.9 - 事件总线集成
Kafka
文档 #I5P5UG - 友好异常文档、日志记录文档、远程请求文档、依赖注入文档、即时通讯文档、事件总线文档、Worker Service 文档、单元测试文档、入门指南文档、数据库新增文档
-
v4.3.9(已发布)
新特性
-
AppSettings
配置的ExcludeAssemblies
属性,支持忽略指定程序集扫描 7b7747f -
Oops.Oh
和Oops.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 -
LoggingMonitor
可配置WithReturnValue
和ReturnValueThreshold
#I5PFJ1 #I5PFOW -
LoggingMonitor
可配置MethodsSettings
更多信息 #I5PFJ1 #I5PFOW
-
查看更改
Serve.Run(RunOptions.Default .AddWebComponent<XXXWebComponent>());public class XXXWebComponent : IWebComponent{ public void Load(WebApplicationBuilder builder, ComponentContext componentContext) { // .... }}
突破性变化
查看更改
[Interceptor(InterceptorTypes.Request)]static void OnRequest(HttpClient client, HttpRequestMessage req){}[Interceptor(InterceptorTypes.Response)]static void OnResponsing(HttpClien client, HttpResponseMessage res){}[Interceptor(InterceptorTypes.Exception)]static void OnException(HttpClient client, HttpResponseMessage res, string errors){}
问题修复
其他更改
文档
-
RabbitMQ
事件总线文档 -
AppSettings
配置文档、事件总线文档、多数据库配置文档、日志文档、定时任务文档、MessageCenter
文档、远程请求文档、组件化文档、入门指南、多语言文档。
-
v4.2.13(已发布)
新特性
- 事件总线工厂,支持运行时动态添加订阅程序和移除订阅程序 #I5NNQX
- 事件总线
[EventSubscribe]
事件Id
支持正则表达式匹配 #I5NNQX - 事件总线
[EventSubscribe]
支持局部失败重试配置 #I5NNQX -
Log
全局静态类,方便随时随地记录日志 ba9b1f1 - 事件总线
options.AddSubscriber(Type)
重载 42446078 -
ValidationMetadata
类型FirstErrorProperty
和FirstErrorMessage
属性 #I5MFJT -
Serve.Run()
模式WithArgs(args)
方法 #I5MOJB -
[UnitOfWork]
分布式事务TransactionScope
支持 #I5MRTY - 16 位
MD5
加密支持 #I5N8RC
突破性变化
-
Scoped.Create(async (f,s) => {})
异步创建作用域方法名称为CreateAsync
,避免一些情况下无法区分,同步方法不变 #I5N9XY
-
查看更改
由:
// Scoped.CreateUow 一样await Scoped.Create(async (f, s) => {});
改为:
// Scoped.CreateUowAsync 一样await Scoped.CreateAsync(async (f, s) => {});
-
.NET 6.0.8
和.NET 7 Preview 7
842d4f7
-
-
[LoggingMonitor]
命名空间为System
,因为使用频率越来越高 b879861
-
- 在非
Web
环境中不正确使用字符串拓展方法检测机制 6389cbd
- 在非
- 所有
.Default
静态属性为.Default()
方法 6389cbd
- 所有
- 工作单元
IUnitOfWork
所有方法参数类型,由ActionExecutingContext
和ActionExecutedContext
改为FilterContext
#I5MHX5
- 工作单元
查看更改
public interface IUnitOfWork{ void BeginTransaction(FilterContext context, UnitOfWorkAttribute unitOfWork); void CommitTransaction(FilterContext resultContext, UnitOfWorkAttribute unitOfWork); void RollbackTransaction(FilterContext resultContext, UnitOfWorkAttribute unitOfWork); void OnCompleted(FilterContext context, FilterContext resultContext);}
问题修复
- 日志上下文数据多次写入被清空问题以及数据库日志出现异常后停止写入 #I5LIWF
- 个别情况下跨域默认配置的响应缓存导致嵌入式资源异常问题 7a57efe
- 远程请求传入不合法的请求报文头数据触发校验失败问题 #I5LPFE
- 多线程中使用静态日志写数据库日志导致连接池耗光问题 8d5cdd6
-
EFCore 6.0
之后IModelCacheKeyFactory
接口方法改变导致分表分库异常问题 #I5MCZ6 EFCore#25154 EFCore!3305 -
ValidationMetadata
对象Message
字符串类型出现\"\"
问题 #I5MFJT -
[IfException]
覆盖Oops.Oh/Bah
错误消息问题 4bbd854 - 数据库日志写入循环写入和频繁创建数据库连接池问题 9ce214c
-
Razor Pages
不支持全局异常拦截问题 #I5MHX5 -
Razor Pages
不支持全局数据验证问题 #I5MHX5 -
Razor Pages
不支持工作单元[UnitOfWork]
问题 #I5MHX5 -
Razor Pages
不支持EFCore
自动SaveChanges
问题 #I5MHX5 -
Blazor Server
因v4.2.2
版本更新导致的问题 #I5MNFN -
[IfException]
不支持多语言配置问题 #I5MPN7 - 通过
services.AddMvcFilter<LoggingMonitorAttribute>()
方式注册无效问题 8d1477d - 事件总线默认
Channel
管道初始化时机过晚问题,解决部分第三方依赖使用问题 #I5MM3O - 主机停止时写入日志异常问题 #I5N7S2
- 数据库上下文手动释放导致
AutoSaveChange
特性出现释放异常问题 #I5NFWC -
[LoggingMonitor]
循环引用序列化问题 #I5NRT9 - 远程请求传入
null
Body 参数抛出空异常问题 #I5NTUE - 事件总线默认开启模糊匹配(正则表达式)导致不必要的订阅 #I5NVOP
其他更改
文档
- 全局日志静态类
Log
文档 ba9b1f1 -
Nuget
本地测试包文档 - 日志文档、静态类文档、数据校验文档、Worker Service 文档、工作单元文档、依赖注入文档
- 全局日志静态类
v4.1.14(已发布)
新特性
-
Furion.Xunit
拓展包,正式实现Xunit
单元测试完整支持Furion
063a034e -
services.AddMonitorLogging()
日志监视器服务,支持非常灵活的日志操作 81df742 -
Serve.Run(silence: true)
等一系列强大的静默启动功能 #I5JBSQ #I5J98T 7cced4 -
SpecificationDocumentBuilder.GetOpenApiGroups()
方法获取底层的规范化接口分组信息 4ff03c5 -
logger.ScopeContext()
配置日志上下文功能 #I5JC0D - 跨域配置
CorsAccessorSettings.SignalRSupport
配置选项,支持配置SignalR
跨域 #I5JREM - 事件总线
UseUtcTimestamp
选项配置,可选择使用DateTime.UtcNow
还是DateTime.Now
,默认是DateTime.Now
#I5JSEU - 规范化文档
[OperationId]
配置,解决自定义Swagger UI
不能正确显示路由问题 #I5K1IB - 远程请求
IHttpDispatchProxy
方式全局拦截支持多态(继承) #I5K8FS
-
突破性变化
-
Furion.Xunit
拓展包,正式实现Xunit
单元测试完整支持Furion
063a034e -
Furion.Extras.DatabaseAccessor.SqlSugar
拓展插件中的[SqlSugarUnitOfWork]
工作单元特性,将使用通用工作单元替换,查看最新实现文档 -
Inject.Create()
方法,再也不需要了,框架提供了无敌强大的Serve.Run()
静默启动方式 200848e -
Serve.Run
的ConfigureConfiguration
方法参数,由configuration => {}
改为(environment, configuration) => {}
83c97bb
-
查看更改
// 由Serve.Run(RunOptions.Default.ConfigureConfiguration(configuration => {}));// 改为:Serve.Run(RunOptions.Default.ConfigureConfiguration((environment, configuration) => {}));
问题修复
-
[LoggingMonitor]
异常消息日志级别为Information
错误问题 ab46cdf - 新版本日志组件频繁提示文件占用问题,将文件独占锁改为共享锁 #I5J3S6
- 配置数据库日志读写器为
EFCore
时控制台出现无限打印问题 #I5J474 -
[LoggingMonitor]
针对byte[]
类型参数输出过大问题 5380f35 - 友好异常和规范化结果丢失了原始
ErrorCode
问题 #I5IX2R - 新版本日志组件自定义数据库读写器注入
IRepository
仓储导致死循环问题 #I5IX2R -
Mvc
默认手动验证和Furion
全局验证冲突问题 2a06c39 -
Serve.Run()
模式不支持SuperSocket
第三方包问题,原生是支持的。186ca0a -
SignalR
跨域错误问题 #I5JREM -
[LoggingMonitor]
将Oops.Oh
和Oops.Bah
记录到了错误日志中,默认应该是Information
且提供可配置 #I5JZ1H - 自定义
Swagger UI
之后个别UI
要求必须配置operationId
,否则出现guid
序号 #I5K1IB - 主动抛出
NotFoundResult
和NotFoundObjectResult
无效问题 #I5KALZ -
[LoggingMonitor]
解析方法参数但前端未传入时出现错误问题 #I5KC5P -
[LoggingMonitor]
无法序列化IQueryable
返回值问题 #I5KJD1 -
[LoggingMonitor]
不能记录全局验证错误问题 b44087d -
[LoggingMonitor]
存在注册顺序差异问题 b44087d
-
其他更改
- 底层的规范化文档
SpecificationDocumentBuilder
部分方法,提供更加便捷的第三方Swagger UI
集成 10f0f01
- 底层的规范化文档
文档
- 单元测试文档、入门指南文档、Worker Services 文档
本期亮点
Serve.Run()
彻彻底底支持全平台,提供非常强大的静默模式
启用静默模式可以实现无阻塞方式执行程序,而且还能体验完整的 Furion
功能。
查看更改
有了 Serve.Run()
静默模式后,Furion
彻彻底底支持全平台,不管你是 控制台、Web、桌面、移动、单元测试,集成测试,基准测试等等应用程序
。
Serve.Run(silence: true);// 不会阻塞执行哦,而且从这里开始可以使用 Furion 任何功能,比如 App.Configuration....Console.WriteLine("Hello, World!");Console.ReadKey();
还有更多静默模式。
// RunOptions 方式Serve.Run(RunOptions.DefaultSilence);// LegacyRunOptions 方式Serve.Run(LegacyRunOptions.DefaultSilence);// GenericRunOptions 方式Serve.Run(GenericRunOptions.DefaultSilence);
- 强大的
Furion.Xunit
单元测试、集成测试
查看更改
单元测试和集成测试是保证一个系统能够持续维护和稳定运行的必备技能,但是目前现有的单元测试组件无法直接集成 Furion
的功能,最常用的就是如何在单元测试中读取配置,以及如何进行依赖注入。
在过去,Furion
只能不断的去调整,以至于适配第三方单元测试写法,搞得不伦不类!
所以,这一次不再妥协,Furion
推出自己的单元测试工具,可以让现有的单元测试如 Xunit
100% 支持 Furion
所有功能,全部保证一致的写法。
单元测试中初始化 Furion
using Furion.Xunit;using Xunit.Abstractions;using Xunit.Sdk;// 配置启动类类型,第一个参数是 TestProgram 类完整限定名,第二个参数是当前项目程序集名称[assembly: TestFramework("TestProject1.TestProgram", "TestProject1")]namespace TestProject1;/// <summary>/// 单元测试启动类/// </summary>public class TestProgram : TestStartup{ public TestProgram(IMessageSink messageSink) : base(messageSink) { // 初始化 Furion Serve.Run(silence: true); }}
测试类支持完整依赖注入
using TestProject1.Services;using Xunit;namespace TestProject1;public class UnitTest1{ private readonly ICalcService _calcService; public UnitTest1(ICalcService calcService) { _calcService = calcService; } [Fact] public void 测试两个数的和() { Assert.Equal(3, _calcService.Plus(1, 2)); }}

- 开放底层规范化文档分组接口,使得集成第三方
Swagger UI
更加容易,如集成IGeekFan.AspNetCore.Knife4jUI
拓展:
查看更改
只需要在 YourPoject.Web.Core
层安装 IGeekFan.AspNetCore.Knife4jUI
即可。
3.1.1 Knife4jUI
独立版本配置
var routePrefix = "api"; // 定义 swagger 路由地址,如果是跟目录,设置 string.Empty 即可app.UseKnife4UI(options =>{ options.RoutePrefix = routePrefix; // 配置 Knife4UI 路由地址 foreach (var groupInfo in SpecificationDocumentBuilder.GetOpenApiGroups()) { options.SwaggerEndpoint("/" + groupInfo.RouteTemplate, groupInfo.Title); }});app.UseInject(routePrefix); // 配置 Furion 路由地址
3.1.2 Knife4jUI
和 Swagger
共存版本配置
app.UseKnife4UI(options =>{ options.RoutePrefix = "newapi"; // 配置 Knife4UI 路由地址,现在是 /newapi foreach (var groupInfo in SpecificationDocumentBuilder.GetOpenApiGroups()) { options.SwaggerEndpoint("/" + groupInfo.RouteTemplate, groupInfo.Title); }});app.UseInject(); // Furion 默认 api 地址为 /api

如需实现登录之后自动将 token
添加到头部可在登录接口 AfterScript
执行以下代码:
ke.global.setAllHeader(
"Authorization",
"Bearer " + ke.response.headers["access-token"]
);

- 提供强大的日志上下文功能
// 写法一_logger.ScopeContext(ctx => ctx.Set("Name", "Furion").Set("UserId", 10)) .LogInformation("我是一个日志 {id}", 20);// 写法二_logger.ScopeContext(new Dictionary<object, object> { { "Name", "Furion" }, { "UserId", 10 }}).LogInformation("我是一个日志 {id}", 20);// 写法三_logger.ScopeContext(new LogContext { // ....}).LogInformation("我是一个日志 {id}", 20)
v4.0.0(重新起航)💖
2020 年 09 月 01 日,一个叫 Fur
的开源项目在 Gitee
的襁褓中悄然诞生,她的出生仿佛带着某种使命,没有包袱,无限可能。
她缓缓的张开双眼,干净雪亮的眼睛似乎对这个世界充满了好奇,任何事物在她眼前晃过都像是直击灵魂的思想碰撞,这些在她看来都是非常宝贵的财富。她貌似有用不完的精力,一路汲取知识,升级打怪,不断奔跑,乐此不疲。
记得 2020 年 11 月 11 日的单身节,她迎来了“一岁(v1.0.0)”生日,自那以后,IT 这个大银幕上频繁出现她的身影,越来越多 .NET5
开发者转粉,像是告诉这个世界,她就是 IT 界大明星。
每一个明星都有一个好听的艺名,她当然也不例外,2020 年 11 月 20 日,经纪人百小僧为她起名为 Furion
。
2021 年 11 月 09 日起,她进入了每个孩子都经历过的叛逆期,年少轻狂喜新厌旧,抛弃了曾经支持她的 .NET5
粉丝们,投入到新的 .NET6
拥趸者怀抱中,自此过上了奢靡富足的生活。
但她过的不开心,时常在夜里想起 .NET5
的粉丝们,内心非常自责,但在双重工作压力下她毅然选择了忽视他们的诉求,仿佛他们就是累赘。
时间真的是好东西,曾经认为是对的,经过岁月的蹉跎历磨,渐渐的明白:不忘初心,方能始终。
这一次,不落下一人(.NET5
,.NET6
,...,.NET N
),携手共进,重新起航,感恩遇见,感恩信任。
新特性
v4.0.0
支持.NET5
,.NET6
,...,.NET N
,所有的Furion
项目都能够升级到该版本,重新起航,实现大统。
v3.9.2(已发布)
日志模块是任何应用系统都必备的功能,可以说是最重要的模块!在 .NET
社区中有 Log4NET
,NLog
,Serilog
等日志组件,它们无一不是优秀的开源项目。
但由于这些日志组件历史悠久,内部兼容的 .NET
版本非常多,功能随着时间推移变得极其强大复杂,在实际项目使用中,发现每一个日志组件配置总是不那么友好,特别是在使用上不够简单。
Furion
作为全栈开发框架,在过去版本并没有提供足以满足开发者需求的日志模块,转而推荐大家集成第三方组件,如 Serilog
,导致后续无法实现自定义功能和也增加了不少维护成本。
这一次,Furion
不再妥协,彻底重构了日志模块,实现日志功能/需求完全自主可控,提供给开发者几乎所有日志功能的需求!
Furion
的使用者们,是时候“更换”掉第三方日志组件,让我们一起迭代出更强更好的日志组件吧! 🍖
新特性
突破性变化
问题修复
- 修复脱敏模块模型绑定个别情况下空异常问题 #I5IM5C
文档
- 日志文档、静态类文档、数据校验文档
本期亮点
- 极易使用且强大的日志模块
查看更改
// 写入文件services.AddFileLogging("logs/application.log");// 写入数据库services.AddDatabaseLogging<DatabaseLoggingWriter>();
- 强大的
[LoggingMonitor]
调试日志
查看更改
using Furion.Logging;namespace Furion.Application;public class TestLoggerServices : IDynamicApiController{ [LoggingMonitor] public PersonDto GetPerson(int id) { return new PersonDto { Id = id }; }}
支持控制器、操作或全局注册拦截。
输出日志为:
┏━━━━━━━━━━━ Logging Monitor ━━━━━━━━━━━┣ Furion.Application.TestLoggerServices.GetPerson (Furion.Application)┣┣ 控制器名称: TestLoggerServices┣ 操作名称: GetPerson┣ 路由信息: [area]: ; [controller]: test-logger; [action]: person┣ 请求地址: https://localhost:44316/api/test-logger/person/11┣ 来源地址: https://localhost:44316/api/index.html┣ 浏览器标识: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36 Edg/103.0.1264.62┣ 客户端 IP 地址: 0.0.0.1┣ 服务端 IP 地址: 0.0.0.1┣ 服务端运行环境: Development┣ 执行耗时: 31ms┣ ━━━━━━━━━━━━━━━ 授权信息 ━━━━━━━━━━━━━━━┣ JWT Token: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjEsIkFjY291bnQiOiJhZG1pbiIsImlhdCI6MTY1ODcxNjc5NywibmJmIjoxNjU4NzE2Nzk3LCJleHAiOjE2NTg3MTc5OTcsImlzcyI6ImRvdG5ldGNoaW5hIiwiYXVkIjoicG93ZXJieSBGdXJpb24ifQ.VYZkwwqCwlUy3aJjuL-og62I0rkxNQ96kSjEm3VgXtg┣┣ UserId (integer): 1┣ Account (string): admin┣ iat (integer): 1658716797┣ nbf (integer): 1658716797┣ exp (integer): 1658717997┣ iss (string): dotnetchina┣ aud (string): powerby Furion┣ ━━━━━━━━━━━━━━━ 参数列表 ━━━━━━━━━━━━━━━┣ Content-Type:┣┣ id (Int32): 11┣ ━━━━━━━━━━━━━━━ 返回信息 ━━━━━━━━━━━━━━━┣ 类型: Furion.Application.Persons.PersonDto┣ 返回值: {"Id":11,"Name":null,"Age":0,"Address":null,"PhoneNumber":null,"QQ":null,"CreatedTime":"0001-01-01T00:00:00+00:00","Childrens":null,"Posts":null}┗━━━━━━━━━━━ Logging Monitor ━━━━━━━━━━━
v3.8.9(已发布)
新特性
- 规范化结果
ExceptionMetadata
和ValidationMetadata
都可以获取ErrorCode
属性 #I5GJ6D -
ValidationMetadata
类StatusCode
属性 #I5HB5L - 远程请求对
Url
是否编码设置,[Get(WithEncodeUrl = false)]
和WithEncodeUrl(false)
#I5GOBC - 更强大的
JWTEncryption.SecurityReadJwtToken('token')
读取解析Token
静态方法 574eeb6 -
ValiationTypes.Html
验证Html
标签 #I5HBKC - EFCore.NamingConventions 支持,可自定义生成表名,字段名风格,比如小驼峰,蛇形命名等 #I5HBEI
-
INamedServiceProvider
命名服务提供器,可解析接口多实现 #I5HF98 - 脱敏处理模块方法参数单个值处理 a22ec3c
- 脱敏词库支持
|
分割词语 3106b1d
- 规范化结果
突破性变化
问题修复
-
Rider
开发工具对同名脚手架 (EFCore
和SqlSugar
) 只显示一个问题 !518 -
UnitOfWork
工作单元在EFCore
中失效问题 #I5H0T3 -
JWT
中Token
如果存在数组类型的值时,刷新Token
后丢失了历史值 #I5GXML - 远程请求
WithEncodeUrl
无法在[HttpMethod]
设置问题 574eeb6 -
Serve.Run()
模式下添加自定义配置导致EFCore
无法获取自定义配置文件问题 #I5GZ0F -
Oops.Bah
进入全局异常拦截器问题 #I5H47S -
AddDbPool/AddDb
扩展未根据配置Key
路径读取问题 #I5H6S4 !520 -
ValiationTypes.Url
正则表达式覆盖不全问题 #I5HBKC -
v3.5.x
版本导致集成 EFCore.NamingConventions 失效问题 #I5HBEI -
Swagger
长路由不支持问题以及[Required]
配置AllowEmptyStrings
无效问题 c014330 - 远程请求上传文件时请求报文
boundary
和Content-Disposition
设置不正确问题 #I5HEF0 - 脱敏模块替换敏感词汇出现多替换问题 a22ec3c
-
其他更改
文档
- 远程请求文档,日志记录文档、多数据库文档、PM2 部署文档、Visual Studio 高效率文档
v3.7.11(已发布)
新特性
-
Minimal API
应用支持:.AddInjectMini()
#I4KOQ5 - 跨域
WithExposedHeaders
默认配置access-token
和x-access-token
42ebdfd - 脚手架默认启用
app.UseHttpLogging()
HTTP
日志 42ebdfd -
Furion
和ASP.NET Core
完整json
配置的JSON Schema
架构 JSON Schema -
Sql
代理支持返回单个类类型参数 1d7fb5b -
Sql
代理支持返回ValueTuple
单个类类型参数 876a2f5 - 组件化设计模块,支持比
AppStartup
更灵活便捷的设计 #components - 独立工作单元模块,支持任何第三方
ORM
a02413d - 跨域
FixedClientToken
配置参数 bd01638 -
throw Oops.Bah
可以手动触发规范化验证失败处理 83f0036 -
FriendlyExceptionSettings
的ThrowBah
配置,可标记Oops.Oh
不进入异常处理 76ffa7f
-
突破性变化
-
Minimal API
应用支持:.AddInjectMini()
#I4KOQ5 -
Furion
和ASP.NET Core
完整json
配置的JSON Schema
架构 JSON Schema - 组件化设计模块,支持比
AppStartup
更灵活便捷的设计 #components - 独立工作单元单元模块,支持任何第三方
ORM
a02413d -
DataValidationFilter
和FriendlyExceptionFilter
,解决不支持手动抛出业务异常问题 83f0036 -
.AddDb<>
和.AddDbPool<>
自定义委托参数签名,由Action<DbContextOptionsBuilder>
改为:Action<IServiceProvider, DbContextOptionsBuilder>
-
查看更改
// 由:options.AddDbPool<TDbContext>(DbProvider.MySql, opt => {});// 改为options.AddDbPool<TDbContext>(DbProvider.MySql, (services, opt)=> {})
问题修复
- 自
v3.6.3
版本依赖,执行原生Sql
添加了参数校验导致存储过程执行错误问题 #I5ERMQ -
tools/cli.ps1
脚本工具出现数据库链接被占用问题 -
JWTSettings
算法配置JSON Schema
错误问题,感谢 @gitwentao #I5G27B !516 - 基于策略授权在不配置
Policy
的情况下出现空异常问题 #I5EVF2 - 启用数据库实体跟踪时导致新增实体多次查询数据库问题 #I4J2LZ
- 不启用规范化结果导致验证失效,异常失效问题 cdb3f57
- 验证异常和友好异常冲突问题 83f0036
-
CentOS 7.9
系统部署无法指定命令--urls
参数问题 8cc8ee
- 自
其他更改
- 脚手架所有
.json
文件,默认添加JSON Schema
支持
- 脚手架所有
文档
- 组件化启动文档
-
Vue/React/Angular
请求代理文档 -
JSON Schema
文档,支持配置智能提示和验证 - 跨域文档、规范化文档、配置文档、日志文档、IIS 部署文档
本期亮点
- 新增
JSON Schema
支持,所有.json
文件支持智能提示和验证
查看更改
{ "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json", "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information", "Microsoft.EntityFrameworkCore": "Information" } }, "AllowedHosts": "*"}

- 根据
Swagger
生成Vue/React/Angular
前端请求代码
Sql
代理支持返回单个类类型参数
查看更改
public interface ISql : ISqlDispatchProxy{ // 集合类型 [SqlExecute("select * from person")] List<Person> GetPersons(); // 自 v3.7.3+ 版本支持返回单个类类型参数 [SqlExecute("select * from person where id=@id")] Person GetPerson(int id);}
Sql
代理支持返回ValueTuple
单个类类型参数
查看更改
public interface ISql : ISqlDispatchProxy{ [SqlExecute(@" select * from person where id =@id; select * from person")] (Person, List<Person>) GetData(int id); // 注意返回值是 `(Person, List<Person>)` 组合}
- 支持
Minimal API
应用
查看更改
var builder = WebApplication.CreateBuilder(args).Inject();// 注册 Minimal 服务builder.Services.AddInjectMini();var app = builder.Build();app.UseHttpsRedirection();app.UseInject(string.Empty);app.MapGet("/hello", () =>{ return "Hello, Furion";});app.Run();
v3.6.9(已发布)
新特性
-
Serve.Run()
极简主机模式,真正实现极速入门。95cac5b -
TP.Wrapper(...)
拓展方法,主要用来生成规范化的日志模板 427999a - 项目类型为
<Project Sdk="Microsoft.NET.Sdk">
的控制台项目 fb08a65 -
BadPageResult
错误页面类型 !494 -
[SchemaId]
特性,解决不同程序集相同的类名生成Swagger
的SchemaId
冲突问题 #I5D3CU - 远程请求
options.ApproveAllCerts()
忽略所有客户端证书拓展 eb7d18a - 判断是否是单文件环境部署静态属性
App.SingleFileEnvironment
de556f0 -
WebApplicationBuilder.UseSerilogDefault()
拓展 e02524c
-
突破性变化
问题修复
- 修复默认注册的
services.AddResponseCaching();
服务导致.axd
内嵌资源请求错误问题 !495 -
Oracle
数据库执行sql
必须要求命令参数和sql
语言参数数量一致 #I5D057 -
IHostService
类型不能自动注册问题,之前只扫描了BackgroundService
派生类 968344 - 国产芯片主机不能识别
dotnet run --urls
参数问题 6d4398 - 远程请求上传文件不支持特定文件后缀问题,如
.pem
文件 ba42198 - 一些程序集已破坏或程序集不完整导致主机无法启动问题 d2dc3e4
- 远程请求传入
headers
时类型为Dictionary<string, string>
导致转换异常问题 #I5DHL9 -
Serilog
单文件发布不生成日志文件 I5DQ2B
- 修复默认注册的
其他更改
文档
-
Serve.Run()
文档 -
HttpContext
文档 -
GlobalUsings
文档 -
TP
全局静态类文档 - 中间件文档、筛选器文档、审计日志文档
- 跨域文档、远程请求文档
-
精彩贡献
- !494 优秀
Pull Request
辩论典范
- !494 优秀
本期亮点
- 极速入门
查看更改
Serve.Run();[DynamicApiController]public class HelloService{ public string Say() { return "Hello, Furion"; }}
启动浏览器查看效果,惊呆了吗!

- 内置错误页
查看更改
using Furion.FriendlyException;public IActionResult Add(Person person){ if(!ModelState.IsValid) { return new BadPageResult(); }}

Swagger
支持Markdown
查看更改
/// <summary>/// 测试 Markdown/// </summary>/// <remarks>/// # 测试 `Markdown` 注释////// ////// ```cs/// Serve.Run();////// [DynamicApiController/// public class HelloService/// {/// public string Say()/// {/// return nameof(Furion);/// }/// }/// ```////// 功能还不错!!!////// | 商品 | 价格 | # 其他 |/// |--------------|-----------|------------|/// | Juicy Apples | 1.99 | *7* |/// | Bananas | **1.89** | 5234 |/// | Bananas | **1.89** | 5234 |/// | Bananas | **1.89** | 5234 |////// -----////// # Furion 探索版////// > 在过去一年,实现 `Furion` 从无到有,编写文档已逾百万字,过程心酸开源人自知。/// >/// > 这一路日夜兼程,嘲讽批评常伴眼耳,即便辛苦无奈、想过放弃,但为了那微不足道的存在感依然努力着。/// >/// > 当然,也收获了不少...越来越多拥趸者,越发精湛技术能力,更高层次思维模式,还有许多跨界跨行朋友。/// >/// > 在 《[开源指北] (https://gitee.com/opensource-guide/comments/)》中,我曾说道:“开源如同人的脸,好坏一面便知,缺点可能会受到嘲讽批评,优点也会收获赞扬尊重。别担心,他们正在塑造更好的你。”/// >/// > 所以,这一次重新起航,重塑 `Furion` 重塑自己。也许未来在某个 IT 圈但凡有人谈起 `.NET` 还能瞟到 `Furion` 的身影。////// ---////// 🎉 探索 Furion 未来更多可能性,实现无第三方依赖的版本,所有模块功能按需安装,按需加载。////// - 作者:[百小僧] (https://gitee.com/monksoul)/// - 日期:2021 年 08 月 30 日////// ## 环境////// - IDE :[Microsoft Visual Studio Enterprise 2022 Preview(64 位) 版本 17.0.0 Preview 3.1] (https://visualstudio.microsoft.com/zh-hans/vs/preview/)/// - SDK :[.NET SDK 6] (https://dotnet.microsoft.com/download/dotnet/6.0)/// - 语言:[C# 10](https://docs.microsoft.com/zh-cn/dotnet/csharp/whats-new/csharp-10)////// ## 包说明////// - `Furion.Core`:无第三方依赖,可在 `.NET 6` 所有项目类型中运行。/// - `Furion`:内部依赖 `Furion.Core` 且无第三方依赖,**聚焦 `Web` 应用**,采用共享框架 `<FrameworkReference Include="Microsoft.AspNetCore.App" />` 模式/// </remarks>public void MarkdownTest(){ // ....}

v3.5.7(已发布)
新特性
-
Options
选项属性支持自定义Key
名称,[MapSettings("key")]
#I5B2HN -
EventBus
模块事件Id
支持枚举类型 2f328aa -
EventBus
模块发布者PublishAsync
和PublishDelayAsync
重载 2f328aa -
EventBus
模块拓展方法:Enum.ParseToString()
和String.ParseToEnum()
2f328aa -
Furion
和SqlSugar
脚手架 🆕🆕🆕 8d9293d -
Dapper
拓展全局配置委托 #I5AYFX -
sql
转实体支持多种命名策略(纯大写,纯小写,带下划线分割等等),如Oracle
数据库 a90e245 -
FS.InitalContentTypeProvider()
拓展方法,获取系统内所有支持的Content-Type
文件提供器 6099900
-
突破性变化
- 彻底解决了
Furion
不能单文件发布的问题 7e8e0b7
- 彻底解决了
问题修复
其他更改
文档
-
Furion
单文件发布文档 -
Furion + SqlSugar
脚手架文档 - 事件总线文档、选项文档、即时通讯文档、
.NET5
升级.NET6
文档、依赖注入文档、跨域文档、数据加解密文档
-
本期亮点
- 事件总线
Id
支持枚举类型
查看更改
EventSubscribe("TO:DO")] // 字符串类型public async Task EventHandler1(EventHandlerExecutingContext context){ // ....}[EventSubscribe(YourEnum.Some)] // 枚举类型public async Task EventHandler2(EventHandlerExecutingContext context){ var eventEnum = context.Source.EventId.ParseToEnum(); // 将事件 Id 转换成枚举对象 // ....}
- 事件总线发布支持更简单调用
查看更改
// 旧版本await _eventPublisher.PublishAsync(new ChannelEventSource("ToDo:Create", name));// 新版本await _eventPublisher.PublishAsync("ToDo:Create", name);await _eventPublisher.PublishAsync(YourEnum.Some); // 也支持枚举
- 选项支持属性自定义配置
Key
查看更改
"AppInfo": { "Name": "Furion", "Version": "1.0.0", "Company_Name": "Baiqian" // 可以和属性不一样}
public class AppInfoOptions : IConfigurableOptions{ public string Name { get; set; } public string Version { get; set; } [MapSettings("Company_Name")] // 支持自定义 public string Company { get; set; }}
- 日志规范化模板
查看更改
// 生成模板字符串var template = TP.Wrapper("Furion 框架", "让 .NET 开发更简单,更通用,更流行。", "[作者] 百小僧", "[当前版本] v3.5.3", "[文档地址] https://furion.icu", "[Copyright] 百小僧, Baiqian Co.,Ltd.");Console.WriteLine(template);
输出结果
┏━━━━━━━━━━━ Furion 框架 ━━━━━━━━━━━┣ 让 .NET 开发更简单,更通用,更流行。┣┣ 作者: 百小僧┣ 当前版本: v3.5.3┣ 文档地址: https://furion.icu┣ Copyright: 百小僧, Baiqian Co.,Ltd.┗━━━━━━━━━━━ Furion 框架 ━━━━━━━━━━━
v3.4.2(已发布)
新特性
- 规范化文件
EnableAllGroups
功能,可以将多个分组合并到一个分组中 9277b98 -
angular-utils
客户端工具库,专门处理angular
项目接口代理问题 6c70584 -
Swagger
支持单个接口更多描述功能(支持html
)e5e1db0 -
Swagger
接口[Obsolete]
过时支持功能 e5e1db0 - 动态
API
的[ApiDescriptionSettings]
特性 和DynamicApiControllerSettings
配置 的ForceWithRoutePrefix
参数,支持强制复写[Route]
特性并添加DefaultRoutePrefix
#I59B74
- 规范化文件
突破性变化
- 默认内置
GBK
,Windows-1252
,Shift-JIS
,GB2312
等编码支持 c456ecb -
Furion
和SqlSugar
脚手架
- 默认内置
问题修复
-
<inheritdoc />
不能跨程序集问题 3b9d39c -
<inheritdoc />
不支持带参数,不支持隐式实现接口注释问题 #I59A6W#note_10699021 -
v3.3.1
版本导致Swagger
不能显示问题 6763352 - 远程请求、
JSON
以及Web
页面不支持GBK
,GB2312
等国标编码问题 c456ecb - 远程请求响应报文设置了
Content-Type:charset=
不能自动转换编码问题 c456ecb
-
其他更改
-
axios-utils.ts
和angular-utils.ts
多客户端支持
-
文档
本期亮点
- 启用
All Groups
分组功能
查看更改
有时候我们为了更好的对接口进行归类,配置了 Swagger
多个分组的功能,但这样也对生成客户端请求代码造成了困扰,所以添加了新的配置:
{ "SpecificationDocumentSettings": { "EnableAllGroups": true }}
- 接口过时控制
查看更改
当我们某个接口已经过时,提示尽早调用最新接口,只需要在方法上面贴 [Obsolete]
即可,如:
[Obsolete("GetName() 已经过时,请调用 GetFrameworkName() 替代")]public string GetName(){ return nameof(Furion);}[Obsolete]public string Other(){ // ...}

- 单一接口更多描述
查看更改
在该版本新增了 [ApiDescriptionSettings]
的 Description
属性,支持定义更多描述,如:
[ApiDescriptionSettings(Description = "我是一段描述,显示更多内容 <button>我是按钮</button>")]public string add(){ //....}

v3.3.3(已发布)
新特性
突破性变化
- 代码注释,规范化文档注释
inheritdoc
语法支持 ❤️️️️ #159A6W -
.NET
所有依赖包至v6.0.5
版本
- 代码注释,规范化文档注释
问题修复
- 自定义全局异常
Exception
后导致获取错误行号,文件空异常问题 #I53EGM - 配置数据库上下文传递空委托导致空引用异常问题 #I519AW
- 字符串模板模板
Render
拓展方法返回void
问题,应该返回string
Github-#99 - 远程请求文件上传出现空情况问题(原因是缺失
Content-Type
)I57ZMN
- 自定义全局异常
其他更改
文档
- 粘土对象序列化
JSON
配置文档 - 前端解密
JWT
文档 - 将
byte[]
转url
文档 - 二级虚拟目录部署文档,远程请求文档,文件上传文档,安全授权文档、规范化文档
- 粘土对象序列化
本期亮点
- ❤️️️️ 根据文件名获取
MIME
或Content-Type
类型
- ❤️️️️ 根据文件名获取
查看更改
var success = FS.TryGetContentType("image.png", out var contentType); // image/png
- ❤️️️️ 支持
Swagger
配置登录后才能访问
{ "SpecificationDocumentSettings": { "LoginInfo": { "Enabled": true, "CheckUrl": "检查登录地址", "SubmitUrl": "提交登录地址" } }}

- ❤️️️️ 支持代码注释继承,Swagger 文档注释也支持
查看更改
/// <inheritdoc cref="ITestInheritdoc" />public class TestInheritdoc : ITestInheritdoc, IDynamicApiController{ /// <inheritdoc cref="ITestInheritdoc.GetName"/> public string GetName() { return "Furion"; } /// <inheritdoc /> public string GetVersion() { return "3.3.3"; }}/// <summary>/// 测试注释继承/// </summary>public interface ITestInheritdoc{ /// <summary> /// 获取名称 /// </summary> /// <returns></returns> string GetName(); /// <summary> /// 获取版本 /// </summary> /// <returns></returns> string GetVersion();}

v3.2.0(已发布)
新特性
突破性变化
- 所有依赖包至最新版
- 依赖注入模块核心代码,移除注册服务采用反射机制,减少反射性能损耗 acdb315
问题修复
其他更改
文档
- 定时任务文档,日志文档
- 文件上传/下载 文档,包含单文件/多文件/Base64/Byte[]
v3.1.0(已发布)
新特性
突破性变化
-
.NET6
依赖包全部升级至Nuget
最新版v6.0.3
-
问题修复
其他更改
- [过时] 标记
Furion.Extras.Logging.Serilog
拓展包IWebHost
拓展为过时状态
- [过时] 标记
文档
- 优化文档体验,新增面包屑导航,重写文档缓存,提升文档访问速度
- 更新动态 API 文档、配置文档、远程请求文档
- 更新二级虚拟目录文档
本期亮点
- 新增动态
WebApi
支持小驼峰路径,如GetMyName
->getMyName
:
查看更改
{ "DynamicApiControllerSettings": { "LowercaseRoute": false, "KeepName": true, "AsLowerCamelCase": true }}
- 支持
.NET6 WebApplication
模式二级虚拟目录配置:
查看更改
app.UseVirtualPath(app =>{ app.UseInject(String.Empty); // 注意 String.Empty 只是例子,可以不填或填其他的,见一分钟入门 app.MapRouteControllers();});
v3.0.0(已发布)
Furion v3.x 版本采用 .NET6 构建。
新特性
- 远程请求支持
GET
请求自动转换类类型
类型对象 #I4HR5Q
- 远程请求支持
突破性变化
- 全面支持
.NET6
版本
- 全面支持
问题修复
其他更改
-
ToPagedList
泛型约束 d0244d
-
文档
- 查看 .NET6 一分钟入门
- 查看 Furion v2 升级 v3
特别鸣谢
v2.20(已发布)
在 Furion v2.20+
版本后采用 Jaina 事件总线替换原有的 EventBus
新特性
- 远程请求支持
GET
请求自动转换类类型
类型对象 #I4HR5Q
- 远程请求支持
突破性变化
-
EventBus
模块,采用 Jaina 方式
-
问题修复
- 开启规范化结果并自定义全局异常导致异常经过
OnSucceeded
过滤器 bug #I4DTVL -
.NET5.0.5+
微软底层修改了[ApiController]
验证失败返回IActionResult
类型 #I4ISOK - 远程请求上传文件异常 0c0752
- 框架启动不支持环境变量
ASPNETCORE_HOSTINGSTARTUPASSEMBLIES
配置 !438 - 定时任务内存和 CPU 占用及特殊情况下空异常问题 12c65de
- 默认控制器启用规范化结果无效 bugc7a4a5e
- 依赖注入
InjectionAttribute
特性的ExceptInterfaces
单词拼写错误问题 !436 -
Sql
命令参数传入Clay
类型异常问题 #I4D21Q
- 开启规范化结果并自定义全局异常导致异常经过
其他更改
文档
- 事件总线新文档
v2.19(已发布)
新特性
问题修复
-
Scoped.Create
在EFCore
进行Add-Migration
时候报空异常问题,原因是在PM
环境中不存在根服务0853e74 - 定时任务执行异常后异常一直驻留内存问题,修正为执行成功自动清空过去异常 197a62b
-
Jwt
拓展包不正确的代码导致 IOptions 失效#I46LUP -
Swagger
枚举Schema
过滤器不输出值问题 #I46LON !404 -
Swagger
处理非int
类型枚举转换 bug #I46QJ9 - 视图引擎编译模板生成
dll
后再次加载出现IL
格式化错误问题 ff52d38 - 管道
Channel
读取器无法释放Handler
对象问题 10f4a90 -
Worker Services
下日志不输出问题 c482548 - 远程请求
multipart/form-data
类型对接微信小程序上传文件 问题 d9bad03 - 工作单元上下文在某些情况下共享事务失效问题 006d439
-
Swagger
枚举值在GET
请求中Schema
显示不正确问题 fb72fd7 - 远程请求
404
不走异常过滤器问题 !426 - 自定义事件总线并发情况下调用完成后无法正确处理队列数据 !429
-
v2.19+
版本之后模块化开发加载外部程序集失效问题 !433 - 定时任务 SpareTime 频繁检查导致 CPU 增高问题 aa0a2ee
-
特别鸣谢
v2.18(已发布)
新特性
突破性变化
在过去,很多开发者总是喜欢在
Startup.cs
配置服务的ConfigureService
方法中解析服务,这样导致内存存在溢出风险,GC 无法回收。 正确的方式是尽可能的避免ConfigureService
中解析服务。如果需要在【启动时】获取配置选项
,请使用App.GetConfig<TOptions>(路径, true)
代替App.GetOptions<TOption>
。
问题修复
文档
- 包管理工具文档
- 模板引擎、
Sql
操作,数据库上下文
等等文档
v2.17(已发布)
新特性
突破性变化
问题修复
其他更改
- Swagger 生成泛型 SchemaIds 默认连接符,由
Of
改为_
81946b6
- Swagger 生成泛型 SchemaIds 默认连接符,由
v2.16(已发布)
新特性
突破性变化
查看更改
using Furion.DataValidation;using Furion.DependencyInjection;using Furion.UnifyResult.Internal;using Microsoft.AspNetCore.Http;using Microsoft.AspNetCore.Mvc;using Microsoft.AspNetCore.Mvc.Filters;using System;using System.Threading.Tasks;namespace Furion.UnifyResult{ /// <summary> /// RESTful 风格返回值 /// </summary> [SuppressSniffer, UnifyModel(typeof(RESTfulResult<>))] public class RESTfulResultProvider : IUnifyResultProvider { /// <summary> /// 异常返回值 /// </summary> /// <param name="context"></param> /// <param name="metadata"></param> /// <returns></returns> public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata) { return new JsonResult(RESTfulResult(metadata.StatusCode, errors: metadata.Errors)); } /// <summary> /// 成功返回值 /// </summary> /// <param name="context"></param> /// <param name="data"></param> /// <returns></returns> public IActionResult OnSucceeded(ActionExecutedContext context, object data) { return new JsonResult(RESTfulResult(StatusCodes.Status200OK, true, data)); } /// <summary> /// 验证失败返回值 /// </summary> /// <param name="context"></param> /// <param name="metadata"></param> /// <returns></returns> public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata) { return new JsonResult(RESTfulResult(StatusCodes.Status400BadRequest, errors: metadata.ValidationResult)); } /// <summary> /// 特定状态码返回值 /// </summary> /// <param name="context"></param> /// <param name="statusCode"></param> /// <param name="unifyResultSettings"></param> /// <returns></returns> public async Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings) { // 设置响应状态码 UnifyContext.SetResponseStatusCodes(context, statusCode, unifyResultSettings); switch (statusCode) { // 处理 401 状态码 case StatusCodes.Status401Unauthorized: await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "401 Unauthorized") , App.GetOptions<JsonOptions>()?.JsonSerializerOptions); break; // 处理 403 状态码 case StatusCodes.Status403Forbidden: await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "403 Forbidden") , App.GetOptions<JsonOptions>()?.JsonSerializerOptions); break; default: break; } } /// <summary> /// 返回 RESTful 风格结果集 /// </summary> /// <param name="statusCode"></param> /// <param name="succeeded"></param> /// <param name="data"></param> /// <param name="errors"></param> /// <returns></returns> private static RESTfulResult<object> RESTfulResult(int statusCode, bool succeeded = default, object data = default, object errors = default) { return new RESTfulResult<object> { StatusCode = statusCode, Succeeded = succeeded, Data = data, Errors = errors, Extras = UnifyContext.Take(),