7

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

 3 years ago
source link: http://www.cnblogs.com/dotnetchina/p/14201430.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

ZBVN7jj.png!mobile

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

Furion 介绍

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

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

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

eqE3Ifj.png!mobile

开源地址

文档地址

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

项目案例

  • 考试君:基于 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 类型的脚手架,可以极速搭建企业项目多层架构。

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

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

3. 极少依赖

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

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

2iUnIvJ.png!mobile

文档传送门: 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 规范化文档,并赋予了更多强大的能力。

bI3UB3Q.png!mobile

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

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

jAZrqa7.png!mobile

文档传送门: 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 多租户实现方式

IjymEjJ.png!mobile

文档传送门: 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 的亮点远不止这些,可以查看文档了解并学习。

fMvAnir.png!mobile

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK