一次asp.net core3.1打造webapi开发框架的实践
aaarticlea/png;base64,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" alt="">
实践技术看点
- 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)配置文件
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"tokenManagement": {
"secret": "",
"issuer": "datacool.cnblogs.com",
"audience": "WebApi",
"accessExpiration": ,
"refreshExpiration":
},
"ConnectionStrings": {
"POS_Db": "server=localhost;userid=root;pwd=dba#2020;port=3306;database=posdb;sslmode=none"
}
}
appsettings
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<!--定义输出到文件中-->
<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
<!--定义文件存放位置-->
<param name="AppendToFile" value="true"/>
<!--最小锁定模型以允许多个进程可以写入同一个文件-->
<param name="LockingModel" value="log4net.Appender.FileAppender.MinimalLock"/>
<param name="StaticLogFileName" value="true"/>
<!--保存路径-->
<param name="File" value="Log/Logs_"/>
<param name="DatePattern" value="yyyyMMdd".txt""/>
<param name="StaticLogFileName" value="false"/>
<param name="RollingStyle" value="Date"/>
<layout type="log4net.Layout.PatternLayout">
<!--每条日志末尾的文字说明-->
<footer value=""/>
<!--输出格式-->
<!--样例:2008-03-26 13:42:32,111 [10] INFO Log4NetDemo.MainClass [(null)] - info-->
<conversionPattern value="%newline%date 级别:%-5level 日志描述:%message%newline"/>
</layout>
</appender>
<root>
<!--文件形式记录日志-->
<appender-ref ref="LogFileAppender"/>
</root>
</log4net>
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 => + (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控制器
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using log4net;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; namespace tokendemo.Controllers
{
[ApiController]
[Route("[controller]")]
[Authorize]
public class WeatherForecastController : ControllerBase
{
private readonly ILog _logger;
public WeatherForecastController()
{
_logger = LogManager.GetLogger(typeof(WeatherForecastController));
}
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
}; [HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
_logger.Info("OK");
return Enumerable.Range(, ).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-, ),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
} }
}
这个大家应该很熟悉了,这就是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
一次asp.net core3.1打造webapi开发框架的实践的更多相关文章
- 使用OAuth打造webapi认证服务供自己的客户端使用
一.什么是OAuth OAuth是一个关于授权(Authorization)的开放网络标准,目前的版本是2.0版.注意是Authorization(授权),而不是Authentication(认证). ...
- OAuth打造webapi认证服务
使用OAuth打造webapi认证服务供自己的客户端使用 一.什么是OAuth OAuth是一个关于授权(Authorization)的开放网络标准,目前的版本是2.0版.注意是Authorizati ...
- 探索Asp net core3中的 项目文件、Program.cs和通用host(译)
引言 原文地址 在这篇博客中我将探索一些关于Asp.net core 3.0应用的基础功能--.csproj 项目文件和Program源文件.我将会描述他们从asp.net core 2.X在默认模版 ...
- 使用OAuth打造webapi认证服务供自己的客户端使用(二)
在上一篇”使用OAuth打造webapi认证服务供自己的客户端使用“的文章中我们实现了一个采用了OAuth流程3-密码模式(resource owner password credentials)的W ...
- asp.net core系列 38 WebAPI 返回类型与响应格式--必备
一.返回类型 ASP.NET Core 提供以下 Web API Action方法返回类型选项,以及说明每种返回类型的最佳适用情况: (1) 固定类型 (2) IActionResult (3) Ac ...
- asp.net core 2.0 webapi集成signalr
asp.net core 2.0 webapi集成signalr 在博客园也很多年了,一直未曾分享过什么东西,也没有写过博客,但自己也是汲取着博客园的知识成长的: 这两天想着不能这么无私,最近.N ...
- ASP.NET Core 2.2 WebApi 系列【九】使用SignalR (作者:tenghao510 ) 学习及内容补充
原文地址: ASP.NET Core 2.2 WebApi 系列[九]使用SignalR 今天,看到了大牛的这篇博文, 发了一下评论, 我很惊喜, 没想到他很快就回复了我, 而且通过QQ帮助了S ...
- ASP.NET Core3.1使用IdentityServer4中间件系列随笔(三):创建使用[ClientCredentials客户端凭证]授权模式的客户端
配套源码:https://gitee.com/jardeng/IdentitySolution 上一篇<ASP.NET Core3.1使用IdentityServer4中间件系列随笔(二):创建 ...
- ASP.NET Core3.1使用IdentityServer4中间件系列随笔(二):创建API项目,配置IdentityServer保护API资源
配套源码:https://gitee.com/jardeng/IdentitySolution 接上一篇<ASP.NET Core3.1使用IdentityServer4中间件系列随笔(一):搭 ...
随机推荐
- Vue 路由规则--传参数
1,query方法去获取参数 <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...
- HDU 1372
题意:模拟国际象棋马的走棋方式,和中国象棋一样马走日,8X8的棋盘,问从起点到终点的最短步数,国际象棋中数字代表行row,字母代表列column, 思路:记忆化深搜. #include<cstd ...
- thinkphp3.2如何自动生成后台模块并且怎么访问
https://blog.csdn.net/whulovely/article/details/72773729 步骤一:在入口文件index.php下绑定Admin模块(这时候别再Home同级手动创 ...
- Linux 字节序
小心不要假设字节序. PC 存储多字节值是低字节为先(小端为先, 因此是小端), 一些高 级的平台以另一种方式(大端)工作. 任何可能的时候, 你的代码应当这样来编写, 它不在 乎它操作的数据的字节序 ...
- 【50.40%】【BZOJ 4553】[Tjoi2016&Heoi2016]序列
Time Limit: 20 Sec Memory Limit: 128 MB Submit: 371 Solved: 187 [Submit][Status][Discuss] Descript ...
- Recall(召回率);Precision(准确率);F1-Meature(综合评价指标);true positives;false positives;false negatives..
转自:http://blog.csdn.net/t710smgtwoshima/article/details/8215037 Recall(召回率);Precision(准确率);F1-Meat ...
- How to output the target message in dotnet build command line
How can I output my target message when I using dotnet build in command line. I use command line to ...
- PDF.JS 读取文件流前端展示 C#
最近再搞PDF得展示问题,因为aspose.pdf成本太高,只能使用pdf.js这个开源强大的前端东东了. 在百度了很久后 网上大都是node,java,php的事例,有位大哥的是C#的后台代码按他写 ...
- 不同RAM空间存储变量区分
- Elasticsearch介绍和安装
Elasticsearch介绍和安装 软件包: 链接:https://pan.baidu.com/s/1O_C0JQGfF8sC_OtcCCLNoQ 提取码:3iai 1.1.简介 1.1.1.Ela ...