.NET Core WebApi开发

1,817 阅读7分钟

序言

我们知道,一个项目分为前端和后端,前端包括很多,Web,app,小程序,Winfrom等等,即用户直观体验的页面展示,后端就是业务逻辑的处理,数据库存储等等操作,是支撑前端功能的基石。以前的项目,每个系统都一套逻辑的话,那整体项目将会难以维护,对开发人员的要求也会更高,所以才会有了前后端分离的思想。前后端分离是什么意思呢?其实也很好理解,就是前端开发负责页面的展现,后端开发负责业务处理,数据的交互就通过调用api的方式进行。

Web Api项目

首先创建新项目 image.png

此处选择启用OpenApi支持,启用后创建的模板是自带SwaggerUI的。 image.png

创建成功后我们就看到了这样的一个项目模板 image.png

目录文件介绍

lanuchSettings.json

字面意思的理解,这个文件是配置启动项的,.NET Core WebApi的项目有两种启动方式,一种是原来的IIS的启动方式,另外一种是Core版本独有的单独启动方式,可以在调试属性里进行修改,对应的就是这个文件里的属性,这里就不做过多的阐述了。

image.png

image.png

依赖项

这里是该项目所有的依赖项,包里面我们能看到该项目引用了Swagger的相关包,我们可以在此处右键能够看到引用菜单,添加项目引用的意思是把现有的类库引用进来,就能够调用其他类库的方法,管理NuGet程序包是引用官方和第三方的类库包。

image.png

如果你的NuGet包加载不出来的话,需要配置一个程序包的源,源地址改成api.nuget.org/v3/index.js…

image.png

我们这里尝试引用一个NLog的日志功能的包

image.png

弹出这个窗口就点击OK或者确定就行了,只是一个通知的窗口

image.png

引入成功后就看到包这里多了NLog的包

image.png

Controllers

熟悉MVC架构可能对这个文件夹并不陌生,这个是控制器的文件,也是WebApi最重要的文件,因为就靠控制器能够把API暴露出去供前端调用

我们先看系统自动创建的文件来说明,Route是配置api路径的前缀的,继承自ControllerBase,具体这个类的方法可以查看官方文档 ControllerBase | Microsoft Docs

image.png

想要暴露出去的api必须标记上类型,这里标记的是HttpGet意思就是Get类型的,还有其他的类型标记,具体怎么使用此处就不做过多的阐述。

image.png

appsettings.json

这个是json格式的配置文件,日常一般用作存放数据库连接地址以及一些中间件的配置等等,也可以根据不同的运行环境调用不同的配置文件,Development代表就是开发环境。

image.png

Program.cs

这是整个程序的启动入口,在.NET Core 5的版本中保留了这部分的代码,在.NET Core 6 的版本就把Program和Startup两个文件合并了,虽然更简洁了,但是使用起来就没那么舒适了。

image.png

前面我们引入的NLog的包,需要添加配置文件,这时候就需要在Program里面去添加配置文件的读取并启用Log日志

<?xml version="1.0" encoding="utf-8"?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true" internalLogLevel="Info">
	<!-- 启用.net core的核心布局渲染器 -->
	<extensions>
		<add assembly="NLog.Web.AspNetCore" />
	</extensions>
	<!-- 写入日志的目标配置 -->
	<targets>
		<!-- 调试  -->
		<target xsi:type="File" name="debug" fileName="logs/debug-${shortdate}.log" layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />
		<!-- 警告  -->
		<target xsi:type="File" name="warn" fileName="logs/warn-${shortdate}.log" layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />
		<!-- 错误  -->
		<target xsi:type="File" name="error" fileName="logs/error-${shortdate}.log" layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />
	</targets>
	<!-- 映射规则 -->
	<rules>
		<!-- 调试  -->
		<logger name="*" minlevel="Trace" maxlevel="Debug" writeTo="debug" />
		<!--跳过不重要的微软日志-->
		<logger name="Microsoft.*" maxlevel="Info" final="true" />
		<!-- 警告  -->
		<logger name="*" minlevel="Info" maxlevel="Warn" writeTo="warn" />
		<!-- 错误  -->
		<logger name="*" minlevel="Error" maxlevel="Fatal" writeTo="error" />
	</rules>
</nlog>

    public class Program
    {
        public static void Main(string[] args)
        {
            //这里添加Nlog
            var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
            try
            {
                //测试Nlog日志输出
                logger.Debug("init main");
                CreateHostBuilder(args).Build().Run();
            }
            catch (Exception exception)
            {
                logger.Error(exception, "Stopped program because of exception");
                throw;
            }
            finally
            {
                NLog.LogManager.Shutdown();
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                }).UseNLog();//启用NLog
    }

Startup.cs

这个类中最重要的就是IConfiguration和两个方法,IConfiguration是配置的属性,,ConfigureServices方法由运行时调用。使用此方法向容器中添加服务。Configure方法由运行时调用。使用此方法配置HTTP请求管道。好,记住这两个名词,容器、管道,下面会详细说说。

image.png

项目模板中的目录和文件和就介绍完了。

编程思想

在介绍容器技术之前,先要理解AOP的编程思想,我们先回顾下之前的POP和OOP思想。

POP面向过程编程

面向过程是一种以过程为中心的编程思想。、是以什么正在发生为目标进行编程
特性:模块化 流程化
优点:性能比面向对象高, 因为类调用时需要实例化,开销比较大,比较消耗资源;单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展

OOP面向对象编程

面向对象是按照认识客观世界的系统思维方式,采用基于对象的概念建立模型,模拟客观的世界分析、设计、实现软件的办法,通过封装多个类的交互完成小功能,多个功能叠加成一个模块,多个模块叠加成一个系统
特性:抽象 封装 继承 多态
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
缺点:性能比面向过程低

AOP面向切面编程

面向切面解决面向对象的静态问题,能够突破类的限制,去动态扩展类的功能,既不破环封装,又能增加功能,AOP不是取代OOP的,只是针对OOP的补充。我们在整体系统设计的时候也能够通过AOP的编程思想去实现即不去更改业务的代码,又能实现拓展功能。
简单画了个流程图,我们的项目中肯定会有这样的场景。

image.png

这里列举接口鉴权和记录日志的功能其实是不同的业务应该都是一样的,这里的日志指的是调用的日志,不是输出的日志,那这种情况如果我每个功能每个业务都要写一份吗?所以,AOP的思想就是解决这个问题的
那需要怎么实现呢?

IOC容器

  1. IOC容器就是一个工厂,负责创建对象的
  2. IOC控制反转:只是把上端对下端的依赖,换成第三方容器决定
  3. DI依赖注入:就是在构造某个对象时,能将对象依赖的东西自动的初始化进去
  4. 正是因为要实现IOC,所以才诞生了DI的技术手段
  5. DIP就是上层模块不应该依赖底层模块,它们都应该依赖于抽象,具体点是Service不应该依赖于Repository,而应该依赖于IRepository .NET Core自带了轻量级的IOC的容器,Transient、Scoped、Singleton

services.AddTransient<>():服务在每次请求时被创建,适合无状态的服务
services.AddScoped<>():服务每个请求只创建一次
services.Singleton<>():单例,只创建一次,第一次被请求的时候被创建

还有第三方的IOC容器,Autofac,由于篇幅的原因,后面单独的文章说明。
下面就来看下这三个容器如何使用的
首先我们创建测试的类和接口

public class Test:ITest
{
    public string GetTestStr(string str)
    {
        return $"Test Class GetTestStr Method:{str}";
    }
}
public interface ITest
{
    public string GetTestStr(string str);
}
public class TestTwo
{
    public string GetTestStr(string str)
    {
        return $"TestTwo Class GetTestStr Method:{str}";
    }
}

上面我们创建了两种类型的,一种是带接口实现的类,一种是单独的类,那怎么通过IOC容器实现呢?
其实也很简单,只需要在ConfigureServices方法中添加以下代码就行了

services.AddScoped<ITest, Test>();
services.AddScoped<TestTow>();

这样其实就添加成功了,那我们怎么去调用呢?
我们在控制器的构造函数中去依赖注入(DI)

[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
    private readonly ITest _test;
    private readonly TestTwo _test2;

    public TestController(ITest test, TestTwo test2)
    {
        _test = test;
        _test2 = test2;
    }

    [HttpGet("Test")]
    public string Test(string str)
    {
        return _test.GetTestStr(str);
    }

    [HttpGet("TestTwo")]
    public string TestTwo(string str)
    {
        return _test2.GetTestStr(str);
    }
}

好,那启动下看下执行的效果吧

image.png

image.png

管道

说完IOC容器,下面就是管道技术了,也就是Configure方法究竟是做什么的。
简单来说,就是从发起请求到返回结果的一个过程,在.Net Core中这里面的处理是由中间件来完成。

用户在发起请求后,系统会自动生成一个请求管道(request pipeline),在这个请求管道中,可以通过run、map和use方法来配置请求委托,而在单独的请求委托中定义的可重用的类和并行的匿名方法即为中间件,也叫做中间件组件。具体流程如图。

image.png

从上图可以看出,当发起请求后,系统会创建一个请求管道,在这个管道中,每一个中间件都会按顺序处理(可能会执行,也可能不会被执行,取决于具体的业务逻辑),等最后一个中间件处理完后,又会按照相反的方向返回最终的处理结果。

比较经典的场景呢就是调用接口的时候我们做个token的认证鉴权,认证通过后才会继续往下执行,不通过则直接返回。具体的使用后面会有文章详细介绍。

总结

该篇文章主要介绍了.NET Core WebApi项目开发中比较基本的应用,在实际开发中呢,都是这种思路来实现。