最近计划开始搭建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()); } }
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文档文件, 路径使用相对路径(如:..\XmlDoc\APIStarter.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); } } }