Логгинговый модуль является обязательной частью любого приложения, можно сказать, что это один из самых важных компонентов! В сообществе .NET есть логгеры Log4Net, NLog, Serilog и другие, все они являются отличными открытыми проектами.
Однако из-за долгой истории этих логгеров, внутри них поддерживаются множество версий .NET, а функционал со временем становится очень мощным и сложным. При использовании в реальных проектах было замечено, что конфигурация каждого из логгеров не всегда удобна, особенно с точки зрения простоты использования.
Furion как полифункциональный фреймворк, ранее не предоставлял достаточно мощного логгера для удовлетворения потребностей разработчиков, поэтому рекомендовал использовать сторонние компоненты, такие как Serilog, что привело к невозможности реализовать пользовательские функции и увеличению затрат на поддержание.
На этот раз Furion больше не будет делать уступок, полностью переоптимизировал логгинговый модуль, обеспечивая полную автономность управления логами и предоставление всем необходимым функциям логирования для разработчиков!
Furion-разработчики, пришло время "заменить" сторонние логгеры, давайте вместе создадим более совершенные логгеры! 🍖
Новые возможности
Разрушение границ
Документация
Основные моменты выпуска
// Запись в файл
services.AddFileLogging("logs/application.log");
// Запись в базу данных
services.AddDatabaseLogging<DatabaseLoggingWriter>();
Новые возможности
Разрушительные изменения
Исправления ошибок
Другие изменения
Документация
Новые возможности
Другие изменения
Документация
Новые возможности
Документация
Vue
, React
и Angular
Новые возможности
Разработки
Документация
Serve.Run()
HttpContext
GlobalUsings
TP
Основные достижения в выпуске
Serve.Run();
[DynamicApiController]
public class HelloService
{
public string Say()
{
return "Привет, Furion";
}
}
Не удивлены ли вы?
Новые возможности
[MapSettings("key")]
#I5B2HN
PublishAsync
и PublishDelayAsync
в EventBus 2f328aa
Enum.ParseToString()
и String.ParseToEnum()
2f328aa
Другие изменения
Документация
Основные достижения
[EventSubscribe("TO:DO")] // строковый тип
public async Task EventHandler1(EventHandlerExecutingContext context)
{
// ...
}
[EventSubscribe(YourEnum.Some)] // перечисляемый тип
public async Task EventHandler2(EventHandlerExecutingContext context)
{
var eventEnum = context.Source.EventId.ParseToEnum(); // преобразование идентификатора события в объект перечисления
// ...
}
// старая версия
await _eventPublisher.PublishAsync(new ChannelEventSource("ToDo:Create", name));
// новая версия
await _eventPublisher.PublishAsync("ToDo:Create", name);
await _eventPublisher.PublishAsync(YourEnum.Some); // также поддерживается перечисляемый тип
"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; }
}
Новые возможности
EnableAllGroups
, который позволяет объединять несколько групп в одну 9277b98
angular-utils
, предназначенная для решения проблем с прокси-интерфейсами проектов angular
6c70584
Swagger
(поддерживает html
) e5e1db0
[Obsolete]
для помечивания устаревших интерфейсов в Swagger
e5e1db0
Разрешенные изменения
GBK
, Windows-1252
, Shift-JIS
, GB2312
и другие c456ecb
Устранение ошибок
<inheritdoc />
через сборки 3b9d39c
Swagger
после версии v3.3.1
6763352
GBK
, GB2312
и других государственных стандартов при выполнении удалённых запросов, работе с JSON
и Web
страницами c456ecb
Content-Type: charset=
в ответах на запросы c456ecb
Другие изменения
axios-utils.ts
и angular-utils.ts
Документация
GlobalUsings
адрес документа
Worker Service
и динамического APIОсновные достижения этого выпуска
All Groups
Часто нам требуется лучше организовать группы для удобства управления интерфейсами. Для этого была добавлена новая настройка:
{
"SpecificationDocumentSettings": {
"EnableAllGroups": true
}
}
Если какой-то метод стал устаревшим, его можно пометить аннотацией [Obsolete]
. Например:
[Obsolete("Метод GetName() устарел, используйте вместо него GetFrameworkName().")]
public string GetName()
{
return nameof(Furion);
}
[Obsolete]
public string Other()
{
// ...
}
В этом выпуске был добавлен атрибут Description
в аннотацию [ApiDescriptionSettings]
, что позволяет добавлять больше информации к описанию интерфейса. Например:
[ApiDescriptionSettings(Description = "Это описание, которое показывает больше контента <button>Кнопка</button>")]
public string Add()
{
//...
}
Новые возможности
Content-Type
) и MIME при удалённой загрузке файла #I57ZMN
Content-Type
) и кодировки (Encoding
) в методах удалённого запроса #I57ZMN
Content-Type
) и MIME по имени файла #8f78184
inheritdoc
в комментариях кода и нормализованной документации ❤️️️️ #159A6W
Разрушительные изменения
inheritdoc
в комментариях кода и нормализованной документации ❤️️️️ #159A6W
[Обновлено] Все зависимости .NET до версии v6.0.5
Исправление ошибок
Exception
#I53EGM
Render
, который возвращает void
, а должен возвращать string
Github-#99
Content-Type
при удалённой загрузке файла #I57ZMN
Другие изменения
Документация
Основные достижения
var success = FS.TryGetContentType("image.png", out var contentType); // image/png
{
"SpecificationDocumentSettings": {
"LoginInfo": {
"Enabled": false,
"CheckUrl": "URL для проверки",
"SubmitUrl": "URL для отправки"
}
}
}
/// <inheritdoc cref="ITestInheritdoc" />
public class TestInheritdoc : ITestInheritdoc, IDynamicApiController
{
/// <inheritdoc cref="ITestInheritdoc.GetName"/>
public string GetName()
{
return "Furion";
}
}
/// <summary>
/// Тест наследования комментариев
/// </summary>
public interface ITestInheritdoc
{
/// <summary>
/// Получение имени
/// </summary>
/// <returns></returns>
string GetName();
}
Новые возможности
Основные изменения
v6.0.5
Исправление ошибок
Exception
#I53EGM
[Исправлено] Проблема с пустым делегатом при передаче контекста базы данных, вызывающая ошибку отсутствующего объекта #I519AW
[Исправлено] Проблема с методом расширения Render
шаблонов строковых моделей, который возвращает void
, а должен возвращать string
[Исправлено] Проблема с пустыми условиями при удалённой загрузке файла (причина — отсутствие Content-Type
) I57ZMN
Другие изменения
Документация
byte[]
) в URLНовые возможности
Разработки
Исправление ошибок
schema
как C# Object
в Swagger Swagger официальный Issue 1a25274
Worker Service
как Windows Services
. Спасибо @jacoat !467
OnRequestFailed
в модуле удалённых запросов #I54PK7
Not found Method
при использовании рефлексии в модуле внедрения зависимостей #I546L1
Другие изменения
Документация
Новые возможности
Исправление ошибок
Документация
Новые возможности
Разработки
Исправления ошибок
2022-03-01 0:00:00
(теперь поддерживается часовой диапазон 0
и 00
) #I4Y3NT
Другие изменения
Furion.Extras.Logging.Serilog
для расширения IWebHost
помечено как устаревшееДокументация
Основные достижения этого выпуска
{
"DynamicApiControllerSettings": {
"LowercaseRoute": false,
"KeepName": true,
"AsLowerCamelCase": true
}
}
app.UseVirtualPath(app =>
{
app.UseInject(string.Empty); // Внимание: string.Empty — это пример, можно использовать любое значение, см. раздел «Быстрый старт за минуту»
app.MapControllers();
});
Исправленные проблемы
OnSucceeded
баг #I4DTVL
.NET5.0.5+
и .NET6
Microsoft изменил внутреннюю реализацию проверки [ApiController]
, теперь в случае провала возвращается тип IActionResult
#I4ISOK
EFCore 6.0
с SqlServer 2005+
#I4ILA5
Clay
в команду Sql
#I4D21Q
Furion v3.x версия построена на .NET 6.
Основные изменения
.NET 6
Документация
Новые возможности
ISpareTimeListener
#I468Q1
SQL
с параметрами типа JsonElement
61985d6
Swagger
с использованием перечислений и фильтром сортировки меток #I46LON !404
application/octet-stream
при удалённых запросах d9bad03
Исправление ошибок
PM
, вызывающая пустую ошибку при выполнении команды Add-Migration
через Scoped.Create
в EFCore
0853e74
Jwt
, приводящий к недействительности IOptions
#I46LUP
Schema
перечислений Swagger
#I46LON !404
Swagger
#I46QJ9
multipart/form-data
для загрузки файлов в WeChat Mini Program d9bad03
Просмотреть журнал обновлений: https://dotnetchina.gitee.io/furion/docs/upgrade
Новые возможности
throw Oops.Bah()
(код состояния 400)UnifyResultSettings
#I42NY7
Разрушительные изменения
IUnifyResultProvider
для нормализованного результата (разрушительные изменения) #I427Z2
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(),
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
};
}
}
}
Устранение ошибок
new Json({})
равного null после включения нормализованной обработки в контроллер MVC #I4354S
Другие изменения
Lang.cs
#I434YJ
Новые возможности
Разрушительные изменения
IUnifyResultProvider
нормализации (разрушительные изменения) #I427Z2
Новый версионный подход к созданию нормализованных результатов
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>
Yöntem: Возврат ошибки
/// </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>
Yöntem: Успешный возврат
/// </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>
Yöntem: Возврат ошибки проверки данных
/// </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>
Yöntem: Возврат специального статусного кода
/// </summary>
/// <param name="context"></param>
/// <param name="statusCode"></param>
/// <param name="options"></param>
/// <returns></returns>
public async Task<IActionResult> OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultStatusCodesOptions options)
{
// Установка статусного кода ответа
UnifyContext.SetResponseStatusCodes(context, statusCode, options);
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>
Yöntem: Возвращает стилизованный 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(),
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
};
}
}
}
Устранение проблем
IRepository<TEntity>
вместо IRepository<TEntity, TDbContextLocator>
#I41MZP
AppDbContext.Tenant
#I421DA
Другие изменения
Новые возможности
ISpareTimeWorker
для планировщиков задач #I40KWR
Разрушительные изменения
Db.GetNewDbContext()
#I400BK
IJsonSerializerProvider
, добавлен новый параметр inherit
#I3ZQU5
AppSettings
параметра LogEntityFrameworkCoreSqlExecuteCommand
в OutputOriginalSqlExecuteLog
#I40VVE
Исправления ошибок
GetNewDbContext
RequestUrl
при использовании клиента Client
для удалённых запросов #I40BC6
Другие изменения