diff --git "a/\346\235\250\345\256\207\346\226\214/note-2021-06-09(\347\254\254\345\215\201\350\212\202\350\257\276 vue Cli \345\256\211\350\243\205 ).md" "b/\346\235\250\345\256\207\346\226\214/note-2021-06-09(\347\254\254\345\215\201\350\212\202\350\257\276 vue Cli \345\256\211\350\243\205 ).md" index 41581f94ac00290ebd705a216780290105b4134c..d327566c6e35340cf96addcd6ac7015bff010a01 100644 --- "a/\346\235\250\345\256\207\346\226\214/note-2021-06-09(\347\254\254\345\215\201\350\212\202\350\257\276 vue Cli \345\256\211\350\243\205 ).md" +++ "b/\346\235\250\345\256\207\346\226\214/note-2021-06-09(\347\254\254\345\215\201\350\212\202\350\257\276 vue Cli \345\256\211\350\243\205 ).md" @@ -5,10 +5,17 @@ ``` npm i -g yarn ``` -2. 安装yarn淘宝镜像 +2. 安装淘宝镜像 ``` +npm npm config set registry https://registry.npm.taobao.org yarn config set registry https://registry.npm.taobao.org/ ``` +``` +配置后可通过下面方式来验证是否成功 +npm config get registry +或 +npm info express +``` 3. 安装 vue cli ``` npm install -g @vue/cli diff --git "a/\346\235\250\345\256\207\346\226\214/note-2021-06-23(web API\347\254\254\344\272\214\350\212\202).md" "b/\346\235\250\345\256\207\346\226\214/note-2021-06-23(web API\347\254\254\344\272\214\350\212\202).md" new file mode 100644 index 0000000000000000000000000000000000000000..7606d3fa31721ebdd6d6fcdf6778c991c7f51fcc --- /dev/null +++ "b/\346\235\250\345\256\207\346\226\214/note-2021-06-23(web API\347\254\254\344\272\214\350\212\202).md" @@ -0,0 +1,138 @@ +# 2021-6-23 web API 第二节 + ++ dotnet build 检查 + ++ Prettier - Code formatter 格式化插件名 ++ RESTClient 一款自动化测试REST API的工具,它可以自动化测试RESTful API并生成精美的测试报告,同时基于测试过的历史API,可以生成精美的RESTful API文档。 + ++ dynamic的出现让C#具有了弱语言类型的特性。编译器在编译的时候不再对类型进行检查,编译期默认dynamic对象支持你想要的任何特性。比如,即使你对GetDynamicObject方法返回的对象一无所知,编译器也不会报错 + +1. 在项目内创建Entity文件夹 +``` +User.cs +using System; + +namespace myApi.Api.Entity +{ + public class User + { + public User(){ + createTime=DateTime.Now; + UpdateTime=DateTime.Now; + } + public int Id { get; set; } + + public string Username { get; set; } + + public string Password { get; set; } + + public DateTime createTime { get; set; } + + public DateTime UpdateTime { get; set; } + + public string Remark { get; set; } + } +} +``` +2. 在项目下Controllers 下创建 UserConteroller.cs +``` +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using myApi.Api.Entity; + +namespace myApi.Api.Controllers +{ + [ApiController] + [Route("[controller]")] + public class UserController : ControllerBase + { + [HttpGet] + public IEnumerable Get() + { + var user = GetUsers(); + return user; + } + + [HttpGet("{id}")] + public dynamic Get(int id) + { + var users = GetUsers(); + var user = users.Where(x => x.Id == id).FirstOrDefault(); + return user; + } + + [HttpPost] + public dynamic Post(dynamic model) + { + return new{ + Code=1000, + Data=model, + Msg="创建成功" + }; + } + + [HttpPut("{id}")] + public dynamic Put(int id,dynamic model) + { + return new{ + Code=1000, + Data=model, + Msg=string.Format("你修改的id为:{0},修改成功",id) + }; + } + + [HttpDelete("{id}")] + public dynamic Delete(int id) + { + return new{ + Code=1000, + Msg=string.Format("你删除的id为:{0},删除成功",id) + }; + } + + + + private IEnumerable GetUsers() + { + var user = + new List { + new User { Id = 1, Username = "root", Password = "123" }, + new User { Id = 2, Username = "admin", Password = "123" } + }; + return user; + } + } +} +``` +3. RESTClient +``` +api.http +### 获取用户 + +GET http://localhost:5000/User HTTP/1.1 + +###获取指定id用户 +GET http://localhost:5000/User/1 HTTP/1.1 + +###创建用户 +POST http://localhost:5000/User HTTP/1.1 +Content-Type:application/json + +{ + "username":"123", + "password":"1113" +} + +###修改指定用户 +PUT http://localhost:5000/User/1 HTTP/1.1 +Content-Type: application/json + +{ + "username":"123", + "password":"1113" +} + +###删除指定用户 +DELETE http://localhost:5000/User/1 HTTP/1.1 +``` diff --git "a/\346\235\250\345\256\207\346\226\214/note-2021-06-26(web APl\347\254\254\344\270\211\350\212\202\350\257\276).md" "b/\346\235\250\345\256\207\346\226\214/note-2021-06-26(web APl\347\254\254\344\270\211\350\212\202\350\257\276).md" new file mode 100644 index 0000000000000000000000000000000000000000..5e4a8ed343e2da212b4c95351a85f5e7732d4a5f --- /dev/null +++ "b/\346\235\250\345\256\207\346\226\214/note-2021-06-26(web APl\347\254\254\344\270\211\350\212\202\350\257\276).md" @@ -0,0 +1,159 @@ +# 2021-6-26 web API 第三节 + +## ef Core +1. 安装 Entity Framework Core +``` +dotnet add package Microsoft.EntityFrameworkCore.Sqlserver +//数据库为sqlserver +``` +2. 在项目文件夹中,创建Entity文件夹,添加User.cs,Student.cs等 定义字段,实体类 +3.定义构成模型的上下文类。 在项目文件夹中,创建Data文件夹,添加myApiDb.cs +``` +using MyApi.Api.Entity; +using Microsoft.EntityFrameworkCore; +namespace MyApi.Api.Db +{ + public class myApiDb: DbContext + { + public DbSet User {get;set;} + public DbSet Student {get;set;} + + protected override void OnConfiguring(DbContextOptionsBuilder options) + => options.UseSqlServer(@"server=.;uid=sa;pwd=123456;database=Admin1000"); + } +} +``` +4. 创建数据库 使用迁移创建数据库 +``` +//安装 dotnet ef +dotnet tool install --global dotnet-ef +//安装设计包 +dotnet add package Microsoft.EntityFrameworkCore.Design +//迁移搭建基架,以便为模型创建一组初始表 +dotnet ef migrations add InitialCreate +//命令创建数据库并向其应用新的迁移 +dotnet ef database update +``` +5. 在项目文件夹中,创建ParamModel文件夹,添加newUser.cs 模型 +``` +namespace MyApi.Api.ParamModel +{ + public class newUser{ + public string Username{get;set;} + public string Password{get;set;} + } +} +``` +6. 在项目下Controllers 的 UserConteroller.cs +``` +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using MyApi.Api.Entity; +using MyApi.Api.ParamModel; +using System; + +namespace MyApi.Api.Controllers +{ + [ApiController] + [Route("[controller]")] + public class UserController : ControllerBase + { + private myApi.Api.Db.myApiDb db = new Api.Db.myApiDb(); + + + [HttpGet] + public IEnumerable Get() + { + var user = db.User.ToList(); + return user; + } + + [HttpGet("{id}")] + public dynamic Get(int id) + { + var user = db.User.Where(x => x.Id == id).FirstOrDefault(); + return user; + } + + [HttpPost] + public dynamic post(newUser model) + { + var user=new User{ + Username=model.Username, + Password=model.Password, + }; + db.User.Add(user); + db.SaveChanges();//保存 + + return new + { + Code=1000, + Data =user, + ParallelMergeOptions="创建成功" + }; + } + + [HttpPut("{id}")] + public dynamic Put(int id,newUser model) + { + var user = db.User.Where(x => x.Id == id).FirstOrDefault(); + + if (user != null) + { + user.Username = model.Username; + user.Password = model.Password; + user.UpdateTime=DateTime.Now; + db.Update(user); + db.SaveChanges(); + return new + { + Code = 1000, + Data = user, + Msg = string.Format("你修改的用户的id为:{0},已经修改成功,请注意查收", id) + }; + } + else + { + return new + { + Code = 104, + Data = "", + Msg = "指定Id的用户不存在,请确认后重试" + }; + } + + + } + + [HttpDelete("{id}")] + public dynamic Delete(int id) + { + var user = db.User.Where(x => x.Id == id).FirstOrDefault(); + if (user != null) + { + db.User.Remove(user); + db.SaveChanges(); + return new + { + Code = 1000, + Msg = string.Format("删除指定id为{0}的用户成功", id) + }; + } + else + { + return new + { + Code = 104, + Data = "", + Msg = "指定Id的用户不存在,请确认后重试" + }; + } + } + + + } +} + +``` +7. RESTClient 测试 diff --git "a/\346\235\250\345\256\207\346\226\214/note-2021-06-29(web API\347\254\254\345\233\233\350\212\202 \345\273\272\347\253\213\350\241\250\344\271\213\351\227\264\347\232\204\345\205\263\347\263\273).md" "b/\346\235\250\345\256\207\346\226\214/note-2021-06-29(web API\347\254\254\345\233\233\350\212\202 \345\273\272\347\253\213\350\241\250\344\271\213\351\227\264\347\232\204\345\205\263\347\263\273).md" new file mode 100644 index 0000000000000000000000000000000000000000..6ef904579543f12812a5f136ecc73de7166d327f --- /dev/null +++ "b/\346\235\250\345\256\207\346\226\214/note-2021-06-29(web API\347\254\254\345\233\233\350\212\202 \345\273\272\347\253\213\350\241\250\344\271\213\351\227\264\347\232\204\345\205\263\347\263\273).md" @@ -0,0 +1,52 @@ +# 2021-6-29 web API 第四节 建立表之间的关系 +1. 包装一下Entity下的.cs的公共字段 创建BaseEntity.cs +``` +using System; +namespace MyApi.Api.Entity +{ + public abstract class BaseEntity{ + public BaseEntity(){ + CreatedTime=DateTime.Now; + UpdatedTime=DateTime.Now; + } + public int Id{get;set;} + public bool IsActived{get;set;} + public bool IsDeleted{get;set;} + public DateTime CreatedTime{get;set;} + public DateTime UpdatedTime{get;set;} + public string Remarks{get;set;} + } +} + +如:在Users.cs定义字段下继承 public class Users : BaseEntity +``` +2. 建立表之间的关系 Entity文件夹下 +``` +Users.cs + public class Users : BaseEntity + { + public string Username { get; set; } + + public string Password { get; set; } + + public virtual IEnumerable UserRoles { get; set; } + + } + +UserRoles.cs + public class UserRoles : BaseEntity + { + public int UserId { get; set; } + public int RoleId { get; set; } + public virtual Users User { get; set; } + public virtual Roles Role { get; set; } + } + +Roles.cs + public class Roles:BaseEntity + { + public string RoleName { get; set; } + public virtual IEnumerable UserRoles { get; set; } + } +``` +### C#泛型集合 using System.Collections.Generic \ No newline at end of file diff --git "a/\346\235\250\345\256\207\346\226\214/note-2021-06-30(web API \347\254\254\344\272\224\350\212\202 \346\216\245\345\217\243).md" "b/\346\235\250\345\256\207\346\226\214/note-2021-06-30(web API \347\254\254\344\272\224\350\212\202 \346\216\245\345\217\243).md" new file mode 100644 index 0000000000000000000000000000000000000000..651db605221f8c3fad2e9466168b49c8e2708f48 --- /dev/null +++ "b/\346\235\250\345\256\207\346\226\214/note-2021-06-30(web API \347\254\254\344\272\224\350\212\202 \346\216\245\345\217\243).md" @@ -0,0 +1,78 @@ +# web API 第五节 接口 ++ IQueryable 是将Skip ,take 这些方法表达式翻译成T-SQL语句之后再向SQL服务器发送命令,它并不是把所有数据都加载到内存里来才进行条件过滤。 ++ interface 接口 +``` +异步多线程Task(任务) +Task InsertAsync(T entity); +Task UpdateAsync(T entity); +``` ++ IRepository.cs +``` +using System.Linq; +using System.Threading.Tasks; +namespace MyApi.Api.Repository +{ + //interface 接口 + public interface IRepository + { + //IQueryable 是将Skip ,take 这些方法表达式翻译成T-SQL语句之后再向SQL服务器发送命令,它并不是把所有数据都加载到内存里来才进行条件过滤。 + IQueryable Table { get; } + + //根据Id获取指定实体 + T GetById(int id); + + // 使用实体对象,插入数据 + void Insert(T entity); + + // 使用实体对象,插入数据(异步) + Task InsertAsync(T entity); + + //使用实体对象,更新数据 + void Update(T entity); + + //使用实体对象,更新数据(异步) + Task UpdateAsync(T entity); + + //使用实体对象,删除数据 + void Delete(int id); + } +} +``` + ++ EfRepository.cs +``` + public class EfRepository : IRepository where T : BaseEntity + { + // 数据库上下文的变量,此处是使用常规手段,直接初始化一个数据库上下文的实例对象 + + private Admin3000Db _db; + + public EfRepository(Admin3000Db db) + { + _db=db; + } + //一个字段成员,用于内部Entity属性 + private DbSet _entity; + + // 一个访问受限的属性,只有访问器,总是返回一个代表T类型的表对象 + protected DbSet Entity + { + get + { + // 如果 _entity为空(没有指向一个对象),则从数据库上下文重新获得一个 + if (_entity == null) + { + _entity = _db.Set(); + } + return _entity; + } + } + //代表一个可以用于查询T类型的表 + public IQueryable Table + { + get + { + return Entity.AsQueryable(); + } + } +``` \ No newline at end of file diff --git "a/\346\235\250\345\256\207\346\226\214/note-2021-07-02(web API\347\254\254\345\205\255\350\212\202 \344\275\277\347\224\250\344\276\235\350\265\226\346\263\250\345\205\245).md" "b/\346\235\250\345\256\207\346\226\214/note-2021-07-02(web API\347\254\254\345\205\255\350\212\202 \344\275\277\347\224\250\344\276\235\350\265\226\346\263\250\345\205\245).md" new file mode 100644 index 0000000000000000000000000000000000000000..c18a85e793c196ec91506bc45e5799c8ddbabb74 --- /dev/null +++ "b/\346\235\250\345\256\207\346\226\214/note-2021-07-02(web API\347\254\254\345\205\255\350\212\202 \344\275\277\347\224\250\344\276\235\350\265\226\346\263\250\345\205\245).md" @@ -0,0 +1,56 @@ +# 2021-7-2 web API web API第六节 使用依赖注入 + ++ dotnet restore - 恢复项目的依赖项和工具。 + +1. Startup.cs +``` + public void ConfigureServices(IServiceCollection services) + { + // 注册数据库上下文到容器 + services.AddDbContext(o =>o.UseSqlServer()); + // 注册对数据库的基本操作服务到容器 + services.AddScoped(typeof(IRepository<>),typeof(EfRepository<>)); + + // 注册验证器(使用何种配置来验证token) + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(option => + { + // 是否要求使用https + option.RequireHttpsMetadata=false; + // 是否保存token + option.SaveToken=true; + // 使用配置中间件,获得token的配置 + var tokenParameter=Configuration.GetSection("TokenParameter").Get(); + // 验证器的核心属性 + option.TokenValidationParameters=new TokenValidationParameters + { + ValidateIssuerSigningKey=true,//要不要验证生成token的密钥 + IssuerSigningKey=new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenParameter.Secret)), + ValidateIssuer=true, // 要不要验证发行token的人(个人或者组织) + ValidIssuer=tokenParameter.Issuer, + ValidateAudience=false // 是否验证受众 + }; + }); + + services.AddControllers(); + } +``` +2. Dara/MyApiDb.cs +``` + // 因为我们使用AddDbContext到容器,所以此处必须得有带参数的构造函数(而且必须传递DbContextOptions类型的参数,同时父类也得调用这个参数) + public MyApiDb(DbContextOptions options):base(options) + { + + } + +``` + +3. UsersController.cs引用 +``` +private IRepository _usersRepository; + +public UserController(IRepository usersRepository) +{ + _usersRepository=usersRepository; +} +``` \ No newline at end of file diff --git "a/\346\235\250\345\256\207\346\226\214/note-2021-07-03(C#\345\237\272\347\241\200\344\270\211\347\202\271\346\216\245\345\217\243 \346\263\233\345\236\213 \345\261\236\346\200\247).md" "b/\346\235\250\345\256\207\346\226\214/note-2021-07-03(C#\345\237\272\347\241\200\344\270\211\347\202\271\346\216\245\345\217\243 \346\263\233\345\236\213 \345\261\236\346\200\247).md" new file mode 100644 index 0000000000000000000000000000000000000000..81de4f2974f8c8b2187870e7fad243c6a27580fb --- /dev/null +++ "b/\346\235\250\345\256\207\346\226\214/note-2021-07-03(C#\345\237\272\347\241\200\344\270\211\347\202\271\346\216\245\345\217\243 \346\263\233\345\236\213 \345\261\236\346\200\247).md" @@ -0,0 +1,104 @@ +# C# 接口 泛型 属性 + ++ dotnet new console 创建C#控制台 ++ 一般用于函数的返回值。如果函数不需要返回值,即可以用void做返回类型 + +1. 属性 +``` +namespace Test.Console +{ + //构造函数 + public Pig(){ + _age=10; + } + public int _age; + public int Age + { + //get是读出属性获取的值 set是修改属性的值 + get + { + return _age; + } + set + { + _age = value; + } + } + + public void SetAge(int age){ + _age=age; + } + public int GetAge(){ + return _age; + } +} +``` +2. 接口 +``` +namespace Test.Console +{ + public interface IZoo + { + int Age { get; set; } + void Eat(); + void Sleep(); + } + public class Tiger : IZoo + { + public int Age { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public void Eat() + { + throw new NotImplementedException(); + } + + public void Sleep() + { + throw new NotImplementedException(); + } + } + +} +``` +接口的使用 +``` +namespace Test.Console +{ + class Program + { + static void Main(string[] args) + { + IZoo eat1=new Tiger(); + IZoo eat2=new Dog(); + eat1.Eat(); + eat2.Eat(); + } + } +} +``` +3. 泛型 +拿笔来说, Pen 上没有标签就一只 Pen ,不知道它什么颜色的, +Pen 留了一个标签空位,写Red就成了 Pen,你就知道是写出红色的笔了。 +``` +namespace Test.Console +{ + public interface IPen{ + void Class(T Pen); + } + + public class RedPen : IPen + { + public void Class(T Pen) + { + System.Console.WriteLine("这是红色的笔"); + } + } + public class WhitePen : IPen + { + public void Class(T Pen) + { + System.Console.WriteLine("这是白色的笔"); + } + } +} +``` diff --git "a/\346\235\250\345\256\207\346\226\214/note-2021-07-06(web API JWT\346\234\200\344\275\263\345\256\236\350\267\265 ).md" "b/\346\235\250\345\256\207\346\226\214/note-2021-07-06(web API JWT\346\234\200\344\275\263\345\256\236\350\267\265 ).md" new file mode 100644 index 0000000000000000000000000000000000000000..1ffa2cdee15887ade2fddf4d1be013eab9d8a6b0 --- /dev/null +++ "b/\346\235\250\345\256\207\346\226\214/note-2021-07-06(web API JWT\346\234\200\344\275\263\345\256\236\350\267\265 ).md" @@ -0,0 +1,301 @@ +# JWT最佳实践 + +1. 安装JWT认证支持库(必须引入) +``` +dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer + +dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer -h +查看帮助 +``` +2. ParamModel创建TokenParameter.cs +``` +namespace MyApi.Api.ParamModel +{ + public class TokenParameter + { + //token的密钥,不能泄露,泄露意味着所有人都可以生成你的token并且访问你的服务或者接口 + public string Secret { get; set; } + + //发行人(可以是个人或组织) + public string Issuer { get; set; } + + //token的作用时间,单位为分钟,过期后需要重新获取 + public int AccessExpiration { get; set; } + + //类似一个token的东西的作用时间,单位为分钟,用于重新获取token + public int RefreshExpiration { get; set; } + } +} +``` +3. appsettings.json 生成JWT Token需要预设一些参数 +``` +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "TokenParameter": { + "secret": "123456123456123456", + //secret: JWT加密的密钥16个字符以上 + + "issuer": "Root", + //issuer: 签发人的名称 + + "accessExpiration": 120, + //accessExpiration: Token的有效分钟数,过时间后Token会过期 + + "refreshExpiration": 1440 + //refreshExpiration: refreshToken的有效分钟数。过了这个时间,用户需要重新登录。 + } +} + +``` +``` +Token过期后,可以让用户重新登录认证拿Token。但这个方式会比较Low。高大上的方式是签发Token的时候,同时也签发一个refreshToken给用户。用户Token过期后,可以拿refreshToken去申请新的Token,同时刷新refreshToken。如果用户长时间未使用系统,refreshToken也过期了,才让用户重新登录认证。 + +refreshToken可以用JWT生成,也可以自己生成,不影响认证。 +``` + + +4. 生成Token 创建Utils文件夹下创建TokenHelper.cs +``` +using MyApi.Api.ParamModel; +using MyApi.Api.Entity; +using System.Security.Claims; +using Microsoft.IdentityModel.Tokens; +using System.Text; +using System.IdentityModel.Tokens.Jwt; +using System; + +namespace MyApi.Api.Utils +{ + public class TokenHelper + { + //这儿是真正的生成Token代码 + public static string GenerateToekn(TokenParameter tokenParameter,Users user) + { + var claims = new[] + { + new Claim(ClaimTypes.Name,user.Username), + new Claim(ClaimTypes.Role, "root"), + }; + + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenParameter.Secret)); + var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + var jwtToken = new JwtSecurityToken(tokenParameter.Issuer, null, claims, expires: DateTime.UtcNow.AddMinutes(tokenParameter.AccessExpiration), signingCredentials: credentials); + + var token = new JwtSecurityTokenHandler().WriteToken(jwtToken); + + return token; + } + } +} +``` +5. Controllers/UsersController.cs +``` +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using MyApi.Api.Entity; +using MyApi.Api.ParamModel; +using MyApi.Api.Repository; +using MyApi.Api.Utils; + + +namespace MyApi.Api.Controllers +{ + [ApiController] + [Route("[controller]")] + public class UsersController : ControllerBase + { + // private MyApi.Api.Db.MyApiDb db = new Api.Db.MyApiDb(); + private IRepository _usersRespository; + private TokenParameter _tokenParameter; + private readonly IConfiguration _configuration; + + public UsersController(IConfiguration configuration,IRepository UsersRespository){ + _usersRespository=UsersRespository; + _configuration=configuration; + _tokenParameter=_configuration.GetSection("TokenParameter").Get(); + } + [HttpGet] + public IEnumerable Get() + { + var user = _usersRespository.Table.ToList(); + return user; + } + + [HttpGet("{id}")] + public dynamic Get(int id) + { + var user = _usersRespository.GetById(id); + if(user==null){ + return new{ + Code=1001, + Data ="", + Msg ="用户不存在", + }; + } + return new{ + Code=1000, + Data =user, + Msg ="用户以获取", + }; + } + + [HttpPost] + public dynamic post(NewUser model) + { + var user=new Users{ + Username=model.Username, + Password=model.Password, + }; + _usersRespository.Insert(user); + + + return new + { + Code=1000, + Data =user, + ParallelMergeOptions="创建成功" + }; + } + + [HttpPut("{id}")] + public dynamic Put(int id,NewUser model) + { + var user = _usersRespository.GetById(id); + + if (user != null) + { + user.Username = model.Username; + user.Password = model.Password; + user.UpdatedTime=DateTime.Now; + _usersRespository.Update(user); + return new + { + Code = 1000, + Data = user, + Msg = string.Format("你修改的用户的id为:{0},已经修改成功,请注意查收", id) + }; + } + else + { + return new + { + Code = 104, + Data = "", + Msg = "指定Id的用户不存在,请确认后重试" + }; + } + + + } + + [HttpDelete("{id}")] + public dynamic Delete(int id) + { + // var user = db.Users.Where(x => x.Id == id).FirstOrDefault(); + var user=_usersRespository.GetById(id); + if (user != null) + { + // db.Users.Remove(user); + // db.SaveChanges(); + _usersRespository.Delete(id); + return new + { + Code = 1000, + Msg = string.Format("删除指定id为{0}的用户成功", id) + }; + } + else + { + return new + { + Code = 104, + Data = "", + Msg = "指定Id的用户不存在,请确认后重试" + }; + } + } + + [HttpPost, Route("token")] + public dynamic GetToken(NewUser model) + { + var username = model.Username.Trim(); + var password = model.Password.Trim(); + + var user = _usersRespository.Table.Where(x => x.Username == username && x.Password == password).FirstOrDefault(); + + if (user == null) + { + return new + { + Code = 1001, + Data = user, + Msg = "用户名或密码不正确,请核对后重试" + }; + } + + var token = TokenHelper.GenerateToekn(_tokenParameter, user); + var refreshToken = "123456"; + + var res = new + { + Code = 1000, + Data = new { Token = token, RefreshToken = refreshToken }, + Msg = "用户登录成功,获取token成功" + }; + return res; + } + + } +} +``` +6. api.http +``` +### 获取用户 + +GET http://localhost:5000/Users HTTP/1.1 + +###获取指定id用户 +GET http://localhost:5000/Users/10 HTTP/1.1 + +###创建用户 +POST http://localhost:5000/Users HTTP/1.1 +Content-Type:application/json + +{ + "username":"123", + "password":"1113" +} + +###修改指定用户 +PUT http://localhost:5000/Users/1 HTTP/1.1 +Content-Type: application/json + +{ + "username":"666", + "password":"1113" +} + +###删除指定用户 +DELETE http://localhost:5000/Users/1 HTTP/1.1 + +### 登录并获取token +POST http://localhost:5000/Users/token HTTP/1.1 +Content-Type: application/json + +{ + "username":"123", + "password":"1113" +} +``` \ No newline at end of file diff --git "a/\346\235\250\345\256\207\346\226\214/note-2021-07-08(web API JWT\346\234\200\344\275\263\345\256\236\350\267\265 \345\222\214 \346\227\245\345\277\227\345\256\241\350\256\241).md" "b/\346\235\250\345\256\207\346\226\214/note-2021-07-08(web API JWT\346\234\200\344\275\263\345\256\236\350\267\265 \345\222\214 \346\227\245\345\277\227\345\256\241\350\256\241).md" new file mode 100644 index 0000000000000000000000000000000000000000..caa986d66169850b4caae916a381badf039cc3b6 --- /dev/null +++ "b/\346\235\250\345\256\207\346\226\214/note-2021-07-08(web API JWT\346\234\200\344\275\263\345\256\236\350\267\265 \345\222\214 \346\227\245\345\277\227\345\256\241\350\256\241).md" @@ -0,0 +1,282 @@ +# JWT最佳实践 和 日志审计 + +## JWT最佳实践(二) ++ Token认证 + +1. 在Startup.cs中,ConfigureServices方法里,添加以下内容 +``` + // 注册数据库上下文到容器 + services.AddDbContext(o =>o.UseSqlServer()); + // 注册对数据库的基本操作服务到容器 + services.AddScoped(typeof(IRepository<>),typeof(EfRepository<>)); + + // 注册验证器(使用何种配置来验证token) + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(option => + { + // 是否要求使用https + option.RequireHttpsMetadata=false; + // 是否保存token + option.SaveToken=true; + // 使用配置中间件,获得token的配置 + var tokenParameter=Configuration.GetSection("TokenParameter").Get(); + // 验证器的核心属性 + option.TokenValidationParameters=new TokenValidationParameters + { + ValidateIssuerSigningKey=true,//要不要验证生成token的密钥 + IssuerSigningKey=new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenParameter.Secret)), + ValidateIssuer=true, // 要不要验证发行token的人(个人或者组织) + ValidIssuer=tokenParameter.Issuer, + ValidateAudience=false // 是否验证受众 + }; + }); + services.AddControllers(); +``` +2. 在Startup.cs里,Configure方法中,打开认证 +``` +// 将token的验证注册到中间件 +app.UseAuthentication(); +app.UseAuthorization(); +``` +3. 设置API认证。 在UsersController.cs下的Get方法来测试 ++ 在方法前边,我们加上Authorize +``` + [Authorize] + [ApiController] + [Route("[controller]")] + public class UsersController : ControllerBase{} +``` +4. 认证后的访问 +``` +api.http +### 获取用户 + +GET http://localhost:5000/Users HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoicm9vdCIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6InJvb3QiLCJleHAiOjE2MjU3NDQwNTIsImlzcyI6IlJvb3QifQ.C_aUR4JDPl-2r6Cccv-BrNnmJgYG7OhJhq6VbOfB5l4 + +### 登录并获取token +POST http://localhost:5000/Users/token HTTP/1.1 +Content-Type: application/json + +{ + "username":"root", + "password":"1113" +} +``` +# 日志审计 +1. 定义审计日志信息 在Entity 下创建AuditInfo.cs +``` +public class AuditInfo +{ + /// + /// 调用参数 + /// + public string Parameters { get; set; } + /// + /// 浏览器信息 + /// + public string BrowserInfo { get; set; } + /// + /// 客户端信息 + /// + public string ClientName { get; set; } + /// + /// 客户端IP地址 + /// + public string ClientIpAddress { get; set; } + /// + /// 执行耗时 + /// + public int ExecutionDuration { get; set; } + /// + /// 执行时间 + /// + public DateTime ExecutionTime { get; set; } + /// + /// 返回内容 + /// + public string ReturnValue { get; set; } + /// + /// 异常对象 + /// + public string Exception { get; set; } + /// + /// 方法名 + /// + public string MethodName { get; set; } + /// + /// 服务名 + /// + public string ServiceName { get; set; } + /// + /// 调用者信息 + /// + public string UserInfo { get; set; } + /// + /// 自定义数据 + /// + public string CustomData { get; set; } +} +``` +2. 实现审计日志过滤器 创建Filters文件夹下创建AuditLogActionFilter.cs +``` +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using MyApi.Api.Repository; +using MyApi.Api.Entity; + +namespace MyApi.Api.Filters +{ + public class AuditLogActionFilter : IAsyncActionFilter + { + + private readonly IRepository _auditLogService; + + public AuditLogActionFilter( + IRepository auditLogService + ) + { + _auditLogService=auditLogService; + } + + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + // 判断是否写日志 + if (!ShouldSaveAudit(context)) + { + await next(); + return; + } + //接口Type + var type = (context.ActionDescriptor as ControllerActionDescriptor).ControllerTypeInfo.AsType(); + //方法信息 + var method = (context.ActionDescriptor as ControllerActionDescriptor).MethodInfo; + //方法参数 + var arguments = context.ActionArguments; + //开始计时 + var stopwatch = Stopwatch.StartNew(); + var auditInfo = new AuditInfo + { + UserInfo = "小明", + ServiceName = type != null ? type.FullName : "", + MethodName = method.Name, + ////请求参数转Json + Parameters = JsonConvert.SerializeObject(arguments), + ExecutionTime = DateTime.Now, + BrowserInfo = context.HttpContext.Request.Headers["User-Agent"].ToString(), + ClientIpAddress = context.HttpContext.Connection.RemoteIpAddress.ToString(), + //ClientName = _clientInfoProvider.ComputerName.TruncateWithPostfix(EntityDefault.FieldsLength100), + // Id = Guid.NewGuid().ToString() + }; + + ActionExecutedContext result = null; + try + { + result = await next(); + if (result.Exception != null && !result.ExceptionHandled) + { + auditInfo.Exception = result.Exception.ToString(); + } + } + catch (Exception ex) + { + auditInfo.Exception = ex.ToString(); + throw; + } + finally + { + stopwatch.Stop(); + auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds); + + if (result != null) + { + switch (result.Result) + { + case ObjectResult objectResult: + auditInfo.ReturnValue = JsonConvert.SerializeObject(objectResult.Value); + break; + + case JsonResult jsonResult: + auditInfo.ReturnValue = JsonConvert.SerializeObject(jsonResult.Value); + break; + + case ContentResult contentResult: + auditInfo.ReturnValue = contentResult.Content; + break; + } + } + Console.WriteLine(auditInfo.ToString()); + //保存审计日志 + await _auditLogService.InsertAsync(auditInfo); + } + } + + /// + /// 是否需要记录审计 + /// + /// + /// + private bool ShouldSaveAudit(ActionExecutingContext context) + { + if (!(context.ActionDescriptor is ControllerActionDescriptor)) + return false; + var methodInfo = (context.ActionDescriptor as ControllerActionDescriptor).MethodInfo; + + if (methodInfo == null) + { + return false; + } + + if (!methodInfo.IsPublic) + { + return false; + } + + //if (methodInfo.GetCustomAttribute() != null) + //{ + // return true; + //} + + //if (methodInfo.GetCustomAttribute() != null) + //{ + // return false; + //} + + //var classType = methodInfo.DeclaringType; + //if (classType != null) + //{ + // if (classType.GetTypeInfo().GetCustomAttribute() != null) + // { + // return true; + // } + + // if (classType.GetTypeInfo().GetCustomAttribute() != null) + // { + // return false; + // } + // } + return true; + } + } +} +``` +3. 注册过滤器 Startup.cs +``` + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(options=>{ + options.Filters.Add(typeof(AuditLogActionFilter)); + }); + } +``` + diff --git "a/\346\235\250\345\256\207\346\226\214/note-2021-07-10(Vue Cli Element).md" "b/\346\235\250\345\256\207\346\226\214/note-2021-07-10(Vue Cli Element).md" new file mode 100644 index 0000000000000000000000000000000000000000..1fa4bc5a41ff864ea988a89194e88e5e4ea056d7 --- /dev/null +++ "b/\346\235\250\345\256\207\346\226\214/note-2021-07-10(Vue Cli Element).md" @@ -0,0 +1,147 @@ +## Element +1. 安装 +``` +npm i element-ui -S +yarn add element-ui +``` +2. 包装router +``` +在src下创建 router文件夹 + +index.js + +import Vue from 'vue' +import VueRouter from 'vue-router' +import routes from './routes' +Vue.use(VueRouter) + +let router= new VueRouter({ + mode:'history', + routes +}) + +export default router + + +routes.js +import Layout from '../components/Layout.vue' + +let routes=[{ + path:'/', + component:Layout, + children:[{ + path:'home', + component:()=>import('../components/Home') + }, + { + path:'user', + component:()=>import('../components/User') + } +] +}] +export default routes +``` +3. main.js +``` +import Vue from 'vue' +import App from './App.vue' +import router from './router' +import ElementUI from 'element-ui'; +import 'element-ui/lib/theme-chalk/index.css'; + +Vue.use(ElementUI); +Vue.config.productionTip = false + +new Vue({ + router, + render: h => h(App), +}).$mount('#app') + +``` +4. src/components 下创建 Layout.js User.js Home.js ++ router 是否使用 vue-router 的模式,启用该模式会在激活导航时以 index 作为 path 进行路由跳转 ++ route Vue Router 路径对象 +``` +Layout.js + + + + +``` +ll -al diff --git "a/\346\235\250\345\256\207\346\226\214/note-2021-07-13(\346\236\204\351\200\240\350\267\257\347\224\261\346\225\260\346\215\256).md" "b/\346\235\250\345\256\207\346\226\214/note-2021-07-13(\346\236\204\351\200\240\350\267\257\347\224\261\346\225\260\346\215\256).md" new file mode 100644 index 0000000000000000000000000000000000000000..60946d8925c65302ae7cb328e88ae664b64ed677 --- /dev/null +++ "b/\346\235\250\345\256\207\346\226\214/note-2021-07-13(\346\236\204\351\200\240\350\267\257\347\224\261\346\225\260\346\215\256).md" @@ -0,0 +1,221 @@ +# 构造路由数据 +1. 简单的构造路由数据 +``` +Layout.vue + + +
+ + + +
+ + + + + + {{ child.title }} + +
+
+ + + + {{ menu.title }} + +
+ + +``` +2. 包装路由数据 ++ components下创建Navbar文件夹 +``` +index.vue + + + + + +``` +``` +Navbarltem.vue + + + + + +``` +``` +collapse 是否水平折叠收起菜单(仅在 mode 为 vertical 时可用) +Layout.js + + + + +``` \ No newline at end of file diff --git "a/\346\235\250\345\256\207\346\226\214/note-2021-07-14(\351\201\215\345\216\206\345\207\272\350\267\257\347\224\261\350\267\257\345\276\204).md" "b/\346\235\250\345\256\207\346\226\214/note-2021-07-14(\351\201\215\345\216\206\345\207\272\350\267\257\347\224\261\350\267\257\345\276\204).md" new file mode 100644 index 0000000000000000000000000000000000000000..3aa4662dad2e8c493c41a8a01cee359cb5cdf79f --- /dev/null +++ "b/\346\235\250\345\256\207\346\226\214/note-2021-07-14(\351\201\215\345\216\206\345\207\272\350\267\257\347\224\261\350\267\257\345\276\204).md" @@ -0,0 +1,41 @@ +# 遍历出路由路径 +1. Layout.vue +``` + +``` \ No newline at end of file diff --git "a/\346\235\250\345\256\207\346\226\214/note-2021-07-16(ECharts\357\274\214iconfont\346\267\230\345\256\235\345\233\276\346\240\207,axios,\350\267\250\345\237\237,).md" "b/\346\235\250\345\256\207\346\226\214/note-2021-07-16(ECharts\357\274\214iconfont\346\267\230\345\256\235\345\233\276\346\240\207,axios,\350\267\250\345\237\237,).md" new file mode 100644 index 0000000000000000000000000000000000000000..fd8158309db1808503ab526b4fde77170934ff10 --- /dev/null +++ "b/\346\235\250\345\256\207\346\226\214/note-2021-07-16(ECharts\357\274\214iconfont\346\267\230\345\256\235\345\233\276\346\240\207,axios,\350\267\250\345\237\237,).md" @@ -0,0 +1,126 @@ +# ECharts,iconfont淘宝图标,axios,跨域, +## ECharts +1. 安装 ECharts +``` +npm install echarts --save +yarn add echarts +``` +2. 引入 ECharts main.js +``` +import * as echarts from 'echarts'; +import 'echarts-gl'; + +Vue.use(ElementUI); +Vue.prototype.$echarts = echarts +``` +3. Diqiu.vue +``` + + + +``` +2. 加入通用css代码(引入一次就行) 创建icon.css src/assets/icon.css +``` +.icon { + width: 1em; height: 1em; + vertical-align: -0.15em; + fill: currentColor; + overflow: hidden; + } +``` +3. 注册使用 +``` +import '../src/assets/icon.css' +``` +4. 挑选相应图标并获取类名,应用于页面 +``` + +``` +## axios +1. 安装axios +``` + npm install axios + yarn add axios +``` +2. 创建实例 src/utils/request.js +``` +import axios from 'axios' +const instance = axios.create({ + baseURL: 'http://localhost:5000/', + timeout: 1000, +}); +//暴露模块 +export default instance +``` +3. 定义方法获取指定id的值 src/api/users.js +``` +import request from '../utils/request' + +const getUserById=(id)=>{ + return request.get(`/users/${id}`) +} +export default getUserById +``` +4. 使用在前端 +``` + +``` +## 跨域 +1. 设置允许所有来源跨域,在StartUp类的ConfigureServices方法中添加如下代码: +``` + services.AddCors(Options=>{ + Options.AddPolicy(allowCors,builder => + { + builder.AllowAnyHeader() + .AllowAnyMethod() + .AllowAnyOrigin(); + }); + }); +``` +2. 修改Configure方法 +``` + // 跨域 允许所有跨域,allowCors是在ConfigureServices方法中配置的跨域策略名称 + app.UseCors(allowCors); +``` +3. 设置特定来源可以跨域,修改ConfigureServices方法代码如下: +``` +//允许一个或多个来源可以跨域 + services.AddCors(Options=>{ + Options.AddPolicy(allowCors,builder => + { + // 设定允许跨域的来源 + builder.AllowAnyHeader() + .AllowAnyMethod() + .AllowAnyOrigin(); + }); + }); +``` + diff --git "a/\346\235\250\345\256\207\346\226\214/note-2021-07-17(\347\224\250\346\210\267\345\210\206\351\241\265).md" "b/\346\235\250\345\256\207\346\226\214/note-2021-07-17(\347\224\250\346\210\267\345\210\206\351\241\265).md" new file mode 100644 index 0000000000000000000000000000000000000000..e89dfcfcc2c09e0d2eedd0f04fb9ce66369a7b04 --- /dev/null +++ "b/\346\235\250\345\256\207\346\226\214/note-2021-07-17(\347\224\250\346\210\267\345\210\206\351\241\265).md" @@ -0,0 +1,152 @@ +# 分页 +1. vue cli Users.vue +``` + + + +``` +2. web Api UsersController +``` + public dynamic Get([FromQuery] Pager pager) + { + // var pageIndex=pager.PageIndex; + // var pageSize=pager.PageSize; + var pageIndex=int.Parse(Request.Query["pageIndex"][0]); + System.Console.WriteLine(pageIndex); + //获取前端vue定义的pageIndex放入一个数组里获取第一个 + var pageSize=int.Parse(Request.Query["pageSize"][0]); + + var user = _usersRespository.Table; + var dt=user.Skip((pageIndex-1)*pageSize).Take(pageSize).ToList(); + //Skip()方法先忽略根据页面的大小和实际的页数计算出的项数,再使用方法Take()根据页面的大小提取一定数量的项: + var res= new + { + Code = 1000, + Data = new {Data =dt,Pager =new {pageIndex,pageSize,rowsTotal=user.Count()}}, + Msg = "获取用户列表成功^_^" + }; + return res; + } +``` +3. 创建类定义字段 ParamModel/Pager.cs +``` +namespace MyApi.Api.ParamModel +{ + public class Pager + { + // 当前页面 + public int PageIndex{get;set;} + + // 当前页条数 + public int PageSize{get;set;} + + // 总条数 + public int RowsTotal{get;set;} + } +} +``` \ No newline at end of file