从头编写 asp.net core 2.0 web api 基础框架 (1)

工具:

1.Visual Studio 2017 V15.3.5+

2.Postman (Chrome的App)

3.Chrome (最好是)

关于.net core或者.net core 2.0的相关知识就不介绍了, 这里主要是从头编写一个asp.net core 2.0 web api的基础框架.

 

我最近几年一直在使用asp.net web api (传统.net framework)作为后台Api, 前台使用 angularjs 1.2 或者 1.6进行开发, 自己独立做过10多个基于这两个技术的项目吧. 在此之前, 我是搞java的, 因为我不受限于公司, 所以我决定放弃java, .net非常适合我.

我一直在关注asp.net core 和 angular 2/4, 并在用这对开发了一些比较小的项目. 现在我感觉是时候使用这两个技术去为企业开发大一点的项目了, 由于企业有时候需要SSO(单点登录), 所以我一直在等待Identity Server4以及相关库的正式版, 现在匹配2.0的RC版已经有了, 所以这个可以开始编写了.

这个系列就是我从头开始建立我自己的基于asp.net core 2.0 web api的后台api基础框架过程, 估计得分几次才能写完. 如果有什么地方错的, 请各位指出!!,谢谢.

 

创建项目:

1.选择asp.net core web application.

2.选择.net core, asp.net core 2.0, 然后选择Empty (因为是从头开始):

下面看看项目生成的代码:

Program.cs


namespace CoreBackend.Api
{    public class Program{        public static void Main(string[] args){BuildWebHost(args).Run();}        public static IWebHost BuildWebHost(string[] args) =>WebHost.CreateDefaultBuilder(args).UseStartup<Startup>().Build();}
}

这个Program是程序的入口, 看起来很眼熟, 是因为asp.net core application实际就是控制台程序(console application).

它是一个调用asp.net core 相关库的console application. 

Main方法里面的内容主要是用来配置和运行程序的.

因为我们的web程序需要一个宿主, 所以 BuildWebHost这个方法就创建了一个WebHostBuilder. 而且我们还需要Web Server.

看一下WebHost.CreateDefaultBuilder(args)的源码:

public static IWebHostBuilder CreateDefaultBuilder(string[] args){            var builder = new WebHostBuilder().UseKestrel().UseContentRoot(Directory.GetCurrentDirectory()).ConfigureAppConfiguration((hostingContext, config) =>{                    var env = hostingContext.HostingEnvironment;config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);                    if (env.IsDevelopment()){                        var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));                        if (appAssembly != null){config.AddUserSecrets(appAssembly, optional: true);}}config.AddEnvironmentVariables();                    if (args != null){config.AddCommandLine(args);}}).ConfigureLogging((hostingContext, logging) =>{logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));logging.AddConsole();logging.AddDebug();}).UseIISIntegration().UseDefaultServiceProvider((context, options) =>{options.ValidateScopes = context.HostingEnvironment.IsDevelopment();});      
      
return builder;}

asp.net core 自带了两种http servers, 一个是WebListener, 它只能用于windows系统, 另一个是kestrel, 它是跨平台的.

kestrel是默认的web server, 就是通过UseKestrel()这个方法来启用的.

但是我们开发的时候使用的是IIS Express, 调用UseIISIntegration()这个方法是启用IIS Express, 它作为Kestrel的Reverse Proxy server来用.

如果在windows服务器上部署的话, 就应该使用IIS作为Kestrel的反向代理服务器来管理和代理请求.

如果在linux上的话, 可以使用apache, nginx等等的作为kestrel的proxy server.

当然也可以单独使用kestrel作为web 服务器, 但是使用iis作为reverse proxy还是由很多有点的: 例如,IIS可以过滤请求, 管理证书, 程序崩溃时自动重启等.

UseStartup<Startup>(), 这句话表示在程序启动的时候, 我们会调用Startup这个类.

Build()完之后返回一个实现了IWebHost接口的实例(WebHostBuilder), 然后调用Run()就会运行Web程序, 并且阻止这个调用的线程, 直到程序关闭.

BuildWebHost这个lambda表达式最好不要整合到Main方法里面, 因为Entity Framework 2.0会使用它, 如果把这个lambda表达式去掉之后, Add-Migration这个命令可能就不好用了!!!

Startup.cs


namespace CoreBackend.Api
{    public class Startup{        // This method gets called by the runtime. Use this method to add services to the container.        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940public void ConfigureServices(IServiceCollection services){}        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IHostingEnvironment env){            if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.Run(async (context) =>{                await context.Response.WriteAsync("Hello World!");});}}
}


其实Startup算是程序真正的切入点.

ConfigureServices方法是用来把services(各种服务, 例如identity, ef, mvc等等包括第三方的, 或者自己写的)加入(register)到container(asp.net core的容器)中去, 并配置这些services. 这个container是用来进行dependency injection的(依赖注入). 所有注入的services(此外还包括一些框架已经注册好的services) 在以后写代码的时候, 都可以将它们注入(inject)进去. 例如上面的Configure方法的参数, app, env, loggerFactory都是注入进去的services.

Configure方法是asp.net core程序用来具体指定如何处理每个http请求的, 例如我们可以让这个程序知道我使用mvc来处理http请求, 那就调用app.UseMvc()这个方法就行. 但是目前, 所有的http请求都会导致返回"Hello World!".

这几个方法的调用顺序: Main -> ConfigureServices -> Configure

请求管道和中间件(Request Pipeline, Middleware)

请求管道: 那些处理http requests并返回responses的代码就组成了request pipeline(请求管道).

中间件: 我们可以做的就是使用一些程序来配置那些请求管道 request pipeline以便处理requests和responses. 比如处理验证(authentication)的程序, 连MVC本身就是个中间件(middleware).

每层中间件接到请求后都可以直接返回或者调用下一个中间件. 一个比较好的例子就是: 在第一层调用authentication验证中间件, 如果验证失败, 那么直接返回一个表示请求未授权的response.

app.UseDeveloperExceptionPage(); 就是一个middleware, 当exception发生的时候, 这段程序就会处理它. 而判断env.isDevelopment() 表示, 这个middleware只会在Development环境下被调用.

可以在项目的属性Debug页看到这个设置: 

需要注意的是这个环境变量Development和VS里面的Debug Build没有任何关系.

在正式环境中, 我们遇到exception的时候, 需要捕获并把它记录(log)下来, 这时候我们应该使用这个middleware: Exception Handler Middleware, 我们可以这样调用它:

            if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}            else{                app.UseExceptionHandler();}

UseExceptionHandler是可以传参数的, 但暂时先这样, 我们在app.Run方法里抛一个异常, 然后运行程序, 在Chrome里按F12就会发现有一个(或若干个, 多少次请求, 就有多少个错误)500错误.

用来创建 Web Api的middleware:

 原来的.net使用asp.net web api 和 asp.net mvc 分别来创建 web api和mvc项目. 但是 asp.net core mvc把它们整合到了一起.

MVC Pattern

model-view-controller 它的定义是: MVC是一种用来实现UI的架构设计模式. 但是网上有很多解释, 有时候有点分不清到底是干什么的. 但是它肯定有这几个有点: 松耦合, Soc(Separation of concerns), 易于测试, 可复用性强等.

但是MVC绝对不是完整的程序架构, 在一个典型的n层架构里面(presentation layer 展示层, business layer 业务层, data access layer数据访问层, 还有服务处), MVC通常是展示层的. 例如angular就是一个客户端的MVC模式.

在Web api里面的View就是指数据或者资源的展示, 通常是json.

注册并使用MVC

因为asp.net core 2.0使用了一个大而全的metapackage, 所以这些基本的services和middleware是不需要另外安装的.

首先, 在ConfigureServices里面向Container注册MVC: services.AddMvc();

        public void ConfigureServices(IServiceCollection services){            services.AddMvc(); // 注册MVC到Container}

然后再Configure里面告诉程序使用mvc中间件:


        public void Configure(IApplicationBuilder app, IHostingEnvironment env){            if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}            else{app.UseExceptionHandler();}            app.UseMvc();


注意顺序, 应该在处理异常的middleware后边调用app.UseMvc(), 所以处理异常的middleware可以在把request交给mvc之间就处理异常, 更总要的是它还可以捕获并处理返回MVC相关代码执行中的异常.

然后别忘了把app.Run那部分代码去掉. 然后改回到Develpment环境, 跑一下, 试试效果:

Chrome显示了一个空白页, 按F12, 显示了404 Not Found错误.

这是因为我只添加了MVC middleware, 但是它啥也没做, 也没有找到任何可用于处理请求的代码, 所以我们要添加Controller来返回数据/资源等等.

Asp.net Core 2 Metapackage 和 Runtime Store

Asp.net core 2 metapackage, asp.net core 2.0开始, 所有必须的和常用的库也包括少许第三方库都被整和到了这个大而全的asp.net core 2 metapackage里面, 所以开发者就不必自己挨个库安装也没有版本匹配问题了.

Runtime Store, 有点像以前的GAC, 在系统里有一个文件夹里面包含所有asp.net core 2程序需要运行的库(我电脑的是: C:\Program Files\dotnet\store\x64\netcoreapp2.0), 每个在这台电脑上运行的asp.net core 2应用只需调用这些库即可. 

它的优点是:

  1. 部署快速, 不需要部署这里面包含的库;

  2. 节省硬盘空间, 多个应用程序都使用同一个store, 而不必每个程序的文件夹里面都部署这些库文件. 

  3. 程序启动更快一些. 因为这些库都是预编译好的.

缺点是: 服务器上需要安装.net core 2.0

但是, 也可以不引用Runtime Store的库, 自己在部署的时候挨个添加依赖的库.

Controller

首先建立一个Controllers目录, 然后建立一个ProductController.cs, 它需要继承Microsoft.AspNetCore.Mvc.Controller

我们先建立一个方法返回一个Json的结果.

先建立一个Dto(Data Transfer Object) Product:

namespace CoreBackend.Api.Dtos
{  
  
public class Product{      
 
public int Id { get; set; }      
 
public string Name { get; set; }  
     
public decimal Price { get; set; }} }

然后在Controller里面写这个Get方法:

namespace CoreBackend.Api.Controllers
{   
 
public class ProductController: Controller{      
  
public JsonResult GetProducts(){            

return new JsonResult(new List<Product>{                new Product{Id = 1,Name = "牛奶",Price = 2.5f},                new Product{Id = 2,Name = "面包",Price = 4.5f}});}} }

然后运行, 并使用postman来进行请求:

请求的网址返回404 Not Found, 因为还没有配置路由 Routing, 所以MVC不知道如何处理/映射这些URI.

Routing 路由

路由有两种方式: Convention-based (按约定), attribute-based(基于路由属性配置的). 

其中convention-based (基于约定的) 主要用于MVC (返回View或者Razor Page那种的).

Web api 推荐使用attribute-based.

这种基于属性配置的路由可以配置Controller或者Action级别, uri会根据Http method然后被匹配到一个controller里具体的action上.

常用的Http Method有:

  • Get, 查询, Attribute: HttpGet, 例如: '/api/product', '/api/product/1'

  • POST, 创建, HttpPost, '/api/product'

  • PUT 整体修改更新 HttpPut, '/api/product/1'

  • PATCH 部分更新, HttpPatch, '/api/product/1'

  • DELETE 删除, HttpDelete, '/api/product/1

还有一个Route属性(attribute)也可以用于Controller层, 它可以控制action级的URI前缀.

namespace CoreBackend.Api.Controllers
{ 
   
//[Route("api/product")][Route("api/[controller]")]  
 
public class ProductController: Controller{      
 [HttpGet]      
 
public JsonResult GetProducts(){        
   
return new JsonResult(new List<Product>{            
new Product{Id = 1,Name = "牛奶",Price = 2.5f},                new Product{Id = 2,Name = "面包",Price = 4.5f}});}} }

使用[Route("api/[controller]")], 它使得整个Controller下面所有action的uri前缀变成了"/api/product", 其中[controller]表示XxxController.cs中的Xxx(其实是小写).

也可以具体指定, [Route("api/product")], 这样做的好处是, 如果ProductController重构以后改名了, 只要不改Route里面的内容, 那么请求的地址不会发生变化.

然后在GetProducts方法上面, 写上HttpGet, 也可以写HttpGet(). 它里面还可以加参数,例如: HttpGet("all"), 那么这个Action的请求的地址就变成了 "/api/product/All".

运行结果:

我们把获取数据的代码整理成一个ProductService, 然后保证程序运行的时候, 操作的是同一批数据:

namespace CoreBackend.Api.Services
{    

public class ProductService{        

public static ProductService CurrentProducts { get; } = new ProductService();    
    
public List<Product> Products { get; }        

private ProductService(){Products = new List<Product>{                new Product{Id = 1,Name = "牛奶",Price = 2.5f},                new Product{Id = 2,Name = "面包",Price = 4.5f},                new Product{Id = 3,Name = "啤酒",Price = 7.5f}};}} }


然后修改一下Controller里面的代码:


namespace CoreBackend.Api.Controllers
{[Route("api/[controller]")]  
 
public class ProductController: Controller{[HttpGet]    
   
public JsonResult GetProducts(){          
 
return new JsonResult(ProductService.Current.Products);}} }


也是同样的运行效果.

再写一个查询单笔数据的方法:

        [Route("{id}")]        public JsonResult GetProduct(int id){            return new JsonResult(ProductService.Current.Products.SingleOrDefault(x => x.Id == id));}

这里Route参数里面的{id}表示该action有一个参数名字是id. 这个action的地址是: "/api/product/{id}"

测试一下:

如果请求一个id不存在的数据:

Status code还是200, 内容是null. 因为框架找到了匹配uri的action, 所以不会返回404, 但是我们如果找不到数据的话, 应该返回404错误才比较好.

Status code

http status code 是reponse的一部分, 它提供了这些信息: 请求是否成功, 失败的原因. 

web api 能涉及到的status codes主要是这些:

200: OK

201: Created, 创建了新的资源

204: 无内容 No Content, 例如删除成功

400: Bad Request, 指的是客户端的请求错误.

401: 未授权 Unauthorized.

403: 禁止操作 Forbidden. 验证成功, 但是没法访问相应的资源

404: Not Found 

409: 有冲突 Conflict.

500: Internal Server Error, 服务器发生了错误.

返回Status Code

目前我们返回的JsonResult继承与ActionResult, ActionResult实现了IActionResult接口.

因为web api不一定返回的都是json类型的数据, 也不一定只返回一堆json(可能还要包含其他内容). 所以JsonResult并不合适作为Action的返回结果.

例如: 我们想要返回数据和Status Code, 那么可以这样做:


        [HttpGet]     
       
public JsonResult GetProducts(){      
     
var temp = new JsonResult(ProductService.Current.Products){              
  StatusCode
= 200};        
return temp;}


但是每个方法都这么写太麻烦了.

asp.net core 内置了很多方法都可以返回IActionResult.

Ok, NotFound, BadRequest等等.

所以改一下方法:

namespace CoreBackend.Api.Controllers
{[Route("api/[controller]")]  
 
public class ProductController : Controller{[HttpGet]    
       
public IActionResult GetProducts(){          
          
return Ok(ProductService.Current.Products);}[Route("{id}")]    
   
public IActionResult GetProduct(int id){        
   
var product = ProductService.Current.Products.SingleOrDefault(x => x.Id == id);          
  
if (product == null){            
        
return NotFound();}          
          
return Ok(product);}} }

现在, 请求id不存在的数据时, 就返回404了.

如果我们用chrome直接进行这个请求, 它的效果是这样的:

StatusCode Middleware

asp.net core 有一个 status code middleware, 使用一下这个middleware看看效果:

        public void Configure(IApplicationBuilder app, IHostingEnvironment env){            if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}            else{app.UseExceptionHandler();}app.UseStatusCodePages(); // !!!app.UseMvc();}

现在更友好了一些.

子资源 Child Resources

有时候, 两个model之间有主从关系, 会根据主model来查询子model.

先改一下model: 添加一个Material作为Product子model. 并在Product里面添加一个集合导航属性.


namespace CoreBackend.Api.Dtos
{   
 
public class Product{      
 
public int Id { get; set; }      
 
public string Name { get; set; }    
   
public float Price { get; set; }    

   
public ICollection<Material> Materials { get; set; }}  

 
public class Material{      
  
public int Id { get; set; }    
   
public int Name { get; set; }} }


改下ProductService:

 View Code

创建子Controller

MaterialController:

namespace CoreBackend.Api.Controllers
{   
[Route(
"api/product")] // 和主Model的Controller前缀一样public class MaterialController : Controller{      
[HttpGet(
"{productId}/materials")]    
   
public IActionResult GetMaterials(int productId){          
  
var product = ProductService.Current.Products.SingleOrDefault(x => x.Id == productId);        
   
if (product == null){              
 
return NotFound();}            return Ok(product.Materials);}    
   [HttpGet(
"{productId}/materials/{id}")]  
     
public IActionResult GetMaterial(int productId, int id){          
  
var product = ProductService.Current.Products.SingleOrDefault(x => x.Id == productId);          
  
if (product == null){            
   
return NotFound();}          
 
var material = product.Materials.SingleOrDefault(x => x.Id == id);          
 
if (material == null){                return NotFound();}            return Ok(material);}} }

测试一下, 很成功:

结果的格式

asp.net core 2.0 默认返回的结果格式是Json, 并使用json.net对结果默认做了camel case的转化(大概可理解为首字母小写). 

这一点与老.net web api 不一样, 原来的 asp.net web api 默认不适用任何NamingStrategy, 需要手动加上camelcase的转化.

我很喜欢这样, 因为大多数前台框架例如angular等都约定使用camel case.

如果非得把这个规则去掉, 那么就在configureServices里面改一下:

        public void ConfigureServices(IServiceCollection services){services.AddMvc()                .AddJsonOptions(options =>{if (options.SerializerSettings.ContractResolver is DefaultContractResolver resolver){resolver.NamingStrategy = null;}});}

现在就是这样的结果了:

但是还是默认的比较好.

内容协商 Content Negotiation

如果 web api提供了多种内容格式, 那么可以通过Accept Header来选择最好的内容返回格式: 例如:

application/json, application/xml等等

如果设定的格式在web api里面没有, 那么web api就会使用默认的格式.

asp.net core 默认提供的是json格式, 也可以配置xml等格式.

目前只考虑 Output formatter, 就是返回的内容格式.

试试: json:

xml:

设置header为xml后,返回的还是json, 这是因为asp.net core 默认只实现了json.

可以在ConfigureServices里面修改Mvc的配置来添加xml格式:

        public void ConfigureServices(IServiceCollection services){services.AddMvc()                .AddMvcOptions(options =>{options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());});}

然后试试:

首先不写Accept Header:

然后试试accept xml :

 

原文地址:http://www.cnblogs.com/cgzl/p/7637250.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/323355.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

美妙的Github

这十天来&#xff0c;一度被自走棋带入了旋涡。没学到什么&#xff0c;但让我惊喜的是&#xff0c;游戏之余&#xff0c;我尝试了下Github。人们都讲Github是程序员的必备&#xff0c;连Github都不知道做什么程序员。玩了github之后我才是知道什么叫大佬。 通常我找源代码都是上…

白嫖之Github

文章目录[x]微信机器人这十天来&#xff0c;一度被自走棋带入了旋涡。没学到什么&#xff0c;但让我惊喜的是&#xff0c;游戏之余&#xff0c;我尝试了下Github。人们都讲Github是程序员的必备&#xff0c;连Github都不知道做什么程序员。玩了github之后我才是知道什么叫大佬。…

layer之弹层组件文档 layui.layer(v.1.9.0之后)

弹层组件文档 - layui.layer layer 至今仍作为 layui 的代表作&#xff0c;她的受众广泛并非偶然&#xff0c;而是这数年来的坚持、不弃的执念&#xff0c;将那些不屑的眼光转化为应得的尊重&#xff0c;不断完善和维护、不断建设和提升社区服务&#xff0c;在 Web 开发者的圈子…

设置宽带自动连接

刚接触电脑的小伙伴有没有这样的困扰&#xff0c;每次电脑开机的时候都要自己手动重新连接宽带&#xff0c;很麻烦。 而通过一些设置我们可以免除这样的麻烦&#xff0c;不需要输入账号密码&#xff0c;开机自动连接&#xff0c;每次开机省去个一两分钟的宽带连接时间&#xf…

随时随地以任意方式编写 .NET 应用程序

希望大家现在都知道&#xff0c;Microsoft .NET 不再仅适用于 Windows。借助 .NET Core&#xff0c;可以使用想要的语言&#xff08;C#、Visual Basic 或 F#&#xff09;编写应用程序&#xff0c;这些应用程序可以在选定的任何 OS&#xff08;Windows、macOS 或 Linux&#xff…

java实现如何定时给微信群中发送消息

大家好&#xff0c;我是雄雄。 前言 前几天&#xff0c;发了一个系列这样的文章&#xff0c;如下所示&#xff1a; java实现每日给女友微信发送早安等微信信息java实现给微信群中定时推送消息如何将每日新闻添加到自己博客中&#xff0c;发送到微信群中 基本都是说的一个事儿…

一张图理清ASP.NET Core启动流程

1. 引言 对于ASP.NET Core应用程序来说&#xff0c;我们要记住非常重要的一点是&#xff1a;其本质上是一个独立的控制台应用&#xff0c;它并不是必需在IIS内部托管且并不需要IIS来启动运行&#xff08;而这正是ASP.NET Core跨平台的基石&#xff09;。ASP.NET Core应用程序拥…

DevOps之发布系统V1.0

一、发布系统架构 &#xff08;1&#xff09;普通发布 &#xff08;2&#xff09;微服务发布 二、子系统与功能模块 &#xff08;1&#xff09;代码仓库 github、gitlab、svn &#xff08;2&#xff09;构建机 jenkins、maven构建机、本地构建包 &#xff08;3&#xff0…

Nodejs安装及使用

现阶段nodejs我用的多的是它里面的npm js的各种库都可以用npm安装&#xff0c;十分方便&#xff0c;再也不用去网上找了 当然&#xff0c;主要还是github上面的很多项目都要用npm“解封” 那下面说下它的安装吧 进入官网 https://nodejs.org/en/ 它会自动检测你的电脑&#xf…

在ASP.NET Core上实施每个租户策略的数据库

不定时更新翻译系列&#xff0c;此系列更新毫无时间规律&#xff0c;文笔菜翻译菜求各位看官老爷们轻喷&#xff0c;如觉得我翻译有问题请挪步原博客地址 本博文翻译自&#xff1a; http://gunnarpeipman.com/2017/08/database-per-tenant/ 让我们继续使用ASP.NET Core web应用…

php动态网站

记得从五六月份就说要建一个可以注册登录的网站了&#xff0c;结果自己两个多月都没能建成&#xff0c;由于学习动态网站实在需要太多知识了&#xff0c;可以说我这几个月的时间都是花在网页上的&#xff0c;从刚开始的htmlcss 到后面的js&#xff0c;js花了太多太多时间&#…

从头编写 asp.net core 2.0 web api 基础框架 (2)

上一篇是: 从头编写 asp.net core 2.0 web api 基础框架 (1) Github源码地址是: https://github.com/solenovex/Building-asp.net-core-2-web-api-starter-template-from-scratch 本文讲的是里面的Step 2. 上一次, 我们使用asp.net core 2.0 建立了一个Empty project, 然后做了…

java实现上传网络图片到七牛云存储

大家好&#xff0c;我是雄雄。 前言 最近阳了&#xff0c;第二条杠红的发紫&#xff0c;真难受啊&#xff0c;但是吧&#xff0c;博客上有个bug&#xff0c;不解决感觉比阳了还难受。 话还是要从博客的图片显示不出来这里说起&#xff0c;当时做的时候&#xff0c;在发文章这…

gRPC官方快速上手学习笔记(c#版)

上手前准备工作 支持操作系统&#xff1a;windows、OS X、Linux。实例采用.net、.net core sdk。 The .NET Core SDK command line tools. The .NET framework 4.5 (for OS X and Linux, the open source .NET Framework implementation, “Mono”, at version 4, is suitable…

spring cloud+dotnet core搭建微服务架构:Api网关(三)

前言 国庆假期&#xff0c;一直没有时间更新。 根据群里面的同学的提问&#xff0c;强烈推荐大家先熟悉下spring cloud。文章下面有纯洁大神的spring cloud系列。 上一章最后说了&#xff0c;因为服务是不对外暴露的&#xff0c;所以在外网要访问服务必须通过API网关来完成&…

java实现人脸识别源码【含测试效果图】——前期准备工作及访问提示

注意&#xff1a; 看完之后如有不懂&#xff0c;请看&#xff1a;关于人脸和指纹识别共同交流方案&#xff0c;也可以关注微信公众号&#xff1a;雄雄的小课堂&#xff0c;回复&#xff1a;人脸识别群获取群号&#xff0c;群内有直接可以运行的源码可供下载&#xff0c;人脸识…

JS原型链与instanceof底层原理

转载自 JS原型链与instanceof底层原理 一、问题&#xff1a; instanceof 可以判断一个引用是否属于某构造函数&#xff1b; 另外&#xff0c;还可以在继承关系中用来判断一个实例是否属于它的父类型。 老师说&#xff1a;instanceof的判断逻辑是&#xff1a; 从当前引用的…

正则之注册登录

不久前写了个登录注册的网站&#xff0c;因为未对其做出限制&#xff0c;所以&#xff0c;随便你输入什么都可以注册成功&#xff0c;遂想怎么通过js规定注册的账号 我的要求是&#xff1a; 一&#xff1a;输入框不能为空&#xff0c;不能太长也不能太短 二&#xff1a; 1、注…

猿创征文|从酒店前台收银到软件研发教学主管到技术经理之路~

大家好&#xff0c;我是雄雄。 内容先知&#x1f449;前言☝酒店收银&#x1f928;项目组长&#x1f91c;OA管理系统&#x1f91c;酒店管理系统&#x1f468;‍&#x1f3eb;软件研发讲师&#x1f4cc;学术主管&#x1f468;‍&#x1f4bb;技术经理&#x1f449;项目情况&…

微服务~分布式事务里的最终一致性

本地事务ACID大家应该都知道了&#xff0c;统一提交&#xff0c;失败回滚&#xff0c;严格保证了同一事务内数据的一致性&#xff01;而分布式事务不能实现这种ACID&#xff0c;它只能实现CAP原则里的某两个&#xff0c;CAP也是分布式事务的一个广泛被应用的原型&#xff0c;CA…