兩千星 .NET5 框架 Furion 亮點分析(一)

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>();
                });
    }
}

文檔傳送門://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 模板

文檔傳送門://monksoul.gitee.io/furion/docs/template

3. 極少依賴

Furion 為了追求極速入門,極致性能,儘可能的不使用或減少第三方依賴。目前 Furion 僅集成了以下兩個依賴:

麻雀雖小五臟俱全。Furion 即使只集成了這兩個依賴,但是主流的 依賴注入/控制反轉AOP 面向切面編程,事件匯流排數據驗證資料庫操作 等等一個都不少。

文檔傳送門://monksoul.gitee.io/furion/docs/

4. appsettings.json 分割合併

默認情況下,.NET5 的配置文件需寫在 appsettings.json 文件中,如需添加其他配置,需手動添加並在主機啟動時載入進來,Furion 提供了非常靈活方便的方式,支援配置文件放在項目其他位置,並自動載入,自動配置併合並。如:system.json

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

此時 system.json 會自動載入並自動合併。

文檔傳送門://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)
        {
        }
}

文檔傳送門://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 請求";
        }
    }
}

文檔傳送門://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;
            }
        }
    }
}

文檔傳送門://monksoul.gitee.io/furion/docs/specification-document#67-統一返回值模型

8. 強大的 Swagger 在線文檔

Furion 內置了強大的 Swagger 規範化文檔,並賦予了更多強大的能力。

文檔傳送門://monksoul.gitee.io/furion/docs/specification-document

9. 非常強大的資料庫操作功能

文檔傳送門://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;
        }
    }
}

文檔傳送門://monksoul.gitee.io/furion/docs/friendly-exception

11. 強大且靈活的 SaaS 多租戶實現方式

文檔傳送門://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);
        }
    }
}

文檔傳送門://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 }
});

文檔傳送門://monksoul.gitee.io/furion/docs/view-engine

結語

由於篇幅有效,這裡只是拋磚引玉,但 Furion 的亮點遠不止這些,可以查看文檔了解並學習。

文檔地址://monksoul.gitee.io/furion/


最後,喜歡 Furion 可以給個 Star,您的支援 Furion 才能越走越遠。