web api 起步之一

最近计划开始搭建asp.net core 的web api环境,我想把实验过程中的一些主要关键点记录下来,以备将来时间长了忘记的时候还有地方可以参考.

开始之前,我们需要建立以下的概念:

1.ORM  如dapper

2.面向接口 interface driver design

3.控制反转Ioc与依赖注入DI 如Autofac 微软自带的DI,在实验中,我们用Autofac来替代DI

一  开发环境

VS2019 + .net core 3.1 sdk

二 建立项目

1. asp.net core web 项目

2. web api

3. 启用docker支持

三  添加swagger支持

1. Nuget 中加入以下package

    Swashbuckle.AspNetCore.Swagger

    Swashbuckle.AspNetCore.SwaggerGen

    Swashbuckle.AspNetCore.SwaggerUI

    Microsoft.Extensions.PlatformAbstractions(非必须)

2. 添加并配置 Swagger 中间件

  1) 重点在于startup.cs里面的配置。

      参考文章:ASP.NET Core WebApi使用Swagger生成api说明文档看这篇就够了 

  2) 如果controller中的某个action不想显示 出来,标示attribute[NonAction]

  3) 如果整 个Controller都不想显示,标示attribute[ApiexplorerSettings(Ignore=true)]

四 新建一个.net standard 类库(.net standard 类库)

 1.名称:APIStarter.Service

 2. 新建文件夹:  IService  并建立一个接口文件 ILoginService.cs;

 3. 新建文件夹: Service 并建立一个类LoginService.cs 实现上面的接口,关注ALT+Enter键的使用,会带来很多方便。

五 用Autofac 来替代自带的DI

1 Nuget 安装 包 : 

   a. Autofac    b. Autofac.Extensions.DependencyInjection  c.Zq.SQLBuilder.Core(封装了对多种数据库的dapper操作)

2 参考文章

   NET Core 3.0 AutoFac替换内置DI的新姿势  

3 重点代码(注释的代码是.net core 2.x的写法)

  startup.cs   

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Microsoft.Extensions.Logging;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using Microsoft.Extensions.PlatformAbstractions;
using Autofac;
using SQLBuilder.Core.Configuration;
using SQLBuilder.Core.Repositories;
using System.Reflection;
using Autofac.Extensions.DependencyInjection;

namespace APIStarter
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        //This method gets called by the runtime.Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            //swagger依赖于MVC
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
            //添加 Swagger 生成器
            services.AddSwaggerGen(option =>
            {
                option.SwaggerDoc("v1", new OpenApiInfo()
                {
                    Title = "Martin API",
                    Version = "v1.0",
                    Contact = new OpenApiContact() { Name = "湖东", Email = "36323732@qq.com" }
                });

                var basePath = PlatformServices.Default.Application.ApplicationBasePath;
                var xmlPath = Path.Combine(basePath, "APIStarter.xml");
                option.IncludeXmlComments(xmlPath, true);
            });
        }

//public IServiceProvider ConfigureServices(IServiceCollection services) //{ // services.AddControllers(); // //swagger依赖于MVC // services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0); // //添加 Swagger 生成器 // services.AddSwaggerGen(option => // { // option.SwaggerDoc("v1", new OpenApiInfo() // { // Title = "Martin API", // Version = "v1.0", // Contact = new OpenApiContact() { Name = "湖东", Email = "36323732@qq.com" } // }); // var basePath = PlatformServices.Default.Application.ApplicationBasePath; // var xmlPath = Path.Combine(basePath, "APIStarter.xml"); // option.IncludeXmlComments(xmlPath, true); // }); // return RegisterAutofac.ForRegisterAutofac(services); //} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); //app.UseMvc(); //启用中间件服务生成Swagger作为JSON终结点 app.UseSwagger(); //启用中间件服务对swagger-ui,指定Swagger JSON终结点 app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Martin API V1"); c.RoutePrefix = "api"; }); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } public void ConfigureContainer(ContainerBuilder builder) { //添加依赖注入关系 builder.RegisterModule(new AutofacModuleRegister()); var controllerBaseType = typeof(ControllerBase); //在控制器中使用依赖注入 builder.RegisterAssemblyTypes(typeof(Program).Assembly) .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType) .PropertiesAutowired(); } }
public class AutofacModuleRegister : Autofac.Module
    {       
        protected override void Load(ContainerBuilder builder)
        {

            //程序集范围注入
            Assembly assembly = GetAssemblyByName("APIStarter.Service");
            builder.RegisterAssemblyTypes(assembly)
                 .Where(t => t.Name.EndsWith("Service"))
                 .AsImplementedInterfaces().PropertiesAutowired();

            

               //加载配置文件这一段代码很重要,否则无法读取到appsettings.json中的配置信息
               var basePath = PlatformServices.Default.Application.ApplicationBasePath; 
               var configPath = Path.Combine(basePath, "appsettings.json");
               ConfigurationManager.SetConfigurationFile(configPath);

            //循环注册多个数据库
            var ConnectionStrings = ConfigurationManager.Configuration.GetSection("ConnectionStrings").GetChildren();
            foreach(var item in ConnectionStrings)
            {
                var split = item.Key.Split('#');
                string DBType = split[0].ToUpper();
                string DBName = split[1].ToUpper();
                switch(DBType)
                {
                    case "SQLSERVER":
                        builder.Register(c=>new SqlRepository(item.Value)).Named<IRepository>(DBName);
                        break;
                    case "ORACLE":
                        builder.Register(c=>new OracleRepository(item.Value)).Named<IRepository>(DBName);
                        break;
                    case "MYSQL":
                        builder.Register(c => new MySqlRepository(item.Value)).Named<IRepository>(DBName);
                        break;
                    default:
                        break;
                }
            }

            //单个注册
            //builder.RegisterType<EmployeeSercice>().As<IEmployeeService>().PropertiesAutowired();

            //在控制器中使用属性依赖注入,其中注入属性必须标注为public
            //var controllersTypesInAssembly = typeof(Startup).Assembly.GetExportedTypes()
            //.Where(type => typeof(Microsoft.AspNetCore.Mvc.ControllerBase).IsAssignableFrom(type)).ToArray();
            //builder.RegisterTypes(controllersTypesInAssembly).PropertiesAutowired();
        }

        /// <summary>
        /// 根据程序集名称获取程序集
        /// </summary>
        /// <param name="AssemblyName">程序集名称</param>
        public static Assembly GetAssemblyByName(String AssemblyName)
        {
            return Assembly.Load(AssemblyName);
        }
    }

  program.cs (注释的代码是没有使用 Autofac之前的代码)

//public static IHostBuilder CreateHostBuilder(string[] args) =>
        //    Host.CreateDefaultBuilder(args)
        //        .ConfigureWebHostDefaults(webBuilder =>
        //        {
        //            webBuilder.UseStartup<Startup>();
        //        });
        public static IHostBuilder CreateHostBuilder(string[] args) =>
           Host.CreateDefaultBuilder(args)
               .ConfigureWebHostDefaults(webBuilder =>
               {
                   webBuilder.UseStartup<Startup>();
               })
      .UseServiceProviderFactory(new AutofacServiceProviderFactory());
    }
}

 六  Swagger下简单的给WebApi分组

1. Startup.cs下ConfigureServices代码,这里主要在DocInclusionPredicate控制输出那些api。

//添加 Swagger 生成器
            services.AddSwaggerGen(option =>
            {
                option.SwaggerDoc("V1", new OpenApiInfo()
                {
                    Title = "Martin API",
                    Description = "Default",
                    Version = "v1.0",
                    Contact = new OpenApiContact() { Name = "湖东", Email = "36323732@qq.com" }

                });

                option.SwaggerDoc("Auth",new OpenApiInfo() { 
                   Title = "Auth API",
                   Description = "Auth API",
                   Version = "v1.0",
                   Contact = new OpenApiContact() { Name = "湖东", Email = "36323732@qq.com" }
                });

                option.SwaggerDoc("Login", new OpenApiInfo()
                {
                    Title = "Login API",
                    Description = "Login API",
                    Version = "v1.0",
                    Contact = new OpenApiContact() { Name = "湖东", Email = "36323732@qq.com" }
                });

                option.DocInclusionPredicate((docName, apiDesc) =>
                {
                    if (!apiDesc.TryGetMethodInfo(out MethodInfo methodInfo)) return false;
                    var versions = methodInfo.DeclaringType
                       .GetCustomAttributes(true)
                       .OfType<ApiExplorerSettingsAttribute>()
                       .Select(attr => attr.GroupName);
                    if(docName.ToLower()=="v1" && versions.FirstOrDefault()==null)
                    {
                        return true;
                    }
                    return versions.Any(v => v.ToString() == docName);
                });

                var basePath = PlatformServices.Default.Application.ApplicationBasePath;
                var xmlPath = Path.Combine(basePath, "APIStarter.xml");
                option.IncludeXmlComments(xmlPath, true);
            });

2.  Startup.cs下Configure代码

 //启用中间件服务生成Swagger作为JSON终结点
            app.UseSwagger(c=>
            {
                c.RouteTemplate = "swagger/{documentName}/swagger.json";
            
            });
            //启用中间件服务对swagger-ui,指定Swagger JSON终结点
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/V1/swagger.json", "Martin API V1");
                c.SwaggerEndpoint("/swagger/Auth/swagger.json", "认证服务:Auth API ");
                c.SwaggerEndpoint("/swagger/Login/swagger.json", "登录服务: Login API");

                c.RoutePrefix = "api";
            });

3. 给Controllers或Action添加[ApiExplorerSettings(GroupName= "ApiGroupName")]

    [ApiExplorerSettings(GroupName = "Auth")]
    [ApiController]
    [Route("api/[controller]")]
    public class AuthController : BaseController

 七. 添加Area 区域(为了方便API的管理)

1. 新建目录Areas

2. 在Area上右键,新建区域

3. 删除 Data/Models/Views目录,保留Controllers目录

4. startup.cs 文件中的Configure方法

//启用Areas
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                 name: "default",
                 pattern: "{controller=Auth}/{action=Login}/{id?}");

                endpoints.MapControllerRoute(
                  name: "Area",
                  pattern: "{area:exists}/{controller=Auth}/{action=Login}/{id?}");
            });

八. swagger增强:从appsettings.json中读取配置信息

1.修改生成xml文件的路径

   在每个项目上点右键--属性--生成--勾选XML文档文件, 路径使用相对路径(如:..XmlDocAPIStarter.xml)

  新建一个Class: SwaggerConfig.cs  与 appsettings.json中的配置信息相对应

using System;
using System.Collections.Generic;
using System.Text;

namespace APIStarter.Dtos.Common
{
    public class SwaggerConfig
    {
        public string XmlDocPath { get; set; }
        public Dictionary<string,string> ApiDoc { get; set; }
    }
}

2.appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "SqlServer#DB_USER": "Server=192.168.1.5;Database=Study;uid=sa;pwd=g3@py3hd",
    "Oracle#DB_WMS": "",
    "Oracle#DB_COST": ""
  },
  "SwaggerConfig": {
    "XmlDocPath": "..\XmlDoc",
    "ApiDoc": {
      "AuthManager": "认证管理",
      "UserManager": "用户管理"
    }
  }
}

3.Program.cs 

 public static IHostBuilder CreateHostBuilder(string[] args)
        {
            var basePath = PlatformServices.Default.Application.ApplicationBasePath;
            var Configuration = new ConfigurationBuilder().SetBasePath(basePath)
                .AddJsonFile("appsettings.json")
                .Build();
            return Host.CreateDefaultBuilder(args)
                 .ConfigureWebHostDefaults(webBuilder =>
                  {
                      webBuilder.UseConfiguration(Configuration);   
                      webBuilder.UseStartup<Startup>();
                  })
                 .UseServiceProviderFactory(new AutofacServiceProviderFactory());

        }

4. startup.cs  也要做调整

(a) ConfigureServices

//添加 Swagger 生成器 (从appsettings.json中配置)
            services.AddSwaggerGen(option =>
            {
                var SwaggerConfig = Configuration.GetSection("SwaggerConfig").Get<SwaggerConfig>();
                foreach( var item in SwaggerConfig.ApiDoc)
                {
                    option.SwaggerDoc(item.Key, new OpenApiInfo()
                    {
                        Version = item.Value,
                        Title   = "API_"+item.Value,                      
                        Contact = new OpenApiContact() { Name = "湖东", Email = "36323732@qq.com" }
                    });
                }                           

                option.DocInclusionPredicate((docName, apiDesc) =>
                {
                    if (!apiDesc.TryGetMethodInfo(out MethodInfo methodInfo)) return false;
                    var versions = methodInfo.DeclaringType
                       .GetCustomAttributes(true)
                       .OfType<ApiExplorerSettingsAttribute>()
                       .Select(attr => attr.GroupName);
                    if (docName.ToLower() == "v1" && versions.FirstOrDefault() == null)
                    {
                        return true;
                    }
                    return versions.Any(v => v.ToString() == docName);
                });

                var basePath = PlatformServices.Default.Application.ApplicationBasePath;
                new DirectoryInfo(SwaggerConfig.XmlDocPath).GetFiles().ToList().ForEach(c=>
                {
                    var xmlFile = c.FullName;
                    option.IncludeXmlComments(xmlFile, true);
                });

            });

(b) Configure

 var SwaggerConfig = Configuration.GetSection("SwaggerConfig").Get<SwaggerConfig>();
            //启用中间件服务对swagger-ui,指定Swagger JSON终结点 (从配置文件中读取)
            app.UseSwaggerUI(c =>
            {
                foreach(var item in SwaggerConfig.ApiDoc)
                {
                    c.SwaggerEndpoint($"/swagger/{item.Key}/swagger.json", item.Value);
                }
                c.RoutePrefix = "api";
            });

(3) AutofacModuleRegister :因为在内部要读取配置文件,所以在构造函数中注入IConfiguration

public class AutofacModuleRegister : Autofac.Module
    {
        private readonly IConfiguration Configuration;

        //因为在内部要读取配置文件,所以在构造函数中注入IConfiguration
        public AutofacModuleRegister(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        protected override void Load(ContainerBuilder builder)
        {

            //程序集范围注入
            Assembly assembly = GetAssemblyByName("APIStarter.Service");
            builder.RegisterAssemblyTypes(assembly)
                 .Where(t => t.Name.EndsWith("Service"))
                 .AsImplementedInterfaces().PropertiesAutowired();

            //加载配置文件这一段代码要注意
            //循环注册多个数据库
            var ConnectionStrings = Configuration.GetSection("ConnectionStrings").Get<Dictionary<string, string>>();
            foreach (var item in ConnectionStrings)
            {
                var split = item.Key.Split('#');
                string DBType = split[0].ToUpper();
                string DBName = split[1].ToUpper();
                switch(DBType)
                {
                    case "SQLSERVER":
                        builder.Register(c=>new SqlRepository(item.Value)).Named<IRepository>(DBName);
                        break;
                    case "ORACLE":
                        builder.Register(c=>new OracleRepository(item.Value)).Named<IRepository>(DBName);
                        break;
                    case "MYSQL":
                        builder.Register(c => new MySqlRepository(item.Value)).Named<IRepository>(DBName);
                        break;
                    default:
                        break;
                }
            }

            //单个注册
            //builder.RegisterType<EmployeeService>().As<IEmployeeService>().PropertiesAutowired();

        }

        /// <summary>
        /// 根据程序集名称获取程序集
        /// </summary>
        /// <param name="AssemblyName">程序集名称</param>
        public static Assembly GetAssemblyByName(String AssemblyName)
        {
            return Assembly.Load(AssemblyName);
        }
    }
}
原文地址:https://www.cnblogs.com/yuchsheng/p/13972668.html