zoukankan      html  css  js  c++  java
  • 【ASP.NET Core】体验一下 Mini Web API

    在上一篇水文中,老周给大伙伴们简单演示了通过 Socket 编程的方式控制 MPD (在树莓派上)。按照计划,老周还想给大伙伴们演示一下使用 Web API 来封装对 MPD 控制。思路很 Easy,树莓派上使用本地 Socket 来封装一下,然后以 Web API 的方式对客户端公开。这样有一个好处:之后不管你打算把客户端做成桌面窗口,还是 Web 页面,或是做成手机 App,你都可以直接调用这套 Web API。这样一来,很多代码就不必重复写了,省时省力,减少脑细胞的大量死亡。

    如果大家比较关心时事的话,应该知道 .NET 6 的 ASP.NET Core 有一个新特性—— Mini API,或 Mini Web API。有一个不错的翻译叫做“极简 API”。其实,极简的不只是 Web API,整个 ASP.NET Core 应用项目的结构都精简了不少。最让老周高兴的就是没有了 Startup 类(其实早期版本中也可以不使用 Startup,如果你以前看过老周的误人子弟教程的话,你应该有印象),也不用费心地搞个什么 ConfigureService 又要弄个 Configure 方法约定了。再配合 C# 9 的新功能,连 Main 方法都省了。所以初始化配置工作都可以在一个代码文件中搞定。

    使用这个简化版的 Web API 来做 MPD 的封装还真的不错。不过,本文老周先不弄这个,先让大伙伴们了解一下 Mini API 怎么玩——权且当作预备知识。

    .NET 6 还有个新功能也不错,就是全局的 using 指令。以前在 C 语言中,如果你写一个 abc.h 头文件,里面放上这些代码:

    #ifndef _ABC_H_
    #define _ABC_H_
    
    #include "nc.h"
    #include "nt.h"
    #include "zz.h"
    #include "xb.h"
    #include "sb.h"
    
    #endif

    然后在代码文件中 include 一个这个 abc.h 就可以间接引用这些头文件,但 C# 中没有这种玩法,每个代码文件要用到哪些命名空间,都要写一遍 using 指令。在随同 .NET 6 一同发布的 C# 10 中终于有全局 using 了。只要你在其中一个代码文件中写上全局 using 指令,然后其他代码文件中就不必再 using 了。

    方法是在 using 前加上 global 就行了。C# 虽然早有 global 关键字,但在过去这个并不是全局 using 用的,而是专用来标识 .NET 框架中的命名空间的,主要是防止命名空间重名的。

    好了,咱们现在就去嗨一下 Mini API吧。

    此处假设你已经安装好 VS 2022 和 .NET 6。在创建新项目时选择空白 ASP.NET Core 应用程序。用空白项目方便稍后写自己的 Code。

     

    然后输入项目名称以及存放路径。

     

    选择.net 版本号。

     

      最后,确定新创建项目。

    ---------------------------------------------------------------------------------

    项目创建后你会发现,真TM简洁了不少,Program.cs 文件中只有这么几行。

    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    
    app.MapGet("/", () => "Hello World!");
    
    app.Run();

    第一行:调用 CreateBuilder 方法创建一个builder ,这个 builder 随后用于构建 Web 应用程序。

    第二行:直接就用预设的参数构建了一个 app 对象。

    第三行:配置 HTTP 管道——怎么处理HTTP请求。此处配置表明只向客户端返回“Hello World!”。

    第四行:运行 app。

    你,不用再写 Startup 类了,也不用遵守方法签约定去写 ConfigureServices 等方法了。

    要配置服务咋办?直接 Services Add。例如

    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddControllers();
    builder.Services.AddRazorPages();
    builder.Services.AddAntiforgery()
                    .AddWebEncoders()
                    .AddSingleton<XXX, YYY>();
    var app = builder.Build();
    app.MapGet("/", () => "Hello World!");
    
    app.Run();

    看看是不是很整齐?注意这个过程是在 build 方法调用之前完成的。原因和以前 Startup 类中写方法一样,Add 服务是声明咱们的应用程序中要使用哪些功能,哪些对象被用于依赖注入,一旦 build 了,程序的功能结构就确定下来了,所以应用程序构建后就不再修改其功能了。

    以前在写 Startup 时,我们知道,还有一个 Configure 方法用来配置中间件的,也就是刚刚说的配置HTTP管道。build 方法构建 app 后,就可以直接通过这个 app 实例来配置你要 Use 的东西。例如

    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    // 中间件配置
    app.MapGet("/", () => "Hello World!");
    app.UseRouting();
    app.UseCookiePolicy();
    app.Use(async (context, next) =>
    {
        context.Response.Headers.Add("Server", "Big Bomb");
        await next();
    });
    

     app.MapControllers();
     app.MapBlazorHub();
     app.MapRazorPages();

    app.Run();

    如果我们要编写 Mini API,不需要 Add 什么 Service,也不需要 Use 什么组件,在 build 和 app.run 之间直接 MapXXX 就行了。XXX 指 HTTP 请求方法,比如 GET、POST、PUT 等。

    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    //====================================
    // Mini API 写在这里
    //====================================
    app.Run();

    相当有意思的是:这些 MapXXX 扩展方法都有一个 handler 参数,类型是 Delegate。也就是说你在调用时可以传递任何类型的委托对象。于是,就可以这样搞:

    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    
    app.UseHttpsRedirection();
    //====================================
    // Mini API 写在这里
    app.MapGet("/greet", () =>
    {
        return "What the fuck?";
    });
    //====================================
    app.Run();

    一个 Web API 就完成了。真的,可以用了,不信运行下看看。拿出秘密武器——Postman,测试一下。

    用 Postman 还是有些不够爽,而且这货现在越做越复杂,还整天叫你注册帐号,实属无趣。我们改为用 swagger 来测试。打开 Nuget 包管理器,搜索 swagger。

     安装这个包包。

    把代码改一下。

    var builder = WebApplication.CreateBuilder(args);
    // 不要忘了加这两个服务
    builder.Services.AddSwaggerGen();
    builder.Services.AddEndpointsApiExplorer();
    var app = builder.Build();
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }
    app.UseHttpsRedirection();
    //====================================
    // Mini API 写在这里
    app.MapGet("/greet", () =>
    {
        return "What the fuck?";
    });
    //====================================
    app.Run();

    这样一来,咱们在开发测试阶段就可以直接用这个组件在 Web 页上测试 API 的调用了,不再启动其他工具软件了。

    运行该项目,然后浏览器定位到 http(s)://root_url:port/swagger。

    比如,我运行后得到的URL是 https://localhost:7189,那么在浏览器中打开 https://localhost:7189/swagger。

    点击右边的“try it out”,然后点 “Execute” 按钮,就能看到执行结果了。

    怎么样,好用吧?咱们再添加一个API,这次要带参数的 POST 请求。

    app.MapPost("/submit", (string name, int age) =>
    {
        return $"你提交的内容:{name} - {age}";
    });

    这个 API 带两个参数,分别取名为 name,age。在调用时,是通过请求的 body 来提供的,格式为 JSON。

    前面老周说过,这些MapXXX方法的委托参数是 Delegate 类型,所以你可以用任意类型的委托,有参数的没参数的,有返回值的没返回值的。

    每次调试时都要在浏览器地址输入 http(s)://root/swagger 太TM不方便了,咱们可以配置一下,让其自动定位到 swagger 下。打开项目属性窗口,转到“调试”标签。

     点击页面上的“打开调试启动配置文件 UI”链接。

     把滚动条往地狱方向拉,一直拉到看到“URL”标题,在文本中填上 swagger。搞定。

    现在,你直接运行项目,就自动打开 API 列表了。点击展开 submit,再点“try it out”。

     为 name 和 age 参数填上值,执行。

    下面再举一例,请求方式为 GET,参数来自 URL 查询(即带 ? 的URL,如 /abc?t=3000)。

    app.MapGet("/md5", ([FromQuery(Name = "msg")] string data) =>
    {
        byte[] buffer = Encoding.UTF8.GetBytes(data);
        using MD5 md5ec = MD5.Create();
        byte[] comres = md5ec.ComputeHash(buffer);
        return $"加密结果:{Convert.ToHexString(comres).ToLower()}";
    });

    这个 API 的功能:接收一个字符串类型的对象,对其作 MD5 运算,然后返回结果。这个请求是从查询字符串中得到参数 data 的值的,所以要加上 FromQuery 特性,而且,实际传值时查询参数的名称与API的参数名称不同,故要用 Name = .... 明确指定,要从 msg 查询参数中提取值。

    综上,此API的调用方式为 GET /md5?msg=呵呵哈哈呵呵哈

    咱们玩这一步了,你心中一定有个高大上的疑问:这货支持依赖注入乎?

    很好,老周也有此疑问,要不,咱们搞搞看。

    public interface IComputer
    {
        int RunIt(int x, int y, int z);
    }
    
    internal class ComputerService : IComputer
    {
        public int RunIt(int x, int y, int z)
        {
            return x - y - z;
        }
    }

    我定义了一个服务接口,里面有个 RunIt 方法;然后俺实现之。逻辑很简单,x、y、z 三数相减。

    接下来改代码,在 build 方法调用之前注册服务,咱们就注册个单实例模式吧,全局共享一个实例。

    var builder = WebApplication.CreateBuilder(args);
    ……
    builder.Services.AddSingleton<IComputer, ComputerService>();

    然后,我们完成 API。

    app.MapPost("/comp", (int n1, int n2, int n3, IComputer compsv) =>
    {
        int r = compsv.RunIt(n1, n2, n3);
        return $"计算结果:{r}";
    });

    不要犹豫,运行它!

     得到结果:

    在这个API中,n1,n2,n3 三个参数是从客户端 POST 过来的,而最后一个参数是通过依赖注入得到引用对象的。那么,把参数的位置调换一下,是否也可行呢?

    app.MapPost("/comp", (IComputer compsv, int n1, int n2, int n3) =>
    {
        int r = compsv.RunIt(n1, n2, n3);
        return $"计算结果:{r}";
    });

    然后再测,结果表明,是可行滴。

    好了,今天的话题就聊到这儿了,下次咱们就用这个 Mini API 来封装 MPD。

  • 相关阅读:
    《Geometric Deep Learning综述介绍》
    《和想象不太一样的'Graph Neural Network + Zero Shot'》
    《几何深度学习前沿》
    《【Paper Live】滴滴出行-探索资源约束的Contextual Bandits问题 & KDD Cup滴滴出行比赛解读>
    《PDP: 解决约束满足问题的神经网络架构 | 刘晶 | 集智俱乐部图网络论文读书会20190819》
    《NeuroSAT: Learning a SAT Solver from Single-Bit Supervision》
    《OR Talk NO.5 | Facebook 田渊栋:用深度(强化)学习为组合优化寻找更好的启发式搜索策略》
    《OR Talk NO.15 | 在业界实验室做AI设计师是一种什么体验?》
    超级跳马 —— 矩阵快速幂优化DP
    图SLAM:Noob的同时本地化和映射指南
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/15554631.html
Copyright © 2011-2022 走看看