我封裝的一個REPR輪子 Biwen.QuickApi

Biwen.QuickApi

項目介紹

[QuickApi("hello/world")]
public class MyApi : BaseQuickApi<Req,Rsp>{}
  • 提供一種簡單集成的Minimal Web Api交互模塊 遵循了 REPR 設計 (Request-Endpoint-Response)
  • 開箱即用的Api路由 和 權限,Bind,validator體驗
  • 該庫是NET WebApi/Minimal Api的補充,性能≈MinimalApi,遙遙領先於MVC和WebApi,但是提供了最簡單的的使用體驗
  • write less, do more ; write anywhere, do anything
  • 歡迎小夥伴們star&issue共同學習進步 (Biwen.QuickApi)[https://github.com/vipwan/Biwen.QuickApi]

使用方式

Step0 Nuget Install

dotnet add package Biwen.QuickApi

Step1 UseBiwenQuickApis


builder.Services.AddBiwenQuickApis(o =>
{
    o.RoutePrefix = "quick";
    //不需要駝峯模式設置爲null
    //o.JsonSerializerOptions.PropertyNamingPolicy = null;
});

//....
app.MapBiwenQuickApis();

Step2 Define Request and Response


    public class HelloApiRequest : BaseRequest<HelloApiRequest>
    {
        public string? Name { get; set; }

        /// <summary>
        /// 別名綁定字段
        /// </summary>
        [AliasAs("a")]
        public string? Alias { get; set; }

        public HelloApiRequest()
        {
            RuleFor(x => x.Name).NotNull().Length(5, 10);
        }
    }

    /// <summary>
    /// 模擬自定義綁定的Request
    /// </summary>
    public class CustomApiRequest : BaseRequest<CustomApiRequest>
    {
        public string? Name { get; set; }

        public CustomApiRequest()
        {
            RuleFor(x => x.Name).NotNull().Length(5, 10);
        }
    }

    /// <summary>
    /// 自定義的綁定器
    /// </summary>
    public class CustomApiRequestBinder : IReqBinder<CustomApiRequest>
    {
        public async Task<CustomApiRequest> BindAsync(HttpContext context)
        {
            var request = new CustomApiRequest
            {
                Name = context.Request.Query["c"]
            };
            await Task.CompletedTask;
            return request;
        }
    }

    public class HelloApiResponse : BaseResponse
    {
        public string? Message { get; set; }
    }

Step3 Define QuickApi


    /// <summary>
    /// get ~/admin/index
    /// </summary>
    [QuickApi("index", Group = "admin", Verbs = Verb.GET | Verb.POST, Policy = "admin")]
    public class NeedAuthApi : BaseQuickApi
    {
        public override EmptyResponse Execute(EmptyRequest request)
        {
            return EmptyResponse.Instance;
        }
    }

    /// <summary>
    /// get ~/hello/world/{name}
    /// </summary>
    [QuickApi("world/{name}", Group = "hello", Verbs = Verb.GET | Verb.POST)]
    public class HelloApi : BaseQuickApi<HelloApiRequest, HelloApiResponse>
    {
        private readonly HelloService _service;
        private readonly IHttpContextAccessor _httpContextAccessor;

        public Hello4Api(HelloService service,IHttpContextAccessor httpContextAccessor)
        {
            _service = service;
            _httpContextAccessor = httpContextAccessor;
        }

        public override HelloApiResponse Execute(HelloApiRequest request)
        {
            var hello = _service.Hello($"hello world {_httpContextAccessor.HttpContext!.Request.Path} !");
            return new HelloApiResponse
            {
                Message = hello
            };
        }
    }

    /// <summary>
    /// get ~/custom?c=11112222
    /// </summary>
    [QuickApi("custom", Verbs = Verb.GET)]
    public class CustomApi : BaseQuickApi<CustomApiRequest>
    {
        public CustomApi()
        {
            UseReqBinder<CustomApiRequestBinder>();
        }

        public override async Task<EmptyResponse> ExecuteAsync(CustomApiRequest request)
        {
            await Task.CompletedTask;
            Console.WriteLine($"獲取自定義的 CustomApi:,從querystring:c綁定,{request.Name}");
            return EmptyResponse.New;
        }

        /// <summary>
        /// 提供minimal擴展
        /// </summary>
        /// <param name="builder"></param>
        /// <returns></returns>
        public override RouteHandlerBuilder HandlerBuilder(RouteHandlerBuilder builder)
        {
            //自定義描述
            builder.WithOpenApi(operation => new(operation)
            {
                Summary = "This is a summary",
                Description = "This is a description"
            });

            //自定義標籤
            builder.WithTags("custom");

            //自定義過濾器
            builder.AddEndpointFilter(async (context, next) =>
            {
                Console.WriteLine("自定義過濾器!");
                return await next(context);
            });

            //自定義Api版本
            //默認爲版本1.0,如果需要訪問其他版本,需要在querystring中添加?api-version=2.0 :)
            builder.HasApiVersion(1.0).WithGroupName("1.0");
            builder.HasApiVersion(2.0).WithGroupName("2.0");

            return builder;
        }

    }


Step4 Enjoy !


//直接訪問
// GET ~/hello/world/biwen
// GET ~/hello/world/biwen?name=biwen
// POST ~/hello/world/biwen
// GET ~/custom?c=11112222


//你也可以把QuickApi當Service使用
app.MapGet("/fromapi", async (Biwen.QuickApi.DemoWeb.Apis.Hello4Api api) =>
{
    //通過你的方式獲取請求對象
    var req = new EmptyRequest();
    //驗證請求對象
    var result = req.RealValidator.Validate(req);
    if (!result.IsValid)
    {
        return Results.BadRequest(result.ToDictionary());
    }
    //執行請求
    var x = await api.ExecuteAsync(new EmptyRequest());
    return Results.Ok(x);
});

Step5 OpenApi 以及Client代理

  • 你可以全局配置版本號,以及自定義的OpenApi描述
  • 你可以重寫QuickApi的HandlerBuilder方法,以便於你自定義的OpenApi描述
  • 我們強烈建議您使用Refit風格直接擼接口,以便於您的客戶端和服務端保持一致的接口定義
  • 因爲遵循REPR風格,所以不推薦SwaggerUI或使用SwaggerStudio生成代理代碼,除非您的QuickApi定義的相當規範(如存在自定義綁定,別名綁定等)!

/// <summary>
/// refit client
/// </summary>
public interface IBusiness
{
    [Refit.Get("/fromapi")]
    public Task<TestRsp> TestPost();
}

//Refit
builder.Services.AddRefitClient<IBusiness>()
    .ConfigureHttpClient(c => c.BaseAddress = new Uri("http://localhost:5101"));

var app = builder.Build();

app.MapGet("/from-quickapi", async (IBusiness bussiness) =>
{
    var resp = await bussiness.TestPost();
    return Results.Content(resp.Message);
});

Q&A

  • 爲什麼不支持多個參數的綁定?
    -- 因爲我認爲這樣的Api設計是不合理的,我們遵循REPR設計理念,如果你需要多個參數,請使用複雜化的Request對象

  • QuickApi中如何拿到HttpContext對象?
    -- 請在構造函數中注入IHttpContextAccessor獲取

  • 是否支持Minimal的中間件和攔截器?
    -- 支持的,本身QuickApi就是擴展了MinimalApi,底層也是Minimal的處理機制,所以請考慮全局的中間件和攔截器,以及重寫QuickApi的HandlerBuilder方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章