zoukankan      html  css  js  c++  java
  • net core3.1打造webapi开发框架的实践

    https://www.cnblogs.com/datacool/p/datacool_dotnetcore_demo.html

    实践技术看点

    • 1、Swagger管理API说明文档
    • 2、JwtBearer token验证
    • 3、Swagger UI增加Authentication
    • 4、EntityFrameworkCore+MySQL
    • 5、在.net core 3.1下使用Log4net

    前言

    元旦过后就没什么工作上的任务了,这当然不能让领导看在眼里,动手实践一下新技术吧。于是准备搭一个webapi的中间件框架。

    由于自己的云主机是台linux服务器,双核2G的centos+1M 没有数据盘,也用不起RDS,如果装个Windows Server那么肯定卡的不行,所以一直想尝试一下跨平台的感觉。

    由于这篇随笔不是定位于教程,故基础知识一概略过。

    项目中使用到的包清单

    复制代码
     <ItemGroup>
        <PackageReference Include="IdentityModel" Version="4.1.1" />
        <PackageReference Include="log4net" Version="2.0.8" />
        <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.1" />
        <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="3.1.1" />
        <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.1">
          <PrivateAssets>all</PrivateAssets>
          <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
        <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.1" />
        <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.0" />
        <PackageReference Include="MySql.Data.EntityFrameworkCore" Version="8.0.19" />
        <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.0" />
        <PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="5.0.0" />
        <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.0.0" />
        <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.0.0" />
      </ItemGroup>
    复制代码

    关键代码点评

    1)Startup

    复制代码
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Threading.Tasks;
    using IdentityModel;
    using Microsoft.AspNetCore.Authentication.JwtBearer;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Logging;
    using Microsoft.IdentityModel.Tokens;
    using Microsoft.OpenApi.Models;
    using MySql.Data.EntityFrameworkCore.Extensions;
    using Swashbuckle.AspNetCore.Swagger;
    using tokendemo.Models;
    
    namespace tokendemo
    {
        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();
                services.Configure<TokenManagement>(Configuration.GetSection("tokenManagement"));
                var token = Configuration.GetSection("tokenManagement").Get<TokenManagement>();
                services.AddAuthentication(x =>
                {
                    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                }).AddJwtBearer(x =>
                {
                    x.RequireHttpsMetadata = false;
                    x.SaveToken = true;
                    x.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(token.Secret)),
                        ValidIssuer = token.Issuer,
                        ValidAudience = token.Audience,
                        ValidateIssuer = false,
                        ValidateAudience = false
                    };
                });
                services.AddSwaggerGen(c =>
                {
                    c.SwaggerDoc("v1",
                        new OpenApiInfo
                        {
                            Title = "XXX项目接口文档",
                            Version = "v1",
                            Contact = new OpenApiContact
                            {
                                Email = "xyf_xiao@cquni.com",
                                Name = "肖远峰",
                                Url = new Uri("http://datacool.cnblogs.com")
                            }
                        });
                    // 为 Swagger 设置xml文档注释路径
                    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                    c.IncludeXmlComments(xmlPath);
                    c.AddSecurityDefinition("Bearer",
                        new OpenApiSecurityScheme
                        {
                            Description = "请输入OAuth接口返回的Token,前置Bearer。示例:Bearer {Roken}",
                            Name = "Authorization",
                            In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                            Type = SecuritySchemeType.ApiKey
                        });
                    c.AddSecurityRequirement(new OpenApiSecurityRequirement
                    {
                       {
                            new OpenApiSecurityScheme
                            {
                                Reference = new OpenApiReference()
                                {
                                    Id = "Bearer",
                                    Type = ReferenceType.SecurityScheme
                                }
                            }, Array.Empty<string>()
                        }
                    });
                });           
                var posdbConnString = Configuration.GetConnectionString("POS_Db");
                services.AddDbContext<posdbContext>(option =>
                {
                    option.UseMySql(posdbConnString, null);
                });
                services.AddScoped<IAuthenticateService, TokenAuthenticationService>();
                services.AddScoped<IUserService, UserService>();
    
            }
    
            // 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.UseHttpsRedirection();
                app.UseAuthentication();
                app.UseRouting();
                app.UseAuthorization();
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                });
                app.UseSwagger();
                //启用中间件服务生成SwaggerUI,指定Swagger JSON终结点
                app.UseSwaggerUI(c =>
                {
                    c.SwaggerEndpoint("/swagger/v1/swagger.json", "XXX接口文档 V1");
                    c.RoutePrefix = string.Empty;//设置根节点访问
                });
                app.UseLog4net();
            } 
        }
    }
    复制代码
    复制代码
    using Microsoft.AspNetCore.Builder;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Threading.Tasks;
    
    namespace tokendemo
    {
        public static class LoggeServiceExt
        {
    
            /// 使用log4net配置
            /// <param name="app"></param>
            /// <returns></returns>
            public static IApplicationBuilder UseLog4net(this IApplicationBuilder app)
            {
                var logRepository = log4net.LogManager.CreateRepository(Assembly.GetEntryAssembly(), typeof(log4net.Repository.Hierarchy.Hierarchy));
                log4net.Config.XmlConfigurator.Configure(logRepository, new FileInfo("log4net.config"));
                return app;
            }
        }
    }
    复制代码
    复制代码
    public interface IUserService
        {
            bool IsValid(LoginRequestDTO req);
        }
    
     public interface IAuthenticateService
        {
            bool IsAuthenticated(LoginRequestDTO request, out string token);
        }
    
    public class UserService : IUserService
        {
            public bool IsValid(LoginRequestDTO req)
            {
                return true;
            }
        }
    
     public class TokenAuthenticationService : IAuthenticateService
        {
            private readonly IUserService _userService;
            private readonly TokenManagement _tokenManagement;
            private readonly posdbContext db;
            public TokenAuthenticationService(IUserService userService, IOptions<TokenManagement> tokenManagement, posdbContext posdb)
            {
                _userService = userService;
                _tokenManagement = tokenManagement.Value;
                db = posdb;
            }
    
            public string GetAuthentUser()
            {
                return JsonConvert.SerializeObject(db.SysApiAuthorize.ToList());
            }
    
            public bool IsAuthenticated(LoginRequestDTO request, out string token)
            {
                token = string.Empty;
                if (!_userService.IsValid(request))
                    return false;
                var claims = new[]
                {
                   new Claim(ClaimTypes.Name,request.Username)
                };
                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenManagement.Secret));
                var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
                var jwtToken = new JwtSecurityToken(_tokenManagement.Issuer, _tokenManagement.Audience, claims, expires: DateTime.Now.AddMinutes(_tokenManagement.AccessExpiration), signingCredentials: credentials);
    
                token = new JwtSecurityTokenHandler().WriteToken(jwtToken);
                return true;
            }
        }
    复制代码

    token验证是我关注的重点,而Swagger支持查看文档的同时调用API,也支持授权认证,所以水到渠成。代码命名都是比较规范的,当然大部分来源于别人的文章,这里就不作过多说明了。

    asp.net core对依赖注入思想是贯彻始终的,新人需要在这个思想的领悟上下苦功夫才能驾驭她。

    2)配置文件

     appsettings
     log4net.config

    Scaffold-DbContext "server=localhost;userid=root;pwd=dba#2020;port=3306;database=posdb;sslmode=none;" Pomelo.EntityFrameworkCore.MySql -OutputDir Models -Force

    由于我的数据库是先存在了,所以直接使用了nutget控制台生成了数据库上下文对象和实体。注意向导生成的数据库上下文里是把数据库连接字符串写死的,需要修改。本例是写入appsettings.json里的。请重点看一下上面的配置和Startup里获取配置的代码。

    3)关联代码,几个数据传输类

    复制代码
    public class TokenManagement
        {
            [JsonProperty("secret")]
            public string Secret { get; set; }
    
            [JsonProperty("issuer")]
            public string Issuer { get; set; }
    
            [JsonProperty("audience")]
            public string Audience { get; set; }
    
            [JsonProperty("accessExpiration")]
            public int AccessExpiration { get; set; }
    
            [JsonProperty("refreshExpiration")]
            public int RefreshExpiration { get; set; }
        }
    复制代码
    复制代码
    public class WeatherForecast
        {
            public DateTime Date { get; set; }
    
            public int TemperatureC { get; set; }
    
            public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    
            public string Summary { get; set; }
        }
    复制代码
    复制代码
     public class LoginRequestDTO
        {
            [Required]
            [JsonProperty("username")]
            public string Username { get; set; }
    
    
            [Required]
            [JsonProperty("password")]
            public string Password { get; set; }
        }
    复制代码

    3)API控制器

    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Threading.Tasks;
     5 using log4net;
     6 using Microsoft.AspNetCore.Authorization;
     7 using Microsoft.AspNetCore.Mvc;
     8 
     9 namespace tokendemo.Controllers
    10 {
    11     [ApiController]
    12     [Route("[controller]")]
    13     [Authorize]
    14     public class WeatherForecastController : ControllerBase
    15     {
    16         private readonly ILog _logger; 
    17         public WeatherForecastController()
    18         {
    19             _logger = LogManager.GetLogger(typeof(WeatherForecastController)); 
    20         }
    21         private static readonly string[] Summaries = new[]
    22         {
    23             "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    24         };
    25 
    26 
    27         [HttpGet]
    28         public IEnumerable<WeatherForecast> Get()
    29         {
    30             var rng = new Random();
    31             _logger.Info("OK");
    32             return Enumerable.Range(1, 5).Select(index => new WeatherForecast
    33             {
    34                 Date = DateTime.Now.AddDays(index),
    35                 TemperatureC = rng.Next(-20, 55),
    36                 Summary = Summaries[rng.Next(Summaries.Length)]
    37             })
    38             .ToArray();
    39         }
    40  
    41     }
    42 }
    复制代码

    这个大家应该很熟悉了,这就是vs2019向导创建的API控制器。[Authorize]标记会导致401错误,就是表示先要去获取access token,在Header里带入Bearer+空格+token才可以正常调用。

    授权控制器

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    
    namespace tokendemo.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class AuthenticationController : ControllerBase
        {
            private readonly IAuthenticateService _authService;
            public AuthenticationController(IAuthenticateService service)
            {
                _authService = service;
            }
    
            [AllowAnonymous]
            [HttpPost, Route("requestToken")]
            public ActionResult RequestToken([FromBody] LoginRequestDTO request)
            {
                if (!ModelState.IsValid)
                {
                    return BadRequest("Invalid Request");
                }
                string token;
                var authTime = DateTime.UtcNow;
                if (_authService.IsAuthenticated(request, out token))
                {
                    return Ok(new 
                    {
                        access_token = token,
                        token_type = "Bearer",
                        profile = new
                        {
                            sid = request.Username, 
                            auth_time = new DateTimeOffset(authTime).ToUnixTimeSeconds()                       
                        }
                    });
                }
                return BadRequest("Invalid Request");
            }
        }
    }
    复制代码

    收获与感想

    • 1、妥妥的吃了次螃蟹,收获了经验
    • 2、正在“为自己挖一口井”的路上
    • 3、.net core算是入门了
    • 4、源码我是没自信放到github的,后面会加上下载链接
    • 5、伙计们分享起来吧,这个生态建设任重而道远啊。

    这里是源码下载的地址:https://files.cnblogs.com/files/datacool/tokendemo.zip

    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    "作者:" 数据酷软件工作室
    "出处:" http://datacool.cnblogs.com
  • 相关阅读:
    "Emgu.CV.CvInvoke”的类型初始值设定项引发异常 解决办法
    EmguCV(OpenCV)实现高效显示视频(YUV)叠加包括汉字
    yuv420p转为emgucv的图像格式Emgu.CV.Image<Bgr, Byte>
    Emgu.CV/opencv 绘图 线面文字包括中文
    5.9 HTML5 新增表单控件 ---不是特别重要
    5.8 HTML5新结构标签
    5.8 HTML5新结构标签 ---不是特别重要
    5.7 CSS浏览器前缀
    5.6 CSS3 animation动画
    5.5 CSS3 transform变换
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/12441920.html
Copyright © 2011-2022 走看看