两千星 .NET5 框架 Furion 亮点分析(一)

时间:2024-01-27 19:55:53

star fork GitHub stars GitHub forks GitHub license nuget

让 .NET 开发更简单,更通用,更流行。

Furion 介绍

Furion 是基于 .NET5 平台下打造的现代化 Web 框架。旨在 让 .NET 开发更简单,更通用,更流行。

Furion 非常精悍短小,底层只依赖两个第三方库,去除注释文件仅有 73kb 大小。

麻雀虽小五脏俱全,企业级项目开发所需功能它一个也不少。

开源地址

文档地址

目前文档正在逐步完善中。

项目案例

  • 考试君:基于 Furion 的在线考试系统
  • 园丁:基于 Furion + Blazor 的超简单后台管理系统
  • Queer:基于 Furion + Layui 的通用型管理系统
  • Pear Admin:PearAdmin 官方和 Furion 官方合作管理系统
  • JoyAdmin:基于 Furion + iviewadmin 开发的管理系统

Furion 亮点介绍

1. 极易集成

Furion 设计遵循“无侵入性或侵入性极小”原则,只需要在 Progame.cs 文件夹中添加 Inject() 方法即可完成初始化配置。

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace FurStart
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder
                    .Inject()   // 只需这一行即可完成配置
                    .UseStartup<Startup>();
                });
    }
}

文档传送门:https://monksoul.gitee.io/furion/docs/get-start

2. 丰富脚手架

Furion 提供了 .NET5 所有 Web 类型的脚手架,可以极速搭建企业项目多层架构。

模板类型 名称 版本 描述
nuget Furion.Template.Mvc nuget Mvc 模板
nuget Furion.Template.Api nuget WebApi 模板
nuget Furion.Template.App nuget Mvc/WebApi 模板
nuget Furion.Template.Razor nuget RazorPages 模板
nuget Furion.Template.RazorWithWebApi nuget RazorPages/WebApi 模板
nuget Furion.Template.Blazor nuget Blazor 模板
nuget Furion.Template.BlazorWithWebApi nuget Blazor/WebApi 模板

文档传送门:https://monksoul.gitee.io/furion/docs/template

3. 极少依赖

Furion 为了追求极速入门,极致性能,尽可能的不使用或减少第三方依赖。目前 Furion 仅集成了以下两个依赖:

麻雀虽小五脏俱全。Furion 即使只集成了这两个依赖,但是主流的 依赖注入/控制反转AOP 面向切面编程,事件总线数据验证数据库操作 等等一个都不少。

文档传送门:https://monksoul.gitee.io/furion/docs/

4. appsettings.json 分割合并

默认情况下,.NET5 的配置文件需写在 appsettings.json 文件中,如需添加其他配置,需手动添加并在主机启动时加载进来,Furion 提供了非常灵活方便的方式,支持配置文件放在项目其他位置,并自动加载,自动配置并合并。如:system.json

{
  "AppInfo": {
    "Name": "Furion",
    "Version": "1.7.0"
  }
}

此时 system.json 会自动加载并自动合并。

文档传送门:https://monksoul.gitee.io/furion/docs/configuration

5. 热重载配置选项

Furion 提供了非常灵活的选项配置功能,支持后期配置,配置更改通知。

using Furion.ConfigurableOptions;

namespace Furion.Application
{
    public class AppInfoOptions : IConfigurableOptionsListener<AppInfoOptions>
    {
        public string Name { get; set; }
        public string Version { get; set; }
        public string Company { get; set; }
    
        // 选项更改监听
        public void OnListener(AppInfoOptions options, IConfiguration configuration)
        {
            var name = options.Name;  // 实时的最新值
            var version = options.Version;  // 实时的最新值
        }

        // 选项后期配置
        public void PostConfigure(AppInfoOptions options, IConfiguration configuration)
        {
        }
}

文档传送门:https://monksoul.gitee.io/furion/docs/options

6. 动态API及变体服务

Furion 提供了动态构建 WebApi 的方式,可以大大提高 WebApi 的开发效率,还能实现 WebApi 服务接口化。

using Furion.DynamicApiController;

namespace Furion.Application
{
    public class FurAppService : IDynamicApiController
    {
        public string Get()
        {
            return $"GET 请求";
        }

        public string Post()
        {
            return $"POST 请求";
        }

        public string Delete()
        {
            return $"DELETE 请求";
        }

        public string Put()
        {
            return $"PUT 请求";
        }

        public string Patch()
        {
            return $"PATCH 请求";
        }
    }
}

文档传送门:https://monksoul.gitee.io/furion/docs/dynamic-api-controller

7. 统一结果规范化处理

Furion 提供了非常灵活的方式去规范化 WebApi 的返回结果,使其采用统一规范的结果返回。

using Furion.DependencyInjection;
using Furion.Utilities;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Furion.UnifyResult
{
    /// <summary>
    /// RESTful 风格返回值
    /// </summary>
    [SkipScan, UnifyModel(typeof(RESTfulResult<>))]
    public class RESTfulResultProvider : IUnifyResultProvider
    {
        /// <summary>
        /// 异常返回值
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public IActionResult OnException(ExceptionContext context)
        {
            // 解析异常信息
            var (ErrorCode, ErrorContent) = UnifyContext.GetExceptionMetadata(context);

            return new JsonResult(new RESTfulResult<object>
            {
                StatusCode = ErrorCode,
                Succeeded = false,
                Data = null,
                Errors = ErrorContent,
                Extras = UnifyContext.Take(),
                Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
            });
        }

        /// <summary>
        /// 成功返回值
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public IActionResult OnSucceeded(ActionExecutedContext context)
        {
            object data;
            // 处理内容结果
            if (context.Result is ContentResult contentResult) data = contentResult.Content;
            // 处理对象结果
            else if (context.Result is ObjectResult objectResult) data = objectResult.Value;
            else if (context.Result is EmptyResult) data = null;
            else return null;

            return new JsonResult(new RESTfulResult<object>
            {
                StatusCode = context.Result is EmptyResult ? StatusCodes.Status204NoContent : StatusCodes.Status200OK,  // 处理没有返回值情况 204
                Succeeded = true,
                Data = data,
                Errors = null,
                Extras = UnifyContext.Take(),
                Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
            });
        }

        /// <summary>
        /// 验证失败返回值
        /// </summary>
        /// <param name="context"></param>
        /// <param name="modelStates"></param>
        /// <param name="validationResults"></param>
        /// <param name="validateFailedMessage"></param>
        /// <returns></returns>
        public IActionResult OnValidateFailed(ActionExecutingContext context, ModelStateDictionary modelStates, Dictionary<string, IEnumerable<string>> validationResults, string validateFailedMessage)
        {
            return new JsonResult(new RESTfulResult<object>
            {
                StatusCode = StatusCodes.Status400BadRequest,
                Succeeded = false,
                Data = null,
                Errors = validationResults,
                Extras = UnifyContext.Take(),
                Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
            });
        }

        /// <summary>
        /// 处理输出状态码
        /// </summary>
        /// <param name="context"></param>
        /// <param name="statusCode"></param>
        /// <returns></returns>
        public async Task OnResponseStatusCodes(HttpContext context, int statusCode)
        {
            switch (statusCode)
            {
                // 处理 401 状态码
                case StatusCodes.Status401Unauthorized:
                    await context.Response.WriteAsJsonAsync(new RESTfulResult<object>
                    {
                        StatusCode = StatusCodes.Status401Unauthorized,
                        Succeeded = false,
                        Data = null,
                        Errors = "401 Unauthorized",
                        Extras = UnifyContext.Take(),
                        Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
                    }, JsonSerializerUtility.GetDefaultJsonSerializerOptions());
                    break;
                // 处理 403 状态码
                case StatusCodes.Status403Forbidden:
                    await context.Response.WriteAsJsonAsync(new RESTfulResult<object>
                    {
                        StatusCode = StatusCodes.Status403Forbidden,
                        Succeeded = false,
                        Data = null,
                        Errors = "403 Forbidden",
                        Extras = UnifyContext.Take(),
                        Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
                    }, JsonSerializerUtility.GetDefaultJsonSerializerOptions());
                    break;

                default:
                    break;
            }
        }
    }
}

文档传送门:https://monksoul.gitee.io/furion/docs/specification-document#67-统一返回值模型

8. 强大的 Swagger 在线文档

Furion 内置了强大的 Swagger 规范化文档,并赋予了更多强大的能力。

文档传送门:https://monksoul.gitee.io/furion/docs/specification-document

9. 非常强大的数据库操作功能

文档传送门:https://monksoul.gitee.io/furion/docs/dbcontext

10. 异常处理最佳实践

Furion 采用独特的异常处理方案,可以大大的将异常和业务逻辑分离。

using Furion.DynamicApiController;
using Furion.FriendlyException;

namespace Furion.Application
{
    public class FurAppService : IDynamicApiController
    {
        [IfException(typeof(ExceptionType), ErrorMessage = "特定异常类型全局拦截")]
        [IfException(ErrorMessage = "全局异常拦截")]
        [IfException(ErrorCodes.z1000, ErrorMessage = "我覆盖了默认的:{0} 不能小于 {1}")]
        [IfException(ErrorCodes.x1001, "格式化参数1", "格式化参数2", ErrorMessage = "我覆盖了默认的:{0} 不能小于 {1}")]
        [IfException(ErrorCodes.x1000, "格式化参数1", "格式化参数2")]
        [IfException(ErrorCodes.SERVER_ERROR, "格式化参数1", "格式化参数2")]
        public int Get(int id)
        {
            if (id < 3)
            {
                throw Oops.Oh(ErrorCodes.z1000, id, 3);
            }

            return id;
        }
    }
}

文档传送门:https://monksoul.gitee.io/furion/docs/friendly-exception

11. 强大且灵活的 SaaS 多租户实现方式

文档传送门:https://monksoul.gitee.io/furion/docs/saas

12. 极易使用的对象依赖配置

Furion 提供了三种极易配置的接口依赖配置。

using Furion.Core;
using Furion.DatabaseAccessor;
using Furion.DependencyInjection;

namespace Furion.Application
{
    public interface IBusinessService<T>
    {
        Person Get(int id);
    }

    public class BusinessService<T> : IBusinessService<T>, ITransient
    {
        private readonly IRepository<Person> _personRepository;

        public BusinessService(IRepository<Person> personRepository)
        {
            _personRepository = personRepository;
        }

        public Person Get(int id)
        {
            return _personRepository.Find(id);
        }
    }
}

文档传送门:https://monksoul.gitee.io/furion/docs/dependency-injection

13. 强大的视图引擎

Furion 内置了强大的视图引擎功能,完美支持 Razor 语法。

var result = _viewEngine.RunCompile(@"
Hello @Model.Name
@foreach(var item in Model.Items)
{
    <p>@item</p>
}
", new TestModel
{
    Name = "Furion",
    Items = new[] { 3, 1, 2 }
});

文档传送门:https://monksoul.gitee.io/furion/docs/view-engine

结语

由于篇幅有效,这里只是抛砖引玉,但 Furion 的亮点远不止这些,可以查看文档了解并学习。

文档地址:https://monksoul.gitee.io/furion/


最后,喜欢 Furion 可以给个 Star,您的支持 Furion 才能越走越远。