Asp.WeiApi

137 阅读9分钟

Asp.net.Core

前言:

Asp的侧重点有2个MVC和WebApi,虽然说WebApi是MVC的一部分都是可以算是分2部分来说。

Asp. MVC

1.1基本概念:

Model(模型)、View(视图)、Controller(控制器) 控制器与视图之间的数据传递就是模型

Controller(控制器)就是我们的实现类,View(视图)就是数据渲染(Render)之后得到的效果页面、模型就是只有属性的普通类


Models/Person.cs
	新的类写法 =>只能说方便
 	public record Person(string Name,bool IsVip,DateTime CreatedDateTime)

Views/Test/Demo1.cshtml
    @model WebApplication1.Models.Person
    <div>姓名:@Model.Name</div>
    <div>是否Vip:@Model.IsVip</div>
    <div>时间:@Model.CreatedDateTime</div>
    
 Controllers/TestController.cs
 public class TestController : Controller
    {
        public IActionResult Demo1()
        {
            var p1 = new Person("Tome",true,DateTime.Now);
            return View(p1);
        }
    }
   然后跑的时候直接就是Test/Demo1就可以得到页面效果 反正就控制器那个文件夹里面把Controller去掉就是网页访问路径之一 然后Demo1就是访问的视图
  只能说写起来的感觉跟以前写PHPMCV差不多

Asp.WebApi

1.1直接来个列子看一下基本接口是怎么写的

细节:其实控制器可以不显式的去继承自任何类

就好比下方的代码TestController : ControllerBase 其实 Test是可以不去继承 ControllerBase也是可以执行的,但是不继承的话会有一些方法是无法直接调用,一般来说 继承一下 ControllerBase就可以不用动他了

Controllers.TestController.cs

namespace WebApplication1.Controllers
{
    [Route("api/[controller]")] //这个就是接口的访问路径
    [ApiController]
    public class TestController : ControllerBase
    {
        //当要发出get请求的时候需要添加一个Http
        //如果是发出Post delet update 都一样 [HttpPost]
        [HttpGet]  
        //这个就是请求里面所包含的方法
        public Person GetPerson()
        {
            return new Person("帝后云曦",18);
        }

        [HttpPost]
          public string[]  SaveNote(SaveNoteRequest req)
        {
            System.IO.File.WriteAllText(req.Title+".text",req.Content);
            return new string[] {"OK",req.Title};
        }
    }
}


Person.cs
 public record Person(string Name,int Age);
1.2了解一下Rest

Rest是WebApi的一种风格,WbeApi有两种风格一种是(面向过程)RPC、面向REST

Rpc跟Rest对比

Rpc:想到什么干什么,不用考虑那么多,方便 一把搜哈

Rest: 凸显一个专业

Rpc:控制器/操作方法的形式把服务端的代码当成方法去调用(简单直接)
就写前端的时候最常见的:(使用的是querystring)
	/Person/GetAll
	/Person/GetByid?id=8
	/Person/DeleteByid/8;
	
Rest:按照HTTP语义来使用Http协议
	1、Url用于资源的定位: /user/888、/user/8888/orders
									//获得用户编号为888的所有东西
	2、Http胃词:就是请求的方式 Get、Post..
	3、幂等: 可以理解成 一个行为不管触发多少次结果都是一样的就是幂等
		//抽象一点来说 lise.Add(3) 这个行为就不是幂等的每次触发这个行为都会新添加3条数据
	4、Get的相应可以被缓存
	5、服务器通过状态码来反映资源的获取结果
		//接口请求触发了之后 如果成功会返回200状态码 如果失败会直接给有关该错误的状态码比如:404

上点代码熟悉一下

 [Route("[controller]/[action]")]
    [ApiController]
    public class Test : ControllerBase
    {
        [HttpGet]
        public Person[] GetAll()
        {
            return new Person[] {
                    new Person(1,"云曦", 16),
                    new Person(2,"月禅",16),
                    new Person(3,"清漪",16),
                    new Person(4,"荒",16) };
        }

        [HttpGet]
        public Person? GetById(long id)
        //这里添加?是用来表示可为null的类型
        {
            if (id == 1)
            {
                return new Person(1, "云曦", 16);
            }
            else if( id == 2)
            {
                return new Person(2, "月禅", 16);
            }else if( id == 3)
            {
                return new Person(3, "清漪", 16);
            }else if( id == 4)
            {
                return new Person(4, "荒", 16);
            }
            else
            {
                return null;
            }
        }

        [HttpPost]
        public string AddNew(Person p)
        {
            return "OK";
        }

        }
    //注意上面的{id}是路由参数并不是querystring
    //就拿Get{id}为例子:运用到浏览器的路径是:Person/6  而不是Person?id=6

Rest的实现

就好比 我想要Person/aaa的路径系统会优先寻找名为aaa的控制器

注意:如果控制器存在一个没有添加HttpGet/Post等的public方法 可以处理这种请求但是Swagger会报错 可以用``

为了实现这样的路径方式:
    [Route("[controller]/[action]")]优先匹配action方法的名字
    
      [ApiExplorerSettings(IgnoreApi =true)]
        public void Read()
        {
            Console.WriteLine("你好");
        }
            	
1.3WebApi的异步及返回值

Action的异步方法

​ 一般来说如果我们去使用异步的时候不是await...async这样去写吗,然后为了更加的好的去分辨异步方法我们会在方法名后面添加async => ActionAsyncm。但是Action方法可以同步也可以异步,异步的Action方法名一般不需要Async结尾

IActionResult

​ 这个类型是用于我们后端自定义的状态码,正常抛异常不使用这个东西那么会出现一大堆错误乱码,我们使用这一个的话,可以去自定义错误的状态码,更加的直观


        [HttpGet]
        public IActionResult<int> GetCj2(int id)
        //使用泛型指定就不用写那些什么OK之类的东西了
        {
            if(id == 1)
            {
            	//return OK(88);
                return 88;  
            }else if(id == 2)
            {
                return 99;
            }else
            {
                return NotFound("id错误");
            }
        } 这里面的OK NotFound是 ControllerBase里面关于JSON的一种方法
1.4 Asp中依赖注入的使用

首先Aps中的依赖注入 不像是控制程序那样写那么多东西,项目创建的时候在Program.cs就已经给你配置好了

打个比方,我创建了一个Tester.cs的实体类里面包含一个添加的方法,然后我想要在其他地方使用它,我通过依赖注入的方式去把这个方法注入 这样

Tester.cs
public class Tester
    {
        public  int Add(int x,int y)
        {
            return x + y;
        }
    }
    
Program.cs
var builder = WebApplication.CreateBuilder(args);.

builder.Services.AddControllers(); // 直接使用这个进行依赖注入
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//上面的这三个Add就已经是帮你配置好使用依赖注入的配置了 
//你想要进行依赖注入就AddScoped<依赖名称> 这样子就已经算是创建好了

builder.Services.AddScoped<Tester>();
var app = builder.Build(); 

TestController.cs

 [Route("api/[controller]/[action]")]
    [ApiController]
    public class TestController : ControllerBase
    {
        private readonly Tester tester;
        //声明了一个只读的字段  tester 他的类型是 Tester.cs里面的配置
        //readonly就是一个私有字段

        public TestController(Tester tester)
        {
            this.tester = tester;
        }
        //使用依赖注入的方式把将一个 Tester 对象注入到 TestController 中
        //

        [HttpGet]
        public int Add1()
        {
            return tester.Add(1, 3);
        }
    }


那么问题来了,有多个项目的时候,如果想要使用其他项目中的东西,那么我不就要都在Asp中创建好几个依赖注入,有没有一种方法是可以让项目自己进行服务的注册

首先安装一下用到配置的包 
Install-Package Zack.Commons

安装好之后在每个项目里面创建一个实现了IModuleInitializer接口的类

ModelIntics.cs
namespace ClassLibrary1
{
    internal class ModelIntics : IModuleInitializer
    {
        public void Initialize(IServiceCollection services)
        {
            services.AddScoped<Class1>();
        }
    }
}

然后在Asp项目中就不需要去创建这个类 只需要在Program.cs中实现
var asms =ReflectionHelper.GetAllReferencedAssemblies();
builder.Services.RunModuleInitializers(asms);
其余的正常使用就可以了

1.5使用EFCore+依赖注入进行一个CURD的数据库操作

先看一下项目目录

image-20240210222921585

首先要先配置好EFCore数据库迁移所需要的东西
BookDbContext.cs
 public class BookDbContext:DbContext
    {
       public BookDbContext(DbContextOptions<BookDbContext> options) : base(options) { }   
      public DbSet<Book> Books { get; set; }       //这个Books就是数据库迁移出来的表名
    }

然后依赖注入一下:
builder.Services.AddDbContext<BookDbContext>(option =>
{
    option.UseSqlServer(builder.Configuration.GetConnectionString("BookConn"));
});

在那个app的json文件里面配置一下数据库的连接字符串
"ConnectionStrings": {
        "BookConn": "Server="";Database=Demo1;Trusted_Connection=True;MultipleActiveResultSets=true"
    },
    
然后直接 add 和 update 
	重点:出现那些什么找不到DbContext的目录删干净一点重新迁移一下就可以了
	
	
直接上接口功能:
 public class BooklController : ControllerBase
    {
        private readonly BookDbContext _db;
        public BooklController(BookDbContext db)
        {
            _db = db;
        }


        [HttpGet]
       public async Task<ActionResult<IEnumerable<Book>>> GetAll()
        {
          var list = _db.Books.ToList(); return Ok(list);
        }

        [HttpPost]
       public async Task<ActionResult<IEnumerable<Book>>> SeletTitle(string Title)
        {
            var selt = _db.Books.Where(x => x.Title == Title).ToList();
            return  Ok(selt);
        }

        [HttpDelete]
        public async Task<ActionResult<IEnumerable<Book>>> DeltOne(string Title)
        {
           var xp = _db.Books.FirstOrDefault(e => e.Title == Title);
            if (xp != null)
            {
                _db.Books.Remove(xp); // 将书籍添加到上下文的删除集合中
                await _db.SaveChangesAsync(); // 提交更改到数据库
                return Ok("删除成功");
            }
            else
            {
                return NotFound("没有找到要删除的书籍");
            }
        }
        
    总结:个人感觉相当于SqlSuger 有一点繁琐,但是还好把

缓存

image-20240204144507826

缓存概念

缓存的命中 、缓存的命中率、缓存的数据不一致

缓存数据的不一致:就可以看一下上面的图,一开始我数据库给出的结果是60 ,但是后面这个id为1给出的结果修改成了62,我们再去调用这个id=1的条件的时候,因为我们缓存中已经保存了结果是60最后出来的结果也是60,但实际数据应该是62 这就是缓存数据的不一致。

1.客户端缓存

首先来看一下cache-control这是一个响应报文头,服务器如果返回cache-control:max-age=60表示服务器只是浏览器端可以缓存这个响应60秒

我们使用的话其实比较简单,只要给需要进行缓存控制的控制器的操作方法添加

 [ResponseCache(Duration=20)] //其中Duration="" 这个是代表这个缓存能保存多少秒
 				//缓存20秒之后失效
        [HttpGet]
        public async Task<ActionResult<IEnumerable<YunXi>>> AAA()
        {
            return await db.Queryable<YunXi>().ToListAsync();
        }               
2.内存缓存

把缓存的数据放到应用程序的内存,内存缓存中保存的是一系列的建值,类型跟Dictionary一样

内存缓存的数据时保存在当前运行网站程序的内存中,他和进程是有关系的,不同网站的内存缓存时不会相互干扰,但是网站重启之后,数据就会清空

内存缓存的用法:
	builder.Services.AddMemoryCache(); //先注入一下
	
然后注入到控制器中:
	public class TestController : ControllerBase
    {     
        private readonly IMemoryCache _cache;
        public TestController(ISqlSugarClient db, IMemoryCache cache)
        {
            _cache = cache;
        }
注入完之后开始去写接口:
	  [HttpGet]
        public async Task<ActionResult<IEnumerable<yunxi>>> AAA()
        {
            var cacheKey = "GetAllYunxi";
            //这里首先是定义一个用于内存缓存的键
            if (_cache.TryGetValue(cacheKey, out List<yunxi> yunxiList))
            {
                return yunxiList;
                //然后使用方法TryGetValue 去获取内存缓存中的数据,拿到的话就直接返回缓存中的结果
            }
			
			//如果缓存不存在数据那就从数据库中查询数据
            yunxiList = await _db.Queryable<yunxi>().ToListAsync();

			//这个部分是设置了缓存的的绝对过期值:10分钟。
            var cacheEntryOptions = new MemoryCacheEntryOptions()
            .SetAbsoluteExpiration(TimeSpan.FromMinutes(10));
            //然后使用set方法将查询到的数据添加到内存缓存中
            _cache.Set(cacheKey, yunxiList, cacheEntryOptions);
			
            return yunxiList;
        }

中间件

中间件:MVC框架、响应缓存、身份验证、Swagger之类的都是内置中间件

三个概念:Map、Use、Run 其中:Map是用来定义一个管道可以处理那些请求,Ues和Run是用来定义管道

看一下Program.cs文件中的Use 那个就是直接引用的内置中间件,如果想要自己去自定义:定义一个中间件的类然后 定义好之后use一下就差不多了 to be continue..

Identity标识框架