c# 匿名方法(函数) 匿名委托 内置泛型委托 lamada - 伊一线天 - 博客园

mikel阅读(270)

来源: c# 匿名方法(函数) 匿名委托 内置泛型委托 lamada – 伊一线天 – 博客园

匿名方法:通过匿名委托 、lamada表达式定义的函数具体操作并复制给委托类型;

匿名委托:委托的一种简单化声明方式通过delegate关键字声明;

内置泛型委托:系统已经内置的委托类型主要是不带返回值的Action<T1,,,,Tn>和带返回值的Func<T1,,,Tn,Tresult>

实例代码(运行环境netcoreapp3.1)

复制代码
class demoFunc
        {
            /// <summary>
            /// 定义函数单条语句直接用lamada表达式
            /// </summary>
            /// <param name="x"></param>
           public void funcA( string x)=> Console.WriteLine("this is funcA!{0}",x);
           /// <summary>
           /// 使用内置泛型委托action(返回值为void) 定义委托类型成员变量,并通过那lamada定义匿名函数
           /// </summary>
           public Action<string> funcB= x => Console.WriteLine("this is funcB!{0}", x);
           /// <summary>
           /// 使用内置泛型委托action(返回值为void) 定义委托类型成员变量,
           /// 并通过匿名委托定义匿名函数
           /// </summary>
           public Action<string> FuncB_1= delegate(string s)
           {
               Console.WriteLine("this is funcB_1!{0}", s);
           };
           /// <summary>
           /// 定义委托类型
           /// </summary>
           /// <param name="s"></param>
           public  delegate  void TFuccB_2(string s);
           /// <summary>
           /// 使用匿名函数声明委托
           /// </summary>
           public TFuccB_2 FuncB_2= delegate(string s) {
               Console.WriteLine("this is funcB_2!{0}", s);
           };

           /// <summary>
           /// 使用内置泛型委托func(返回值不可以为void,参数列表中最后一个时返回值),
           /// 定义委托类型成员变量,并通过lamada定义单含带返回值的匿名函数
           /// 单行表达式的返回值就是此匿名函数的返回值
           /// </summary>
           public Func<string, string> funcC=x=>  string.Format("this is funcC!{0}", x);

           /// <summary>
           /// 使用内置泛型委托func(返回值不可以为void),定义委托类型成员变量,
           /// 并通过lamada定义多行代码的匿名函数
           /// </summary>
           public Func<string,string, string> funcD= (x1, x2) =>
           {
               Console.WriteLine("this is funcd!{0}{1}", x1, x2);
               return string.Format("this is funcd!{0}{1}", x1, x2);
           };

        }
复制代码

运行测试代码

复制代码
static void Main(string[] args)
        {
            demoFunc demo=new demoFunc();
            demo.funcA("a");
            demo.funcB("b");
            demo.FuncB_1("b");
            demo.FuncB_2("b");
            Console.WriteLine(demo.funcC("c"));
            Console.WriteLine(demo.funcD("d1","d2"));
            
            Console.WriteLine("Hello World!");
        }
复制代码

运行结果

复制代码
  1 "C:\Program Files\dotnet\dotnet.exe" C:/Users/edzjx/RiderProjects/testDemo/testDemo/bin/Debug/netcoreapp3.1/testDemo.dll
  2 this is funcA!a
  3 this is funcB!b
  4 this is funcB_1!b
  5 this is funcB_2!b
  6 this is funcC!c
  7 this is funcd!d1d2
  8 this is funcd!d1d2
  9 Hello World!
 10 
 11 Process finished with exit code 0.
 12
复制代码

asp.net core 3.1 入口:Program.cs中的Main函数_伊一线天的博客-CSDN博客

mikel阅读(288)

来源: asp.net core 3.1 入口:Program.cs中的Main函数_伊一线天的博客-CSDN博客

本文分析Program.cs 中Main()函数中代码的运行顺序分析ASP.NET core程序的启动,重点不是剖析源码,而是理清程序开始时执行的顺序。到底用了哪些实例,哪些法方。

ASP.NET core 3.1 的程序入口在项目Program.cs文件里,如下。

ususing System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace WebDemo
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
1. Program类
program类是定义在项目根目录Program.cs文件中,所有.net core程序的入口,包括asp.net core 程序。这很有意思,就有点类似控制端程序。相比之前的web项目没有任何程序入口只有web.config、global这类配置文件和全局文件这种没有程序入口web项目,我觉得这类更容易理解asp.net core是怎么工作的。

在asp.net core 3.1中 的Program类里,定义了2个方法:Main() 和CreateHostBuilder()

2. 主程序入口Program.Main()方法
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
Main() 方法是整个项目的入口方法,就如同C系列语言,所有的程序入口都是。这里main()只有一行代码,但是实际上执行了三个函数C:

1 IHostBuilder builder= CreateHostBuilder(args);
2 IHost host=builder.Build();
3 host.Run();
第一行,是定义在Program类的CreateHostBuilder 它主要产生一个IhostBuilder实例builder。

第二行,通过builder.Build()法方产生一个Ihost实例 host。

第三含,通过host.Run()方法,开始运行web项目,这时候就可以响应各种请求了。

而这个过程里最繁琐的就是创建builder,本文重点篇幅就是在这里。

3. 创建并配置主机Builder:Program.CreateHostBuilder(args)法方
分解CreateHostBuilder(args) 的定义
此法方通过lamada表达式定义在Pogram类中。因为它就执行了一句化,所以可以用这种简便的方式定义(关于匿名函数、lamada、内置委托可以参考我之前的文章C# 匿名方法(函数) 匿名委托 内置泛型委托 lamada1)。为了方便阅读按照上面刨析Main()法方,所以它实际的定义如下:

1 public static IHostBuilder CreateHostBuilder(string[] args)
2 {
3 IHostBuilder builder = Host.CreateDefaultBuilder(args);
4 Action < IWebHostBuilder > configAction = delegate(IWebHostBuilder webBuilder)
5 {
6 webBuilder.UseStartup<Startup>();
7 };
8 builder=builder.ConfigureWebHostDefaults(configAction);
9 return builder;
10 }
3.1. builder诞生 :生成IHostBuilder
将从Host.CreateHostBuilder()法方分析入手
CreateHostBuilder()法方的第一行代码是调用Host.CreateDefaultBuilder(args)法方,来创建IBuilder 对象实例。

IHostBuilder builder = Host.CreateDefaultBuilder(args);
IHostBuilder是一个非常重要的实例,有了这个实例才可以继续后续的加载更多的配置操作。通过IHostBuilder.Build()生产IHost实例。最终通过IHost.Run()运行项目。

3.1.1 Host类——用于产生初始的builder静态类

Host类是定义在Microsoft.Extensions.Hosting命名空间(Program.cs中引用了该命名公开)下的静态类.在Program.cs里引入。

声明如下:

1 using Microsoft.Extensions.Hosting;
2
3 namespace Microsoft.Extensions.Hosting
4 {
5 public static class Host
6 {
7 public static IHostBuilder CreateDefaultBuilder();
8 public static IHostBuilder CreateDefaultBuilder(string[] args);
9 }
10 }
Host静态类就一个法方就是CreateDefaultBuilder() 。合计2个重载声明:一个带参数,一个不带参数。参考源码(.NET Core 3.0之深入源码理解Host(一)2)不带参数的重载实际在是将null作为参数调用带参数形式,如下:

1 public static IHostBuilder CreateDefaultBuilder() =>CreateDefaultBuilder(args: null);
3.1.2 Host.CreateDefaultBuilder(args)方法
官方说明是用预先配置的默认值初始化一个Microsoft.Extensions.Hosting.HostBuilder实例(Initializes a new instance of the Microsoft.Extensions.Hosting.HostBuilder class with pre-configured defaults,详细见参考官方文档3)。
相关操作有:设置根目录ContentRootPath ;给Host.IConfiguration 加载:环境变量EnvironmentName,命令行参数args,配置文件IConfiguration.[EnvironmentName] json;还有加载日志模块等等。
详细见参考文档2、3,其中.NET Core 3.0之深入源码理解Host(一),作者从源码角度剖析了此法方,官方文档Host.CreateDefaultBuilder 方法官方说明了法方操作那些内容。

3.2. IHostBuilder转变成IWebHostBuilder
继续对Host.CreateHostBuilder()法方分析,在3.1建立了IHostBuilder实例后,将通过builder.ConfigureWebHostDefaults(Action <IWebHostBuilder >)方法将IHostBuilder实例转变为一个web主机性质的webbuilder(IWebHostBuilder)
IHoustBuilder.ConfigureWebHostDefaults(Action <IWebHostBuilder>)方法定义在Microsoft.Extensions.Hosting.GenericHostBuilderExtensions静态类中(GenericHostBuilderExtensions.cs),此静态类就定义这一个静态方法。

3.2.1. ConfigureWebHostDefaults()参数
此处参数configure是一个内置委托Action <IWebHostBuilder>,这个委托的定义就执行一行代码:
webBuilder.UseStartup<Startup>();

3.2.2. ConfigureWebHostDefaults()方法定义
其实ConfigureWebHostDefaults(Action <IWebHostBuilder >)方法是一个IHostBuilder的扩展方法(所以之前的写法并不准确,缺少了this builder参数,之前省略了)。
为了方便阅读法,用3.中的方式码剖析如下:
1 public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure)
2 {
3 Action<IWebHostBuilder> webconfigure = delegate(IWebHostBuilder webHostBuilder)
4 {
5 WebHost.ConfigureWebDefaults(webHostBuilder);
6 configure(webHostBuilder);
7 };
8 return builder.ConfigureWebHost(webconfigure);
9 }
其实就调取了IHostBuilder的另一个扩展类ConfigureWebHost(Action<IWebHostBuilder>)(此扩展法方定义在Microsoft.Extensions.Hosting.GenericHostWebHostBuilderExtensions静态类中)。
3.2.2.1 把IHostBuilder转变为IWebHostBuilder: builder.ConfigureWebHost(webconfigure)法方
3.2.2.1.1 Action<IWebHostBuilder> webconfigure参数
内置委托,在这里对委托进行了定义,主要两行代码:
配置IWebHostBuilder的默认web设置。
WebHost.ConfigureWebDefaults(webHostBuilder);
在core2.1中该方法是在CreateHostBuilder()中WebHost.CreateDefaultBuilder(arg)中调用,core3.1,改为Host.ConfigureDefualts(arg)后,通过委托在ConfigureWebHost中调用。因为此方法在委托里调用,在下面章节降到委托执行的时候再详细说明。
调用参数传入的委托
configure(webHostBuilder);
就是3.2.1中的
webBuilder.UseStartup<Startup>();
具体在下面章节讲解委托执行时在详细说明。
3.2.2.1.2 builder.ConfigureWebHost(webconfigure)法方定义
定义在Microsoft.Extensions.Hosting.GenericHostWebHostBuilderExtensions(GenericHostWebHostBuilderExtensions.cs)
1 using System;
2 using Microsoft.AspNetCore.Hosting;
3 using Microsoft.AspNetCore.Hosting.Internal;
4
5 namespace Microsoft.Extensions.Hosting
6 {
7 public static class GenericHostWebHostBuilderExtensions
8 {
9 public static IHostBuilder ConfigureWebHost(this IHostBuilder builder, Action<IWebHostBuilder> configure)
10 {
11 var webhostBuilder = new GenericWebHostBuilder(builder);
12 configure(webhostBuilder);
13 return builder;
14 }
15 }
16 }

Microsoft.AspNetCore.Hosting.Internal.GenericWebHostBuilder
在asp.net core 2.1 里IWebHostBuilder 在asp.net core 3.1里是怎么把泛型IHostBuilder生产webhost的builder的了,终于扒到它了。
通过Microsoft.AspNetCore.Hosting.Internal.GenericWebHostBuilder类实例。GenericWebHostBuilder是一个内部类。这个类网上资源极少,甚至在官方文档里找不到说明,但可以参考其源码GenericWebHostBuilder.cs源代码。此类声明和其构造函数声明如下:
internal class GenericWebHostBuilder : IWebHostBuilder, ISupportsStartup, ISupportsUseDefaultServiceProvider
{
public GenericWebHostBuilder(IHostBuilder builder);
}
此类非常重要,它是Asp.net core 3.1 项目中IHostBuilder转变成IWebHostBuilder的基石或者源头 一个IHostBuilder实例最早就是通过此类构造函数实例化后变成了2.1里的IWebHostBuilder。实际干的就是注入了一些web有关的服务,详细建议查看源码(参考文档4)
3.2.2.2 给IWebHostBuilder配置Web相关参数委托参数:终于执行委托了
configure(webhostBuilder);
在传入了那么多层委托后,终于我们扒到底了,在实现了IHostBuilder转变为IWebHostBuilder后,所有config有关的委托终于得以执行
1 //在 builder.ConfigureWebHostDefaults()(–>在program.GreateHostBuilder()中)
2 //和下一句代码一同作为委托参数传入builder.ConfigureWebHost()
3 WebHost.ConfigureWebDefaults(webHostBuilder);
4 //Program.CreateHostBuilder()里的作为委托参数传入builder.ConfigureWebHostDefaults()
5 //再经过builder.ConfigureWebHostDefaults()合上一句一同作为委托传入builder.ConfigureWebHost()
6 webBuilder.UseStartup<Startup>();
3.2.2.2.1 WebHost.ConfigureWebDefualts(IWebHostBuilder)方法:
此方法在Asp.net core 2.1中 是WebHost.CreateDefaultBuilder(args) 里最后调用的方法,而在Asp.net core 3.1中,把IWebHostBuilder该为泛型IHostBuilder后,在执行IHostBuilder.ConfigureWebHost()时回调委托执行。此方法作用类似与Host.CreateDefaultBuilder(args),使用预配置配置一些关于Web方式的参数。主要是注入一些web相关的服务,配置主机地址等。此方法没有官方文档说明,建议直接查看源码WebHost.cs源代码(参考文档5)。
3.2.2.2.2 webBuilder.UseStartup<Startup>()法方:
UseStartup()是webbuilder的扩展发放,定义在Microsoft.AspNetCore.Hosting.WebHostBuilderExtensions静态类中(WebHostBuilderExtensions.cs),此方法申明如下
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType)
从方法名就可以看出是使用Startup类,这个方法主要使用反射技术,反射Startup类,响应startup类中的配置的信息

3.3. 最后梳理一下builder核心的代码
1.IHostBuilder builder = Host.CreateDefaultBuilder(args);
创建一个基础builder(读取app.json)
2.IWebHostBuilder webHostBuilder= new GenericWebHostBuilder(builder);
转换为webbuilder
3.WebHost.ConfigureWebDefaults(webHostBuilder);
给webbuilder使用预先的默认值配置
4.webBuilder.UseStartup<Startup>();
读取startup类配置信息

4.总结
aps.net core 3.1的程序启动代码分析下来。基本3个概念创建builder ,通过builder生成host ,最后使用host执行起来。其中builder及builder配置 ,host主机都是重要的概念。core 3.1 和 core2.1的区别,就是把IWebHostBuilder上再抽象一个IHostBuilder,可以是core 3.1的代码更加的灵活,以后的服务可以不单单是web服务。
另外asp.net core 的builder 配置中可以看到很多非常依赖,控制反转技术也就是注入依赖,好处就是想要什么服务就注册什么服务,更加灵活。
如果想深入学习,建议参考如下文章:

1. .NET Core 3.0之深入源码理解Host(一)
2. .NET Core 3.0之深入源码理解Host(二)
作者的这个2个文章从源码角度简介了Ihostbuilder的相关知识剖析深度更深
3. 官方文档:.NET 通用主机 讲解IHostBuilder相关知识
4. 官方文档:ASP.NET Core 中的应用启动 介绍startup类相关知识

参考文档:
1. C# 匿名方法(函数) 匿名委托 内置泛型委托 lamada 作者:edzjx
2. .NET Core 3.0之深入源码理解Host(一) 作者:艾心
3. 官方文档Host.CreateDefaultBuilder 方法 作者:Micrsoft官方文档
4. GenericWebHostBuilder.cs源代码 作者:Asp.net@github
5. WebHost.cs源代码 作者:Asp.net@github
6. .NET Core 3.0之深入源码理解Host(二) 作者:艾心
7. 官方文档:.NET 通用主机 作者:Micrsoft官方文档
8. 官方文档:ASP.NET Core 中的应用启动 作者:Micrsoft官方文档
————————————————
版权声明:本文为CSDN博主「伊一线天」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/edzjx/article/details/104257596

List多个字段标识过滤 IIS发布.net core mvc web站点 ASP.NET Core 实战:构建带有版本控制的 API 接口 ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目 Using AutoFac - ~雨落忧伤~ - 博客园

mikel阅读(293)

来源: List多个字段标识过滤 IIS发布.net core mvc web站点 ASP.NET Core 实战:构建带有版本控制的 API 接口 ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目 Using AutoFac – ~雨落忧伤~ – 博客园

List多个字段标识过滤

 

class Program
{
public static void Main(string[] args)
{

List<T> list = new List<T>();
list.Add(new T() { orderid = 1, houseid = 1 });
list.Add(new T() { orderid = 1, houseid = 1 });
list.Add(new T() { orderid = 1, houseid = 2 });
list.Add(new T() { orderid = 1, houseid =3 });
list.Add(new T() { orderid = 2, houseid = 1 });
list.Add(new T() { orderid = 2, houseid = 2 });
list.Add(new T() { orderid = 2, houseid = 2 });
list.Add(new T() { orderid = 2, houseid = 3 });

var _list= list.Where((x, y) => list.FindIndex(a => a.orderid == x.orderid&&a.houseid==x.houseid) == y);

}
public class T {
public long orderid { get; set; }
public long houseid { get; set; }
}
}

 

_list 结果如下:

 

new T() { orderid = 1, houseid = 1 }
new T() { orderid = 1, houseid = 2 }
new T() { orderid = 1, houseid = 3 }
new T() { orderid = 2, houseid = 1 }
new T() { orderid = 2, houseid = 2 }
new T() { orderid = 2, houseid = 3 }

lista.Where(a => a.name == “…”);
lista.Where((x, y) => lista.FindIndex(a => a.name == x.name && a.age == x.age) == y); // 相当于 where 的第二个重载吧
Where<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate);
y 相当于 源元素的索引

FindIndex 返回第一个匹配索引

IIS发布.net core mvc web站点

 

 

这里只有操作步骤!

第一、查看IIS是否安装了 AspNetCoreModule,查看路径:IIS-》模块 查看

 

 

 

安装步骤

下载网址:https://www.microsoft.com/net/download/windows

下载完以后点击安装,安装完成以后重启电脑

 

第二、.net Core Mvc 发布站点

右键站点-》发布

 

选择IIS、FTP等=》点击发布=》选择文件系统=》保存路径自行设置(C:\Users\wxd\Desktop\EP.Front.Feeds.H5)=》最后一步:发布(Release/Debugger版本 自行选择)

 

 

 

第三、IIS部署站点

保存路径为第二步发布网站的路径,然后点击确定

 

修改应用程序池,无托管代码

 

 

设置一切OK以后访问网站

 

 

 

 

 

ASP.NET Core 实战:构建带有版本控制的 API 接口

 一、前言

在上一篇的文章中,主要是搭建了我们的开发环境,同时创建了我们的项目模板框架。在整个前后端分离的项目中,后端的 API 接口至关重要,它是前端与后端之间进行沟通的媒介,如何构建一个 “好用” 的 API 接口,是需要我们后端人员好好思考的。
在系统迭代的整个过程中,不可避免的会添加新的资源,或是修改现有的资源,后端接口作为暴露给外界的服务,变动的越小,对服务的使用方造成的印象就越小,因此,如何对我们的 API 接口进行合适的版本控制,我们势必需要首先考虑。

系列目录地址:ASP.NET Core 项目实战
仓储地址:https://github.com/Lanesra712/Grapefruit.VuCore

 二、Step by Step

项目总是在不断迭代的,某些时候,因为业务发展的需要,需要将现有的接口进行升级,而原有的接口却不能立刻停止使用。比如说,你开发了一个接口提供给爱啪啪 1.0 版本使用,后来爱啪啪的版本迭代了,需要接口返回的数据与原先 1.0 版本返回的数据不同了,这时候,接口肯定是需要升级的,可是如果直接升级原有的接口,还在使用 1.0 版本的用户不就 GG 了,因此,如何做到既可以让 1.0 版本的用户使用,也可以让 2.0 版本的用户使用就需要好好考虑了,常见的解决方案,主要有以下几种。

a)使用不同的 API 名称

最简单粗暴,需要变更接口逻辑时就重新起个 API 名称,新的版本调用新的 API 名称,旧的版本调用旧的 API 名称。

https://yuiter.com/api/Secret/Login ##爱啪啪 1.0
https://yuiter.com/api/Secret/NewLogin ##爱啪啪 2.0

b)在 Url 中标明版本号

直接将 API 版本信息添加到请求的 Url 中,调用不同版本的 API ,就在 URL 中直接标明使用的是哪个版本。

https://yuiter.com/api/v1/Secret/Login ##爱啪啪 1.0
https://yuiter.com/api/v2/Secret/Login ##爱啪啪 2.0

c)请求参数中添加版本信息

将 API 的版本信息作为请求的一个参数传递,通过指定参数值来确定请求的 API 版本。

https://yuiter.com/api/Secret/Login?version=1 ##爱啪啪 1.0
https://yuiter.com/api/Secret/Login?version=2 ##爱啪啪 2.0

d)在 header 中标明版本号

前端在请求 API 接口时,在 header 中添加一个参数用来表明请求的版本信息,后端通过前端在 header 中设置的参数来判断,从而执行不同的业务逻辑分支。

复制代码
复制代码
POST https://yuiter.com/api/Secret/Login
Host: yuiter.com  
api-version: v1   ##爱啪啪 1.0

POST https://yuiter.com/api/Secret/Login
Host: yuiter.com  
api-version: v2   ##爱啪啪 2.0
复制代码
复制代码

在 Grapefruit.VuCore 这个项目中,我选择将 API 的版本信息添加到请求的地址中,从而明确的指出当前请求的接口版本信息。

  1、Swagger 集成

  后端完成了接口之后,肯定需要告诉前端,不管是整理成 txt/excel/markdown 文档,亦或是写完一个接口就直接发微信告诉前端,总是要多做一步的事情,而 Swagger 则可以帮我们省去这一步。通过配置之后,Swagger 就可以根据我们的接口自动生成 API 的接口文档,省时,省力。当然,如果前端小姐姐单身可撩,而你碰巧有意的话,另谈。

Swagger 是一个可以将接口文档自动生成,同时可以对接口功能进行测试的开源框架,在 ASP.NET Core 环境下,主流的有 Swashbuckle.AspNetCore 和 NSwag 这两个开源框架帮助我们生成 Swagger documents。这里,我采用的是 Swashbuckle.AspNetCore

在使用 Swashbuckle.AspNetCore 之前,首先我们需要在 API(Grapefruit.WebApi) 项目中添加对于 Swashbuckle.AspNetCore 的引用。你可以直接右键选中 API 项目选择管理 Nuget 程序包进行加载引用,也可以通过程序包管理控制台进行添加引用,这里注意,使用程序包管理控制台时,你需要将默认的项目修改成 API(Grapefruit.WebApi) 项目。当引用添加完成后,我们就可以在项目中配置 Swagger 了。

Install-Package Swashbuckle.AspNetCore

ASP.NET Core 的本质上可以看成是一个控制台程序,在我们创建好的 ASP.NET Core Web API 项目中,存在着两个类文件:Program.cs 以及 Startup.cs。与控制台应用一样,Program 类中的 Main 方法是整个程序的入口,在这个方法中,我们将配置好的 IWebHostBuilder 对象,构建成 IWebHost 对象,并运行该 IWebHost 对象从而达到运行 Web 项目的作用。

在框架生成的 Program 类文件中,在配置 IWebHostBuilder 的过程时,框架默认为我们添加了一些服务,当然,这里你可以注释掉默认的写法,去自己创建一个 WebHostBuilder 对象。同时,对于一个 ASP.NET Core 程序来说,Startup 类是必须的(你可以删除生成的 Startup 类,重新创建一个新的类,但是,这个新创建的类必须包含 Configure 方法,之后只需要在 UseStartup<Startup> 中将该类配置为 Startup 类即可),这里如果不指定 Startup 类会导致启动失败。

在 Startup 类中,存在着 ConfigureServices 和 Configure 这两个方法,在 ConfigureServices 方法中,我们将自定义服务通过依赖注入的方式添加到 IServiceCollection 容器中,而这些容器中的服务,最终都可以在 Configure 方法中进行使用;而 Configure 方法则用于指定 ASP.NET Core 应用程序将如何响应每一个 HTTP 请求,我们可以在这里将我们自己创建的中间件(Middleware)绑定到 IApplicationBuilder 上,从而添加到 HTTP 请求管道中。

这里只是很粗略的说明了 ASP.NET Core 项目的启动过程,想要仔细了解启动过程的推荐园子里的这篇文章 =》ASP.NET Core 2.0 : 七.一张图看透启动背后的秘密,因为 ASP.NET Core 2.1 版本相比于 2.0 版本又有些改变,这里有一些不一样的地方需要你去注意。

当我们简单了解了启动过程后,就可以配置我们的 Swagger 了。Swashbuckle.AspNetCore 帮我们构建好了使用 Swagger 的中间件,我们只需要直接使用即可。

首先我们需要在 ConfigureServices 方法中,将我们的服务添加到 IServiceCollection 容器中,这里,我们需要为生成的 Swagger Document 进行一些配置。

复制代码
复制代码
services.AddSwaggerGen(s =>
{
    s.SwaggerDoc("v1", new Info
    {
        Contact = new Contact
        {
            Name = "Danvic Wang",
            Email = "danvic96@hotmail.com",
            Url = "https://yuiter.com"
        },
        Description = "A front-background project build by ASP.NET Core 2.1 and Vue",
        Title = "Grapefruit.VuCore",
        Version = "v1"
    });
});
复制代码
复制代码

之后,我们就可以在 Configure 方法中启用我们的 Swagger 中间件。

app.UseSwagger();
app.UseSwaggerUI(s =>
{
    s.SwaggerEndpoint("/swagger/v1/swagger.json", "Grapefruit.VuCore API V1.0");
});

此时,当你运行程序,在域名后面输入/swagger 即可访问到我们的 API 文档页面。因为项目启动时默认访问的是我们 api/values 的 GET 请求接口,这里我们可以打开 Properties 下的 launchSetting.json 文件去配置我们的程序默认打开页面。

从上面的图可以看出,不管是使用 IIS 或是程序自托管,我们默认打开的 Url 都是 api/values,这里我们将两种启动方式的 launchUrl 值都修改成 swagger 之后再次运行我们的项目,可以发现,程序默认的打开页面就会变成我们的 API 文档页面。

我们使用 API 文档的目的,就是为了让前端知道请求的方法地址是什么,需要传递什么参数,而现在,并没有办法显示出我们对于参数以及方法的注释,通过查看 Swashbuckle.AspNetCore 的 github 首页可以看到,我们可以通过配置,将生成的 json 文件中包含我们对于 Controller or Action 的 Xml 注释内容,从而达到显示注释信息的功能(最终呈现的 Swagger Doc 是根据之前我们定义的这个 “/swagger/v1/swagger.json” json 文件来生成的)。

右键我们的 API 项目,属性 =》生产,勾选上 XML 文档文件,系统会默认帮我们创建生成 XML 文件的地址,这时候,我们重新生成项目,则会发现,当前项目下会多出这个 XML 文件。在重新生成项目的过程中,你会发现,错误列表会显示很多警告信息,提示我们一些方法没有添加 XML 注释。如果你和我一样强迫症的话,可以把 1591 这个错误添加到上面的禁止显示警告中,这样就可以不再显示这个警告了。

创建好 XML 的注释文件后,我们就可以配置我们的 Swagger 文档,从而达到显示注释的功能。这里,因为我会在 Grapefruit.Application 类库中创建各种的 Dto 对象,而接口中是会调用到这些 Dto 对象的。因此,为了显示这些 Dto 上的注释信息,这里我们也需要生成 Grapefruit.Application 项目的 XML 注释文件。

PS:这里我是将每个项目生成的注释信息 xml 文档地址都放在了程序的基础路径下,如果你将 xml 文档生成在别的位置,这里获取 xml 的方法就需要你进行修改。

复制代码
复制代码
services.AddSwaggerGen(s =>
{
    //...

    //Add comments description
    //
    var basePath = Path.GetDirectoryName(AppContext.BaseDirectory);//get application located directory
    var apiPath = Path.Combine(basePath, "Grapefruit.WebApi.xml");
    var dtoPath = Path.Combine(basePath, "Grapefruit.Application.xml");
    s.IncludeXmlComments(apiPath, true);
    s.IncludeXmlComments(dtoPath, true);
});
复制代码
复制代码

当我们把 Swagger 配置完成之后,我们就可以创建具有版本控制的 API 接口了。

  2、带有版本控制的 API 接口实现

在请求的 API Url 中标明版本号,我不知道你第一时间看到这个实现方式,会想到什么,对于我来说,直接在路由信息中添加版本号不就可以了。。。em,这样过于投机取巧了。。。。

[Route("api/v1/[controller]")]//添加版本信息为v1
[ApiController]
public class ValuesController : ControllerBase
{
}

想了想,在 Url 中添加版本号,这个版本号是不是很像我们在 MVC 中使用的 Area。

Area 是 MVC 中经常使用到的一个功能,我们通常会将某些小的模块拆分成一个个的 Area,而这一个个的小 Area 其实就是这个 MVC 项目中的 MVC。通过为 controller 和 action 添加另一个路由参数 area,从而达到创建具有层次路由的结构。比如,这里,我们可以创建一个 Area 叫 v1,用来存储我们 1.x 版本的 API 接口,之后如果有新的 API 版本,新增一个 Area 即可,是不是很简单,嗯,说干就干。

右击我们的 API 项目,选择添加区域,新增的 Area 名称为 v1。

当 ASP.NET Core 的脚手架程序添加完成 Area 后,则会打开一个文件提示我们需要在 MVC 中间件中创建适用于 Area 的路由定义。

复制代码
复制代码
app.UseMvc(routes =>
{
  routes.MapRoute(
    name : "areas",
    template : "{area:exists}/{controller=Home}/{action=Index}/{id?}"
  );
});
复制代码
复制代码

当我们添加好路由规则定义后,我们在 Area 的 Controllers 文件夹下添加一个 WebAPI Controller。不同于 ASP.NET 中的 Area ,当我们在 ASP.NET Core 创建好一个 Area 之后,脚手架生成的文件中不再有 XXXAreaRegistration(XXX 为 Area 的名称)文件去注册这个 Area,而我们只需要在 Area 中的 Controller 中添加 Area 特性,即可告诉系统框架,这个 Controller 是在当前的 Area 下的。

如果你有自己尝试的话,就会发现,当我们创建好一个 v1 的 Area 后,这个请求的地址并没有按照我们的想法会体现在路由信息中,我们最后还是需要在 Route 中手动指明 API 版本。

复制代码
[Area("v1")]
[Route("api/v1/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
}
复制代码

这样的话,和最开始直接在路由信息中写死版本信息其实也就没什么差别了,上网搜了搜,发现巨硬爸爸,也早已为我们准备好了实现版本控制 API 的利器 – Microsoft.AspNetCore.Mvc.Versioning。

和上面使用 Swashbuckle.AspNetCore 的方式相同,在我们使用 Versioning 之前,需要在我们的 API 项目中添加对于该 dll 的引用。这里需要注意下安装的版本问题,因为 Grapefruit.VuCore 这个框架距离现在搭建也有几个月的时间了,在这个月初的时候 .NET Core 2.2 也已经发布了,如果你和我一样还是采用的 .NET Core 2.1 版本的话,这里安装的 Versioning 版本最高只能到 2.3。

Install-Package Microsoft.AspNetCore.Mvc.Versioning

当我们安装完成之后,就可以进行配置了。

复制代码
复制代码
public void ConfigureServices(IServiceCollection services)
{
    services.AddApiVersioning(o =>
    {
        o.ReportApiVersions = true;//return versions in a response header
        o.DefaultApiVersion = new ApiVersion(1, 0);//default version select 
        o.AssumeDefaultVersionWhenUnspecified = true;//if not specifying an api version,show the default version
    });
}
复制代码
复制代码

ReportApiVersions:这个配置是可选的,当我们设置为 true 时,API 会在响应的 header 中返回版本信息。

DefaultApiVersion:指定在请求中未指明版本时要使用的默认 API 版本。这将默认版本为1.0。

AssumeDefaultVersionWhenUnspecified:这个配置项将用于在没有指明 API 版本的情况下提供请求,默认情况下,会请求默认版本的 API,例如,这里就会请求 1.0 版本的 API。

这里,删除我们之前的创建的 Area 和默认的 ValuesController,在 Controllers 文件夹下新增一个 v1 文件夹,将所有 v1 版本的 Controller 都建在这个目录下。新建一个 Controller,添加上 ApiVersion Attribute 指明当前的版本信息。因为我采用的方案是在 Url 中指明 API 版本,所以,我们还需要在 Route 中修改我们的路由属性以对应 API 的版本。这里的 v 只是一个默认的惯例,你也可以不添加。

复制代码
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class VaulesController : ControllerBase
{
}
复制代码

当我们修改好我们的 Controller 之后,运行我们的项目,你会发现,API 文档中显示的请求地址是不对的,难道是我们的配置没起作用吗?通过 Swagger 自带的 API 测试工具测试下我们的接口,原来这里请求的 Url 中已经包含了我们定义的版本信息,当我们指定错误的版本信息时,工具也会告诉我们这个版本的接口不存在。

虽然我们请求的 Url 中已经带上了版本信息,但是 API 文档上显示的请求地址却是不准确的,强迫症,不能忍。这里,需要我们修改生成 Swagger 文档的配置代码,将路由中的版本信息进行替换。重新运行我们的项目,可以发现,文档显示的 Url 地址也已经正确了,自此,我们创建带有版本控制的 API 也就完成了。

复制代码
复制代码
public void ConfigureServices(IServiceCollection services)
{
    services.AddSwaggerGen(s =>
    {
        //...

        //Show the api version in url address
        s.DocInclusionPredicate((version, apiDescription) =>
        {
            var values = apiDescription.RelativePath
                .Split('/')
                .Select(v => v.Replace("v{version}", version));

            apiDescription.RelativePath = string.Join("/", values);

            return true;
        });
    });
}
复制代码
复制代码

 三、总结

本章使用了 Microsoft.AspNetCore.Mvc.Versioning 这一组件来实现我们对于 API 版本控制的功能实现,可能你会有疑问,我们直接在路由中写明版本信息不是更简单吗?在我看来,使用这一组件的目的,在于我们可以以多种的方式实现 API 版本控制的目的,如果哪天你不想在 Url 中指明版本信息后,你可以很快的使用别的形式来完成 API 的版本控制。另外,直接在路由中写上版本信息,是不是会显得我们比较 ‘low’,哈哈哈,开玩笑,最后祝大家圣诞快乐~~~

 

 

 

 

ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目

 一、前言

这几年前端的发展速度就像坐上了火箭,各种的框架一个接一个的出现,需要学习的东西越来越多,分工也越来越细,作为一个 .NET Web 程序猿,多了解了解行业的发展,让自己扩展出新的技能树,对自己的职业发展还是很有帮助的。毕竟,现在都快到9102年了,如果你还是只会 Web Form,或许还是能找到很多的工作机会,可是,这真的不再适应未来的发展了。如果你准备继续在 .NET 平台下进行开发,适时开始拥抱开源,拥抱 ASP.NET Core,即使,现在工作中可能用不到。
雪崩发生时,没有一片雪花是无辜的,你也不会知道那片雪花,会引起最后的雪崩。有些自说自话,见谅。

系列目录地址:ASP.NET Core 项目实战
仓储地址:https://github.com/Lanesra712/Grapefruit.VuCore

 二、Step by Step

在整个的开发过程中,后端应用使用 Visual Studio 2017 进行开发,对于前端项目,则是使用 Visual Studio Code 进行开发,嗯,使用专业的工具做相应的事。对于前端的 Vue 项目,我采用的是 Vue CLI 来进行构建的,当然,巨硬也为我们准备了一套 Vue 的模板,如何使用的方法可以在附录中进行查看。

  1、项目开发环境搭建

1.1、安装 .NET Core

.NET Core 与之前的 .NET Framework 不一样,它不再紧紧的耦合在 Windows 系统上了,因此,我们可以在支持的操作系统上以安装软件的形式安装我们的 .NET Core 开发环境。

打开官网的下载页面(.NET Downloads),找到 .NET Core,这里因为我们需要在当前环境进行开发,所以需要安装 .NET Core SDK,下载完成后,一路 Next 即可。

当我们安装完成后,打开控制台,输入命令,则会显示出我们本机安装的 .NET Core 版本。

dotnet --info ## 或者使用 dotnet --version 查看本机安装的 .NET Core 版本信息

在 .NET Core 中为我们提供了 .NET Core CLI 这一工具使我们使用命令行的方式创建我们的 .NET Core 应用,这里我们还是使用 VS 来创建我们的应用,有兴趣的朋友,可以看看园子里的这篇文章 =》.NET Core dotnet 命令大全

1.2、安装 Node.js & Vue CLI

在整个前后端分离的项目的搭建中,前端的 Vue 项目,是使用 Vue CLI 3 进行搭建的脚手架项目,而 Vue CLI 本质上是一个全局安装的 npm 包,通过安装这一 npm 包可以为我们提供终端里的 vue 命令,因此我们需要使用这一脚手架工具的前提,则是需要我们安装 Node.js 环境。
打开 Node.js 官网(Node.js),选择长期支持版下载,之后一路 Next 下去即可。目前的 Node.js 安装包中已经包含了 npm,因此,我们安装好 Node.js 即可。npm 可以类比于我们 .NET 平台的 Nuget,而默认我们安装的全局组件和缓存默认是在 C:\Users\用户名\AppData\Roaming 下,如果你想修改缓存的地址或者全局安装的包地址则需要自己配置环境,具体可参考 =》Node.js安装及环境配置之Windows篇

PS:Vue CLI 3 需要 Node.js 8.9 或更高版本 (推荐 8.11.0+)。

当 Node 环境安装好之后,我们就可以安装 Vue CLI 3 脚手架工具了,如果你之前已经全局安装了旧版本的 vue-cli (1.x 或 2.x),则需要先卸载旧版本的 Vue CLI。

npm uninstall vue-cli -g ## 卸载 vue-cli (1.x or 2.x)
npm install -g @vue/cli

安装之后,我们就可以在命令行中使用 vue 命令。

vue ## 查看 vue 相关帮助信息
vue --version ## 查看安装的 vue cli 版本信息

1.3、安装 Git

为代码添加版本控制是必须的,它可以详细的记录你的每一次操作,以及当你的某次作死导致的环境出错时,你可以很快的恢复环境。经常作死的表示,这个巨需要。
Git 作为一个分布式的版本控制系统,与 SVN 这种集中式的版本控制系统不同,我们的本地仓库不仅包含了我们的代码,还包含了每个人对代码的操作历史 log,而 SVN 的历史操作记录只存在于中央仓库中。
这样有什么好处呢?假如,某天中央仓库出错了需要重新创建,因为我们本地的代码不包含操作历史 log,你只能把代码重新放置到中央仓库,而文件的历史版本却丢失了。如果使用 Git 进行版本控制的话,因为我们本地的仓库是一个完整的包含历史操作记录的仓库,我们就可以毫无差别的重新搭建一个中央仓库。
Git 方面的学习教程,可以看看廖雪峰大神写的这一系列的教程 =》Git 教程
打开 Git 官网(Git)下载安装包安装,一路 Next 即可。安装完成后,开始菜单里出现 Git Bash 这个应用,则说明我们的 Git 已经安装成功了。安装 Git 之后,我们需要设置我们的名字以及 Email,从而表明我们的身份,这里使用 Git Bash 设置即可。

git config --global user.name "Your Name" ## 全局设置用户名
git config --global user.email "email@example.com" ## 全局设置邮箱

  2、应用整体框架搭建

  当我们安装好项目开发的环境之后就可以搭建我们的项目框架了,这里我选择将前后端的项目放到同一个 Git 仓储中,你也可以根据你自己的喜好放到多个 Git 仓储中。
新建一个文件夹作为仓储,在创建好的文件夹路径下打开 Git Bash,初始化我们的仓储。如果你勾选了显示隐藏文件夹,则会发现,当我们执行好初始化的命令之后,则会在当前文件夹下创建一个 .git 文件夹。

git init

当然,你也可以使用 VS 进行创建 Git 仓储,使用 VS 创建仓储后会自动帮我们创建 .gitignore 和 .gitattributes 文件,同样的,后续对于该仓储的任何 Git 操作,我们也可以通过 VS 进行。
gitignore 文件表示我们需要忽略的文件或目录,而 gitattribute 则用于设置非文本文件的对比方式,这里我们使用 VS 创建 Git 仓储后生成的 gitignore 文件默认会添加 .NET 项目需要忽略提交的文件和目录。因为,前端的项目我是使用 VS Code 进行开发的,这里,我需要将一些 VS Code 生成的文件也添加到 gitignore 文件中。

.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

创建 ASP.NET Core Web API 的具体过程就不演示了,这里采用的就是基础的多层架构,当我们创建好项目之后,可以看到 VS 右下角铅笔 icon 处会显示我们未做提交的修改。点击 icon ,输入我们的提交信息后,就可以将我们的修改提交到仓储中。

后端的 API 接口应用创建好了,现在我们使用 Vue CLI 来构建我们前端的 Vue 项目。这里,我选择在解决方案的根目录创建我们的前端项目。
在 Vue CLI 3 中,我们不仅可以使用 vue create 命令来创建我们的项目,而且可以使用图形化的页面创建我们的应用。

vue create project-name ## 使用命令行的形式创建
vue ui ## 使用图形化的方式创建

当使用 vue ui 命令后会自动打开创建项目的页面,可以看到,这个路径下,并没有创建好的项目,你可以选择从别的路径下导入,或者是直接创建新的项目。如果你有使用过 Vue CLI 之前的版本,使用大写字母创建项目时是会报错的,但是在 Vue CLI 3 版本中没有出现这种问题。

因为我将前端项目与后端的项目放到同一个仓储中,所以这里就不需要再进行初始化 git 仓库了,对于项目的配置,这里就采用默认的配置。点击创建之后就会自动搭建我们的项目。如何启动这个脚手架项目,可以按照生成的 README 文件中的步骤执行。

到这里,基础的 Vue 脚手架项目就已经搭建完成了,对于添加插件之类的内容,放到我们后面的内容中。另外,虽然我们在创建项目时并没有勾选初始化 Git 仓储,但是 Vue CLI 还是创建了一个 gitignore 文件,如果你和我一样,是将前后端项目放到一个仓储的话,可以把这个文件里的内容复制到项目根目录中的 gitignore 文件中,然后把这个文件删除。

 三、附录

微软官方有提供一套 Vue 的 SPA 应用模板,不过并没有显示在我们使用 VS 创建项目的页面中,而且需要我们添加一个插件之后,使用 .NET Core CLI 的方式创建。因为自己并没有详细了解这块的内容,这里只列出创建的方法,详细的介绍请查看微软的官方文档(Building Single Page Applications on ASP.NET Core with JavaScriptServices )。

## 安装 SPA 模板
dotnet new --install Microsoft.AspNetCore.SpaTemplates::*

当你安装好模板之后,可以看到,多了使用 Aurelia、Vue、Knockout 创建 SPA 模板的选项,这时我们就可以使用 dotnet new 命令来创建包含 Vue 的模板应用。模板创建完成后需要安装依赖的包。加载完依赖的包之后,我们就可以通过 VS 或 VS Code 开发调试我们的项目。

dotnet new vue ## 创建 Vue SPA 项目
npm install ## 还原依赖的 npm 包

 四、总结

这一章没有包含很多的内容,主要就是如何搭建我们的 .NET Core 和 Vue 的开发环境,以及创建我们的项目架构,在后面的文章中则会慢慢的阐述整个项目的开发过程,希望可以能对你有一丢丢的帮助。

 

 

Using AutoFac

第一次整理了下关于autofac的一些具体的用法

1. 安装 Autofac: Install-Package Autofac -Version 4.8.1

2. 创建两个类库项目,IService (用于编写接口),ServiceImpl(用于创建实现类)

  • IService 下

public interface IAnimalBark
{
void Bark();
}

public interface IAnimalSleep
{
void Sleep();
}

public interface IUser
{
void AddNew(string name, string pwd);
}

public interface ISchool
{
void AfterSchool();
}

  • ServiceImpl下

public class Dog : IAnimalBark,IAnimalSleep
{
public void Bark()
{
Console.WriteLine(“汪汪汪汪汪”);
}

public void Sleep()
{
Console.WriteLine(“zZ,睡着了”);
}
}

public class Cat : IAnimalBark
{
public void Bark()
{
Console.WriteLine(“喵喵喵”);
}
}

public class User : IUser
{
public void AddNew(string name, string pwd)
{
Console.WriteLine(“添加新的用户:” + name);
}
}

public class School : ISchool
{
public IAnimalBark dog { get; set; }
public void AfterSchool()
{
dog.Bark();
Console.WriteLine(“放学了”);
}
}

3. 原理性使用方法,如果再有一个接口和一个实现类,那就再注册一次

ContainerBuilder builder = new ContainerBuilder();
//注册实现类Dog,当我们 请求IAnimalBark接口 的时候返回的是类Dog的对象,原理性的代码
builder.RegisterType<Dog>().As<IAnimalBark>();
//上面一句也可改成下面一句,这样 请求Dog实现的任何接口 的时候都会返回Dog对象,原理性的代码
//builder.RegisterType<Dog>().AsImplementedInterfaces();
IContainer container = builder.Build();
//请求IAnimalBark接口
IAnimalBark dog = container.Resolve<IAnimalBark>();
dog.Bark();
Console.ReadKey();

4. 如果有很多接口,很多实现类,每次都要注册一次会很麻烦,可以如下进行一次注册

ContainerBuilder builder = new ContainerBuilder();
Assembly asm = Assembly.Load(“Service”);//实现类所在的程序集名称
builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces();//常用
IContainer container = builder.Build();
IAnimalBark dog = container.Resolve<IAnimalBark>();
IUser user = container.Resolve<IUser>();
dog.Bark();
user.AddNew(“baidu”,”123″);
Console.ReadKey();

5. 如果有多个实现类,container.Resolve<IAnimalBark>();只会返回其中一个对象,如果想返回多个类的对象,应改成container.Resolve<IEnumerable<IAnimalBark>>();

ContainerBuilder builder = new ContainerBuilder();
Assembly asm = Assembly.Load(“Service”);//实现类所在的程序集名称
builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces();//常用
IContainer container = builder.Build();
IEnumerable<IAnimalBark> animals = container.Resolve<IEnumerable<IAnimalBark>>();
foreach(var animal in animals)
{
animal.Bark();
}
Console.ReadKey();

6. 如果一个实现类中定义了其他类型的属性(接口类型的属性),在注册时又加上builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces().PropertiesAutowired();这样会给属性进行“注入”

ContainerBuilder builder = new ContainerBuilder();
Assembly asm = Assembly.Load(“Service”);//实现类所在的程序集名称
builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces().PropertiesAutowired();//常用
IContainer container = builder.Build();
ISchool school = container.Resolve<ISchool>();
school.AfterSchool();
Console.ReadKey();

7. 可以通过在builder.RegisterAssemblyTypes(asm)后面以 Instance***()配置来实现Auto对象的生命周期

  •  InstancePerDependency()每次Resolve都返回新的对象

ContainerBuilder builder = new ContainerBuilder();
Assembly asm = Assembly.Load(“Service”);//实现类所在的程序集名称
builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces().PropertiesAutowired().InstancePerDependency();//常用
IContainer container = builder.Build();
ISchool school = container.Resolve<ISchool>();
ISchool school2 = container.Resolve<ISchool>();
Console.WriteLine(school.Equals(school2));
Console.ReadKey();

  • SingleInstance()每次Resolve都返回同一个对象,推荐

ContainerBuilder builder = new ContainerBuilder();
Assembly asm = Assembly.Load(“Service”);//实现类所在的程序集名称
builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces().PropertiesAutowired().SingleInstance();//常用
IContainer container = builder.Build();
ISchool school = container.Resolve<ISchool>();
ISchool school2 = container.Resolve<ISchool>();
Console.WriteLine(object.ReferenceEquals(school,school2));
Console.ReadKey();

 

【IIS】设置URL重定向 - 狼窝窝 - 博客园

mikel阅读(344)

来源: 【IIS】设置URL重定向 – 狼窝窝 – 博客园

名字解释:

URL转发分为隐含转发和非隐含转发,隐含转发就是指当前域名转发后,仍然显示当前域名,而非隐含转发者指当前域名转发后,显示被转发的地址。

 

前提:

你有一个A网站:地址是:192.168.1.223:8700

B网站:地址是 www.baidu.com

现在要在输入A网站地址后,直接跳转到B网站中【旧版域名转新版域名类似的需求】

 

具体操作:

1、在IIS上安装ARR,下载地址(http://www.iis.net/downloads/microsoft/application-request-routing)

2、完成后打开,Microsoft Web Platform Installer

 

 

3、搜索arr关键字,安装应用程序请求路由

 

4、搜索url关键字,安装url重写工具

 

5、重启服务器,才能在iis目录下看到新安装的ARR

 

6、打开ARR,在右侧功能菜单中点击   Server Proxy Settings

 

7、勾选 Enable proxy,然后点击应用

 

8、打开你要设置URL重写的网站目录,此时就能看到一个URL重写的功能

 

9、双击打开,添加规则

此时可以在当前站点增加Index.html页面,里面说明即将要做的事情。

然后规则名随便起,模式输入:^(./*)  代表匹配当前网站192.168.1.223:8700,跳转规则为输入  192.168.1.223:8700/ss  则默认都跳转至新页面

下面的URL重定向页面直接写你的目标地址即可

 

 

 

PS:如果想在输入 192.168.1.223:8700的地址时就立刻跳转到百度,则需要设置HTTP重定向

 

 

另附:

IIS配置Url重写实现http自动跳转https的重定向方法(100%解决)

 

 

 

 

 

参考:

https://blog.51cto.com/cold2076goddess/1542583

https://blog.csdn.net/kelly921011/article/details/87889062    隐含转发

https://my.oschina.net/tanyixiu/blog/123832      隐含转发

https://blog.csdn.net/kelly921011/article/details/87889062   隐含转发

解决客户端和服务器不支持一般SSL协议版本或加密套件问题_客户端和服务器不支持常用的ssl协议版本或密码套件_Huang-Bo的博客-CSDN博客

mikel阅读(382)

来源: 解决客户端和服务器不支持一般SSL协议版本或加密套件问题_客户端和服务器不支持常用的ssl协议版本或密码套件_Huang-Bo的博客-CSDN博客

错误信息
详细报错信息如下图

错误原因
这种错误通常表示客户端和服务器之间存在协议版本或加密套件不匹配的情况。在SSL(Secure Socket Layer)连接过程中,客户端和服务器需要协商一种相同的加密协议版本和加密套件,以确保数据的安全传输。
具体可能出现的原因包括:
1.协议版本不兼容:客户端和服务器可能支持不同版本的 SSL 协议,导致协议版本不匹配。

2.加密套件不匹配:客户端和服务器可能支持不同的加密套件,导致加密套件不匹配。例如,如果客户端只支持使用 AES 加密算法,而服务器只支持使用 RC4 加密算法,则它们无法协商一种相同的加密套件。

3.协议配置不正确:客户端和服务器的 SSL 协议配置可能不正确,例如使用不安全的协议版本或加密套件,导致连接失败。

解决方法
适用于Windows server 2008 R2 操作系统
1.配置支持高版本TLS协议,去到iis站点所在服务器修改注册表,配置路径如下:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols
在该层级下新建两个项,名称分别为TLS1.1和TLS1.2

2.然后在TLS1.1和TLS1.2项中再分别新增两个项Client和Server

3.然后分别再Client和Server项中新增两个(DWORD 32位值) TLS1.1和TLS1.2都要加
key:值
DisabledByDefault:0
Enabled: 1

client项

server项

4.最后重启服务器,再去到浏览器访问看效果

5.检测web站点支持的ssl协议版本号
https://www.ssllabs.com/ssltest/index.html
————————————————
版权声明:本文为CSDN博主「Huang-Bo」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Habo_/article/details/129203156

领域驱动模型DDD(一)——服务拆分策略 - 键盘三个键 - 博客园

mikel阅读(347)

来源: 领域驱动模型DDD(一)——服务拆分策略 – 键盘三个键 – 博客园

前言

领域驱动模型设计在业界也喊了几年口号了,但是对于很多“务实”的程序员来说,纸上谈“术”远比敲代码难得太多太多。本人能力有限,在拜读相关作品时既要隐忍书中晦涩难懂的专业名词,又要去迎合西方大牛在撰写的过程中融入的西式故事。我想总会有一部分水平和我类似的码农们,需要一份对系统阐述DDD小白化的文本。因此,本人便自不量力地结合一些简单的项目经验,将领域驱动模型设计思想从理解到落地的实施和总结分享给诸位。当然,如果是某些行业先锋不幸看到本人稚嫩的文字时,就当作是马戏团中的小丑,一笑了之翻页便可。

思维的入门

在学习架构思想的初期,特别时面对架构模型时(例如六边形架构、领域服务拆分),我总会不自然地在对号入座,思考在模型中的这一块放到代码实现上是Controller层还是Dao层,是采用消息中间件还是NoSQL缓存。这种自动联想的“被动技能”在学习微服务设计架构思想的过程中是致命的,过于专注业务的技术实现而脱离架构思想本身的大局观,就容易陷入用“具体”无法概括“抽象”的处境。

对此,让我们先忘了平日在开发中使用的各种被分类到细致入微的技术,带着一种阅读“无用”文学而非可以模仿实操的工具书的心态一起来对领域驱动设计做一个基础性的理解。

1 常用的服务拆分方法

1.1 根据业务能力进行服务拆分

创建微服务架构的策略之一就是采用业务能力进行服务拆分,这也是目前市面上大部分产品设计初期采用的方式。主要原因是这种方法对于架构师来说是比较容易实施的,就好像在做一个分类游戏,关于用户的注册登录以及信息管理可以划分为一个业务,关于文章的发布下架以及内容更迭可以划分为一个业务。

这样的分类手段核心是以业务活动进行划分,也保证了大部分面向对象的程序员在代码实现时可以更快地、更明确地创建实体类,从而开辟出一个个针对不同业务功能的微服务。

倘若我们以程序员盘踞的技术论坛/博客为例,那么它们必须要实现最基础的三大业务:1、用户的注册登录以及用户对个人信息的操作;2、用户可以发布/修改/删除自己的文章;3、网站的后台管理员可以对前面两者进行更高权限的操作。至于更多的用户评论、用户私聊、vip充值等一些功能则是拓展,毕竟没有它们也不会影响到整个网站的正常运作,因此暂不加入讨论。

所以根据上述最基础的三大业务,如果要进行微服务架构设计(当然现实中不会有为了如此简陋的网站采用微服务,不然会被从业的程序员背地挖苦痛骂),我们可以画出相应的映射图【图1-1】:

采用业务能力进行拆分固然方便了架构师和程序员,在应对一般的企业项目时,这种方式是非常稳妥的方案,架构中每个划分出的服务内部后期可以根随功能的需求增加而逐渐迭代(例如,我现在需要文章发布时可以附带图片,那么可以在实现文章业务的服务中添加相关的接口与具体实现代码),但是整体的项目架构是保持不变的。

话虽如此,但读者们要知道上面仅仅是为了实现技术博客网站最基本业务而定义的“初代”架构。一种情况是随着整个技术博客网站的日益庞大,为了迎合用户的需要,我们不得不扩展网站的功能性,例如增添用户对文章点赞/收藏/留言,后台对文章内容是否健康的审核等等,这时候初代被划分好的服务内部就会日益“庞大”,需要我们重新操刀对其进行分解切割。

另外一种情况是由于用户量的提升,致使服务之间的远程调用、进程间的通信次数骤增而导致请求响应效率逐步低下,例如拥有庞大数量的读者用户在访问文章时不仅要通过文章服务获取文章内容,还要通过文章的作者每次调用用户服务来获取作者个人信息简介(在没有缓存机制,每个服务根据业务主体分割明确的假设情况),我们又得考虑把一些服务组合在一起:

例如当我们现在常用的注册/登录手段——利用第三方服务进行短信验证,如果只是为了实现用户注册/登录功能,完全可以并入到用户的服务中【图1-2】:

一旦不单用户登录/注册情况用到短信验证,例如要充值时候也要用到短信验证支付,那么就得把它独立出来【图1-3】:

上面的问题正是以业务进行服务拆分时难以避免的,因为业务必然是不断发展的(参考市面上众多臃肿的软件),被繁杂的业务牵着鼻子走的架构模式必然会陷入到不停拆分或重组的境地,那么服务内部以及服务之间的关系也将逐步模糊紊乱。

1.2 根据子域进行服务拆分

即使这是一段很枯燥的历史,但为了感谢曾在这个领域躬耕的技术先驱们我还是要带上这段文字:Eric Evans在他的经典著作中(Addison-Wesley Professional)提出的领域驱动设计是构建复杂软件的方法论,这些软件通常都是以面向对象和领域模型为核心。

既然是方法论,它提供的是“怎么办”的理论体系。类比蛋炒韭菜,我所能提供的是做这道菜需要什么工具和佐料,放油放蛋放盐放韭菜的先后顺序和比例,至于真正做起来火候的大小,炒菜的姿势,蛋菜的克量等都是视情况而定,并没有固定的要求。这又回到我在“思维入门”中提到的,不要用机械的“具体”去反推“抽象”理论,因为炒菜的姿势最多能影响的是菜的口味,并非是完成这道菜的必要条件。

下面让我们来理解DDD中重要的两个概念:子域和界限上下文。我先以好理解的方式在各位脑海中勾勒出这两者的基本认识,然后各位再去看专业解释会好接受的多。

如果把一个项目业务比作一个国家整体,而子域相当于国家内部的各个省份,这些省份细致地划分了国家每个地域面积大小和居住人群,而所有省份的聚合又构成了整个国家。作为限界上下文,其英文为bounded context,可以直译成“有界的环境”,那么套入前文各位可能举一反三理解成“省界”。但事实并非如此,以“省份”类比子域的话,省界并非是限制人口流动的主要原因,限制人流的本质在于一个省份有一个省份的风土人情和文化语言,例如广东省用粤语交流,福建省用闽南语交流,这些语言在省份内部是大家达成共识都能理解的,但跑到广东说福建话只会让本地人摸不着头脑。

因此,每个省份(子域)之间产生的交流障碍归根于它们各自内部通用语言环境不同(限界上下文)。这个举例或许不符合社会学对于人口流动的分析,但拿来解释子域和限界上下文还是很贴切的。

那么让我们引入教科书对二者的解释来巩固诸位对它们的记忆:子域是领域的一部分,领域是DDD中用来描述应用程序问题域的一个术语。识别子域的方法和识别业务能力一样:分析业务并识别业务的不同专业领域,分析产生的子域定义结果也会和识别业务能力得到的结果非常相近。DDD把领域设计模型的边界成为界限上下文,当使用微服务架构时,每一个界限上下文对应一个或则一组服务,我们可以通过DDD方式定义子域,并把子域对应为每一个服务【图1-4】。

1.3 拆分单体应用的难点

1.网络延时

网络延时是分布式系统中一直存在的问题。不论是以业务进行拆分还是子域进行拆分,对服务不断细化分解会导致各个服务之间的大量往返调用。即使可以通过批量处理API在一次往返中获取多个对象,从而减少延时。但是在其他情况下,解决方案是把多个服务整合到一起,用变成语言中的函数调用替换昂贵的进程通信。

2.同步进程间通信导致可用性降低

举一个最简单的例子,当我们在某东下单一件商品时创建了订单,创建订单的过程中需要获取商品的详细信息和购买者的详细信息,而这其中有一个服务出现了不可用的状态就会导致整个业务创建失败,这种同步进程通信带来的可用性降低让我们不得不折中采用异步消息进行处理。

3.服务之间维持数据的一致性

当我们对服务进行拆分后,服务之间如何保持数据一致性成为重点和难点。还是以某东为例,在活动期间大量用户可能在同一刻参与了秒杀活动,而对于仓存服务与商品服务之间如何保证它们在数量的一致性(比如前台有100个用户下单,那么对于商品来说只需要在原有的数量上减去100然后返回给前端页面作为商品信息的一部分及时展示给用户还有多少存货便可,但是实际上真正需要扣减的应该发生在仓库服务中,因为仓库服务内存储的才是实际库存),怎么让一波狂欢后实际库存与前端保持一致是业务中必须攻克的难题。

4.上帝类阻碍了服务的拆分

分解的一部分障碍就是所谓的上帝类,即全局类或则是“公用”类。上帝类通常为应用程序不同方面实现业务逻辑。

以美团外卖业务举例,一个不经思考设计的订单类中会将以下所有信息属性直接构建成一个类:商家信息属性(商家名称、商家地址等)、用户信息属性(账户、昵称、地址等)、外卖商品属性(外卖商品名称、价格、配料等)、配送方属性(配送员身份、配送员电话号码、配送起始时间、配送截止时间等)。不过这些属性涉及了不同服务中的应用程序,导致了商品系统中必然存在的订单所包含的信息变得特别庞大,并且与其他服务之间因为共同的属性保持着“暧昧”的联系。

一种解决方法是在数据库中创立一个公用的订单数据库,处理订单的所有服务使用此数据库,但这就出现了“紧耦合”的情况。

对此应用DDD将每个服务视为“孤岛”般的子域(尽量不与其他服务发生在属性的上纠葛,形成一座具有特色便于识别的“孤岛”)。所以让我们看看自己手机上美团APP中客户订单,然后再看看拿到外卖时钉在外卖上的商家收到的订单,幸运的话再看看外卖小哥送外卖时接收的订单,它们所包含的信息内容和信息数量绝对是不同。

这代表着在商家子域、用户子域、配送子域中我们根据子域的不同定义了不同侧重点的“订单”,而不是一股脑地都塞进全局订单类里让不同服务进行共享。也正是不同侧重点的“订单”只有在自己子域中才是“有效的”,“能被读懂的”(总不可能让商家去看外卖小哥的订单信息,用户去看商家的订单信息),子域便有了与之对应的界限上下文。

结语

最后我想很多人一直疑惑DDD与微服务到底有什么区别or联系?若用我浅薄的认识来解释那便是:DDD是一个抽象的设计理念,而现今微服务在开发领域的生态技术十分契合地为DDD架构思想的具象化提供了可行性。但诚如各位在文中所见,服务的分割又不仅仅只有DDD这一种方案。DDD给开发者带来的永远不是某种可以更快捷地开发“工具”,甚至夸大一点来说它依赖于技术却超脱技术。或许对于一个狂热的设计者来说,DDD给予他们一个能够决定项目拥有何种生命力与活力的机会,这种犹如“艺术创造”的思维活动便是架构的真正魅力所在。

这一章的基础内容便到此结束,下一章可能会讲一下服务的进程通信或则是Saga管理事务,尽情期待。

本文作者:键盘三个键

本文链接:https://www.cnblogs.com/jianpansangejian/p/16047304.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

删除系统盘大文件“hiberfil.sys”的方法 - 知乎

mikel阅读(615)

来源: 删除系统盘大文件“hiberfil.sys”的方法 – 知乎

删除系统盘大文件“hiberfil.sys”的方法

最近我的电脑系统C盘空间满了,在清理磁盘空间时在系统盘下有两个超G的隐藏文件,一个是pagefile.sys,2G左右,一个是hiberfil.sys,6G左右,前面一个文件虚拟内存。

而“hiberfil.sys”是系统休眠文件,其大小和物理内存一样大,它可以删除掉,但却不能手动删除掉,只要在“控制面板”中打开“电源选项”,之后在电源管理对话框的“休眠”标签下,去掉“启用休眠”前的勾,重新启动计算机后,休眠文件就会自动删除。

更直接和简便的方法如下:

1. 如果使用了休眠功能,那么打开系统盘就会有一个很大的hiberfil.sys文件,它是将用户正在运行的程序,保存在这里,再启动系统就很快了。如要清理它,可以这样操作:

2. 开始→所有程序→附件→右键命令提示符→以管理员身份运行(否则可能会出错)。

3. 在命令提示符中输入:powercfg -hibernate off 按回车(Enter键)执行命令即可关闭休眠功能。

4. 再去开始菜单的关机选项中查看,休眠菜单已经没有了。

5. 再到系统盘查看,数G大小的hiberfil.sys文件也没有了(如还有可以自行删除)。

6. 如要再次开启休眠功能,可以在命令提示符中输入:powercfg -hibernate on 按回车(Enter键)执行命令即可打开休眠功能(无需重启电脑)。

7. 还可以在控制面板的电源选项中调整启用休眠的等待时间。

进入C:WindowsSystem32 找到cmd.exe 点右键—-以管理员身份运行
在cmd模式下输入命令“powercfg -h off”回车
重新启动计算机,再去看看已经没了!
(不需要关闭账户控制)

“pagefile.sys”是页面交换文件,即虚拟内存文件,这个文件不能删除,不过可以改变其大小和存放位置:文法如下:

1. 右击“我的电脑/属性”,然后在对话框的“高级”标签下单击“性能”下的“设置”按钮,在”性能选项”对话框中切换到“高级”标签下,再单击“虚拟内存”下的“更改”按钮,即可根据需要更改页面文件的大小。

2. 先选中C盘,然后选“无分页文件”,再点“设置”按钮;之后选中要生成该文件的盘符,在下面点选“自定义大小”并输入合适的数值,此数值通常为物理内存的1.5倍,再单击“设置”,最后单击“确定”就可以了。

3. 重新启动电脑,该文件就会存放到其他分区上了。

以下是该pagefile.sys文件的一些简单点的说明.
在一些较新的具有STR功能的主板上安装了xp及更新的操作系统后,在系统区根目录下会有一个 hiberfil.sys 的文件,它的大小和内存容量一样,这是什么文件呢?原来这个就是系统的休眠功能所占用的硬盘空间的文件(Windows 默认休眠功能是处于打开状态的),电脑处于休眠状态时,内存中的所有内容将保存到硬盘,并且电脑会被关闭。电脑在返回打开状态之后,关闭电脑时打开的所有程序和文档将全都还原到桌面。也就是说,当你让电脑进入休眠状态时,Windows 在关闭系统前将所有的内存内容写入hiberfil.sys文件。

而后,当你重新打开电脑,操作系统使用Hiberfil.sys把所有信息放回内存,电脑恢复到关闭前的状态。然而,Windows 在硬盘上留下了hiberfil.sys文件,即使这个文件不再需要了。

Hiberfil.sys文件可能很大,是磁盘碎片整理程序不能整理的特殊系统文件。因此,文件Hiberfil.sys的存在将阻止磁盘碎片整理程序进行彻底的整理操作。

交通部道路运输车辆卫星定位系统部标JTT808、809、796标准大全 - panchanggui - 博客园

mikel阅读(387)

来源: 交通部道路运输车辆卫星定位系统部标JTT808、809、796标准大全 – panchanggui – 博客园

无论是开发GPS设备硬件还是开发应用软件,都要面临一个标准,这个标准就是国家交通部发布的道路运输车辆卫星定位系统部标认证标准,它涵盖了GPS硬件设备参数、功能标准,也包括了设备上传到应用平台的协议标准,同时也包括了平台对平台的互联互传的技术标准。

也就是说凡是根据交通部这个标准开发的应用平台软件,都可以接入不同厂家开发的符合国标的GPS设备发送的上传数据。因为协议是同一的,所以平台也可以将数据转发给各地的省级交通部门的运管中心。

目前国家对需要上路的客车、危险品运输车,简称两客一危,要求必须要安装符合国标的GPS,如果运输承运者没有自己的软件平台,可以使用运管局的客户端软件,并接入到运管中心进行监控,收费对于企业来说也是个负担。现在国家对与GPS终端设备的标准又进行了进一步的扩展,要求必须要加上指纹等身份识别的读卡器,强制性加,费用不低。

平台软件开发商和GPS设备开发商安装标准开发后,需要通过交通部的部标检测和认证,获得部标认证后,这样才可以参加各地的软硬件招标。交通部对平台的功能测试,主要是模拟GPS终端发送标准的协议数据,然后测试是否能正确的接收数据。

GPS应用的开发者必须要了解并掌握这些协议,平台协议主要JT/TB808、809及扩展补充协议,同时协议中对于行车记录仪部分又应用了部颁的GB19056标准,所以这些都要看,JT/T796主要是硬件设备的标准,可以参考。

JTT 808-2011 道路运输车辆卫星定位系统终端通讯协议及数据格式

JTT 796-2011道路运输车辆卫星定位系统平台技术要求

JTT 794-2011北斗完善版-道路运输车辆卫星定位系统北斗兼容车载终端技术规范

JTT 809-2011 道路运输车辆卫星定位系统–平台数据交换

行车记录仪标准GB19056

行车记录仪标准GB19056 硬件参数标准

Vue vs React:你需要知道的一切 - chuckQu - 博客园

mikel阅读(281)

来源: Vue vs React:你需要知道的一切 – chuckQu – 博客园

Vue 和 React 是创建 JavaScript 网络应用程序最常用的两种工具。但我们该如何在两者之间做出选择呢?在本篇 Vue 与 React 的对比中,我们将尝试找出答案。我们将回顾每种工具的优缺点,提供选择使用哪种工具的技巧,并帮助你开始使用。

总览

  1. 什么是Vue?
    • Vue趣事
  2. 什么是React?
    • React趣事
  3. Vue vs React:全面比较
    • React 灵活性
    • Vue 灵活性
    • 使用 React 进行开发
    • 使用 Vue 进行开发
    • React 工具
    • Vue 工具
    • React 性能
    • Vue 性能
    • React 的文档和社区支持
    • Vue 的文档和社区支持
  4. Vue vs React:总结
  5. 受欢迎程度
    • 使用 Vue 和 React 创建的知名应用程序
  6. Vue vs React:何时使用?
  7. 10条FAQ
    1. Vue 和 React 之间的实际区别是什么?
    2. 为什么 Vue 比 React 更快?
    3. 为什么 React 比 Vue 更受欢迎?
    4. Vue 比 React 更好吗?
    5. Vue 比 React 更简单吗?
    6. 如果我们知道 React,我们能学习 Vue 吗(反之亦然)?
    7. 我们应该从 React 转向 Vue 吗?
    8. Vue 比 React 更适合我的职业生涯吗?
    9. Vue 可以与 React 一起使用吗?
    10. 那么,我们应该学习 Vue 还是 React?
  8. 总结

什么是Vue?

Vue 是一个用于构建用户界面的渐进式、可逐步采用的 JavaScript 框架。它由 Evan You 于 2014 年创建,并由一个活跃的开发者社区负责维护。

Vue 设计得非常轻量级、灵活和强大。它建立在一个基于组件的架构上,以组件为中心,可以更轻松地创建动态用户界面。它还有一个强大的双向数据绑定系统,可以让我们轻松地保持数据和用户界面同步。

Vue 的API简单但功能强大,易于上手,而且体积小(约 20KB),非常适合移动应用程序或单页应用程序(SPA)。说 Vue 是一个”渐进式”框架,意味着它不会自动提供一堆我们可能不需要的东西。相反,我们可以从最基本的功能开始构建响应式应用程序,然后随着项目的发展逐步导入更多功能。所有这些都让我们避免了模板代码和尺寸过大的困扰。

Vue趣事

  • Vue 的语法受到 Angular 和 React 的启发,旨在让已经熟悉这些框架的开发人员易于学习。
  • Vue 的名称源自法语动词 “vue”(看)。
  • Vue 的标志是一个抽象的字母 “V”。

什么是React?

React 是一个用于构建用户界面的 JavaScript 库,由 Facebook 于 2013 年创建。它最初用于构建 Facebook 网站,后来成为开发web应用程序最流行的 JavaScript 框架之一。

React 为开发人员提供了一种声明式编程风格,更易于阅读和调试。它还使用虚拟 DOM,速度比传统 DOM 更快,性能更好。

React 基于组件的方法使开发人员能够利用可快速、轻松地组合在一起的可重用组件创建强大的应用程序。这使它成为快速原型开发和应用程序的绝佳选择。

React趣事

  • 全球一些最受欢迎的网站都在使用 React,其中包括 Facebook、Netflix 和 Airbnb。
  • React 非常注重性能,其渲染速度明显快于主要竞争对手 Angular。
  • React 是最流行的前端 JavaScript 库。

Vue vs React:全面比较

现在,我们将进行一次 Vue 与 React 的压力测试,看看它们在整个开发链路中的一些关键方面有哪些优势。这些方面包括以下内容:

  • 灵活性。根据项目需求定制和调整有多容易,既可以使用其核心组件,也可以通过第三方附加组件来实现,而第三方附加组件通常无法获得同等程度的文档和支持。
  • 开发和工具。建立项目和开始编码有多容易。这一点非常重要,因为它将决定我们启动项目的速度,以及新开发人员提高工作效率的速度。
  • 性能。这一点非常重要,因为它决定了应用程序在用户设备上的运行效果。我们不应该低估这一点,因为它意味着愉快的应用体验(用户会不断回访)和令人沮丧的应用体验(用户可能会放弃)之间的差别。
  • 文档和社区支持。这将从根本上决定开发人员在遇到困难时获得帮助的速度,以及找到常见问题解决方案的难易程度。

下面就开始比较吧。

React 灵活性

React 以其灵活性而著称,因为它的核心只是一个UI库。它允许开发人员在编写代码时使用基于组件的编程方法或函数式编程方法。这意味着开发人员可以选择自己最擅长的编码风格,并以此创建功能强大的应用程序。

React 还支持各种库和工具,因此很容易与现有技术和项目集成。例如,React 可以与 Redux 结合使用以进行状态管理,也可以与 Next.js 结合使用以进行服务端渲染

  • 优点:高度可定制和可扩展;支持广泛的库和工具。
  • 缺点:可扩展性主要通过第三方插件实现。

Vue 灵活性

Vue 也提供很大的灵活性,因为我们可以在开始时只使用核心库,然后随着项目的发展逐步采用所需的内容。Vue 也支持基于组件的编程方法或函数式编程方法(包括 JSX),并支持各种库和工具。

但与 React 不同的是,这些扩展大部分都是由同一个团队开发的,因此集成和支持都非常出色。例如,Vue 甚至原生提供了服务端渲染功能。这让我们可以放心地扩展我们的项目,因为我们知道,无论将来我们需要做什么,Vue 都能满足我们的需求。

  • 优点:高度可定制和可扩展,对库和工具提供广泛的原生支持。
  • 缺点:无。

使用 React 进行开发

通过第三方插件,React 还提供路由状态管理,以管理导航流和处理应用程序数据。React 的支柱之一 JSX 可以让开发人员轻松地在 JavaScript 中编写类似 HTML 的语法,从而创建组件并将它们快速组合在一起。

  • 优点:易于设置和上手;有许多第三方插件。
  • 缺点:我们可能经常使用的许多扩展都不是官方的。

使用 Vue 进行开发

Vue 为路由状态管理提供了原生模块,从而实现了导航流和应用程序数据的管理过程。Vue 还提供模板语法,允许开发人员创建易于重用和维护的组件。

尽管如此,由于 Vue 社区不像 React 社区那么庞大,要找到好的第三方解决方案还是有点困难。(不过有一个庞大的中文 Vue 社区,我们稍后再谈)。

  • 优点:易于设置和上手;原生模块可满足大多数开发需求。
  • 缺点:没有那么多第三方插件,维护也不完善。

React 工具

React 很容易设置并开始开发,它自带用于开发和调试的 Visual Studio Code 扩展(由微软开发)。

名为 Create React App 的官方 CLI 允许我们快速设置基本的脚手架,以便立即开始编码。

React 还有一套开发人员工具(目前处于测试阶段),用于检查 React 组件、编辑props和状态以及发现性能问题。

  • 优点:用于调试和检查组件的强大工具。
  • 缺点:CLI 功能有限,性能不佳。

Vue 工具

Vue 也很容易设置和开始开发,它也有自己的 Visual Studio Code 扩展、用于快速开发 SPA 的 CLI(比 React 的 CLI 功能更全面、更强大)以及自己的插件系统

此外还有 Vue Devtools,一个用于调试 Vue 应用程序的官方浏览器 devtools 扩展。

此外,Vite 是用于现代web开发的下一代构建工具,具有极快的冷启动和增量编译时间。它是 Vue 团队的一个官方项目,因此能与 Vue 生态系统无缝集成,并提供一些高级功能,如热模块替换和tree-shaking。

  • 优点:用于调试和检查组件的强大工具,以及用于快速开发的 Vite。
  • 缺点:无。

React 性能

React 的速度相当快,因为它的设计目的只有一个而且做得很好:渲染用户界面。因此,无需太多额外工作,React 的性能就会非常出色;当然,还有进一步优化性能的具体指导原则

React 支持服务端渲染,这有助于进一步提高web应用程序的性能,尤其是在移动设备和较慢网络连接上。

  • 优点:开箱即用,性能稳定;可根据特定指南进行优化;支持服务端渲染。
  • 缺点:速度不如 Vue 快。

Vue 性能

React 在性能方面非常出色,而 Vue 甚至更快。它的虚拟 DOM 可以确保在数据发生变化时只对必要的元素进行重新渲染,因此它的性能和效率都很高。

Vue 可以比 React 做得更多,但性能仍然优于 React,这充分证明了 Vue 创建者 Evan You 的软件工程技能和强大的设计原则。👏

Vue 还原生支持服务端渲染,因此可以进一步提高性能。

  • 优点:开箱即用,性能非常快;可根据特定指南进行优化;支持服务端渲染。
  • 缺点:无。

React 的文档和社区支持

React 拥有详尽的文档和非常活跃的社区。以下是一些最活跃的社区:

Vue 的文档和社区支持

诚然,Vue 社区比 React 社区小,但仍有许多爱好者:

Vue 的文档一直备受赞誉,可能是此类项目中最好的文档。它非常清晰、简洁、易懂,而且布局精美。(这也是我成为 Vue 开发人员的一个关键因素)。

Vue vs React:总结

经过全面比较,我们可以得出这样的结论:React 和 Vue 都是开发现代web应用的绝佳选择,它们提供了大量的灵活性和强大的工具来完成工作。

不过,Vue 在性能、灵活性和文档方面更胜一筹,而 React 则在采用率、第三方工具和社区支持方面更胜一筹。

React Vue
灵活性 高(但主要通过第三方插件) 高(主要是原生扩展)
开发 容易上手,有很多第三方插件。 容易上手,有很多原生模块,但第三方插件不多。
工具 工具功能强大,但性能不佳。 工具功能强大,性能卓越。
性能 很好,可以进一步优化。 非常好,可以进一步优化。
文档和社区支持 详尽的文档和一个活跃的大型社区。 出色的文档和一个较小但活跃的社区。

受欢迎程度

就受欢迎程度而言,React 是迄今为止这两种框架中最受欢迎的,而 Vue 则落后于 Angular(本文不涉及 Angular)。

React 自 2013 年诞生以来,拥有一个庞大的开发者社区,他们不断使用并为该平台做出贡献。它还得到了 Meta(Facebook)的支持,并被用于创建一些世界上最流行的应用程序。

另一方面,Vue 比 React 更年轻,有时被描述为 Angular 和 React 的混合体。Vue 诞生于 2014 年,由于其易用性和对开发人员友好的特性,从那时起,Vue 就受到了越来越多的关注。Vue 还被用于创建流行的应用程序,这将在下一节中介绍。

使用 Vue 和 React 创建的知名应用程序

React 已被用于创建一些世界上最流行的网站和移动应用程序。其中包括 Facebook、Instagram、Airbnb、Dropbox 和 Twitter。

Vue 也被用于创建一些世界上最流行的应用程序。其中包括 9GAG、Alibaba、Xiaomi、GitLab 和 Wizz Air。

以下是一些有用的、非官方的 Vue 和 React 项目展示列表:

Vue vs React:何时使用?

好吧,我们在开始这一部分之前就知道没有”正确”答案,好吗?

  • 对于需要轻量级灵活框架的项目来说,Vue 是一个不错的选择。对于需要大量交互性的项目来说,Vue 也是不错的选择。
  • 对于需要高度可扩展性和可维护性的项目来说,React 是一个不错的选择。它也非常适合需要大量定制的项目。

以上就是在 Vue 和 React 之间做出选择的技术原因。不过,在决定使用哪种技术时,还有一些非技术性的原因(可以这么说),比如支持、社区甚至偏见。接下来,我们将仔细分析其中的一些原因。

10条FAQ

在本节中,我们将介绍有关 Vue 与 React 的十个实用注意事项。

Vue 和 React 之间的实际区别是什么?

Vue 和 React 的主要区别在于,React 是一个用于构建用户界面的 JavaScript 库,而 Vue 则是一个用于构建 Web 应用程序的 JavaScript/TypeScript 渐进式框架。”渐进”意味着其功能和范围可以随着项目的增长而轻松扩展。

除此之外,React 和 Vue 都采用基于组件的方法,允许开发人员将复杂的UI分解成较小的组件,以便在不同的项目中重用。Vue 和 React 的性能都很高,当然比 Angular 更强。

为什么 Vue 比 React 更快?

首先要说的是,React(它只是一个UI库)已经相当快了。然而,Vue 一般来说比 React 更快,因为 Vue 团队非常注重使其具有高性能和轻量级。考虑到我们可以用 Vue 做更多的事情,这已经是一项了不起的成就了。

Vue 之所以更快,还因为它采用了虚拟 DOM 实现,只在数据发生变化时才更新 DOM,而不是每次渲染页面时都更新。

为什么 React 比 Vue 更受欢迎?

至少在西方世界,React 比 Vue 更受欢迎。React 出现的时间更长,熟悉它的开发者社区也更大。

此外,React 是由 Facebook 创建的,这也是吸引许多开发人员的一个因素。相比之下,Vue 背后并没有大的技术公司(这取决于你的观点,也许是坏事,也许不是)。

尽管如此,事实上,在包括中国在内的一些亚洲国家,Vue 比其他任何框架都更受欢迎!

image.png

Vue 比 React 更好吗?

在回答这个问题时,我们不可能不引起网络开发者社区的关注,因此,我们可以妥协地说,React 和 Vue 都是很棒的工具。

React 因其简单性和庞大的社区而成为许多开发人员的首选,而 Vue 则提供了更多的内置功能,在某些用例中比 React 性能更好。

最后的答案可以归结为以下两点:

  • 如果我们了解 Vue,或者对 React 没有什么经验,那么 Vue 会更好
  • 如果我们了解 React,但没有使用过 Vue,那么 React 会更好(但 Vue 仍然优于 React)。

Vue 比 React 更简单吗?

是的。Vue 不仅比 React 更容易学习,而且因为 React 基本上是一个(非常好的)UI 库,而 Vue 实际上是一个框架,所以我们可以用它做更多的事情。

因此,Vue 的”性价比”(可以这么说)–基于你所花的学习时间和你之后能做的事情–是相当高的。

如果我们知道 React,我们能学习 Vue 吗(反之亦然)?

是的,我们可以。虽然这两种技术的开发方法不同,但基本原理是相同的。

无论学习 React 还是 Vue,我们都需要学习一种新的语言/扩展–学习 Vue 时需要学习 TypeScript(为了获得更好的开发体验),学习 React 时需要学习 JSX(出于同样的原因)。尽管存在诸多不同,但这两种框架都使用组件,并具有相似的 API,这使得它们之间的切换更加容易。

我们应该从 React 转向 Vue 吗?

如果我们的团队在使用其中任何一种产品时都已经非常出色,并且取得了丰硕成果,那么就坚持使用它。此外,如果担心第三方扩展和社区支持,那么使用 React 可能会更好。

现在,如果我们尚未启动项目,正在寻找更简单、更灵活的方法,那么 Vue 可能是更好的选择,因为 Vue 提供了更多开箱即用的功能,而且一般来说比 React 更快。

寻找具有 Vue 专业知识的开发人员比较困难,但教授 JavaScript 开发人员如何使用 Vue 则比较容易。

Vue 比 React 更适合我的职业生涯吗?

这要看情况。如果你正在寻找更多的工作机会,那就不一定了。毕竟,React 仍然更受欢迎,甚至可以说是标准(至少在西方)。而在包括中国在内的亚洲许多地方,情况恰恰相反。

如果你想专门从事 JavaScript 开发,那么这两个选项都是不错的选择,因为 Vue 的市场份额仍在不断扩大,尽管没有以前那么快。

Vue 可以与 React 一起使用吗?

不,不要这样做。Vue 和 React 是完全不同的工具,彼此不兼容。

那么,我们应该学习 Vue 还是 React?

还是要看情况。比方说,我们需要这样一个框架:

  • 开箱即能做很多事情
  • 可以通过官方插件进一步扩展,这些插件将遵循与核心软件包相同的实践和指导原则
  • 如果需要,可以与 TypeScript 一起使用
  • 易于学习
  • 在亚洲非常流行
  • 没有任何大公司支持

如果是这样的话,我们就应该学习 Vue

另一方面,我们可能需要一个库:

  • 使我们能够创建用户界面
  • 还可以使用官方和非官方插件进行扩展
  • 当使用 React Native 时,它甚至可以成为移动项目的基础
  • 在西方世界最为流行
  • 由 Meta 提供支持

如果是这样,我们就应该学习 React

总结

Vue 和 React 都是创建 web 应用程序的强大工具。React 因其庞大的社区和 Meta 的支持而更受欢迎,而 Vue 则因其简洁性和性能在过去几年中获得了越来越多的关注。

得到科技巨头的支持并非无关紧要!这基本上意味着资金和开发人员。对于 Vue 来说,独立是一个长期的挑战,因为它需要获得企业的采用和开源社区的支持,才能保持良好的状态。

总而言之,如果我们正在寻找一个可以帮助我们快速、轻松地创建用户界面的库,React 是一个不错的选择–这些界面以后可以成为使用 React Native 构建移动应用程序的基础。如果我们需要更强大、功能更丰富的东西,Vue 可能是更好的选择,因为它提供了更多开箱即用的功能,而且一般比 React 更快。

以上就是本文的全部内容,如果对你有所帮助,欢迎点赞、收藏、转发~

Vue3中的几个坑,你都见过吗? - 葡萄城技术团队 - 博客园

mikel阅读(315)

来源: Vue3中的几个坑,你都见过吗? – 葡萄城技术团队 – 博客园

Vue3 目前已经趋于稳定,不少代码库都已经开始使用它,很多项目未来也必然要迁移至Vue3。本文记录我在使用Vue3时遇到的一些问题,希望能为其他开发者提供帮助。

1. 使用reactive封装基础数据类型

传统开发模式中,数据声明很简单。但是在Vue中有多个响应式变量声明方式,整体的使用规则如下:

使用reactive来封装Object,Array,Map,Set数据类型;

使用ref封装String,Number,Boolean类型。

如果使用reactive来封装基础数据类型,会产生警告,同时封装的值不会成为响应式对象。

<script setup>
import { reactive } from "vue";
const count = reactive(0);
</script>

但是,可以使用ref来封装Object、Array等数据类型,内部会调用reactive。

2. 解构reactive对象

下面代码中,count封装成了reactive对象,点击按钮时,count会自增。

<template>
  Counter: {{ state.count }}
  <button @click="add">Increase</button>
 </template>


 <script>
 import { reactive } from "vue";
 export default {
  setup() {
   const state = reactive({ count: 0 });


   function add() {
    state.count++;
   }

   return {
    state,
    add,
   };
  },
 };
 </script>

如果需要使用ES6结构赋值对state进行结构,需要使用如下的代码:

<template>
  <div>Counter: {{ count }}</div>
  <button @click="add">Increase</button>
</template>

<script>
import { reactive } from "vue";
export default {
  setup() {
    const state = reactive({ count: 0 });

    function add() {
      state.count++;
    }

    return {
      ...state,
      add,
    };
  },
};
</script>

结构复制完成之后,点击按钮,效果如下:

代码看起来比较相似,而且根据以前的表现,逻辑是可以正常执行的。但事实上,Vue的响应式追踪通过属性获取,这意味着我们不能去解构响应式对象,会导致引用连接丢失。这是响应式代理的限制之一。

3. 使用.value造成的困惑

Ref接受一个值并返回一个响应式对象,该值只有在内部对象.value属性下可用。

const count = ref(0)

 console.log(count) // { value: 0 }
 console.log(count.value) // 0

 count.value++
 console.log(count.value) // 1

但是ref如果应用在template中,不需要对ref进行解包,也就是不需要使用.vue。

<script setup>
import { ref } from 'vue'

const count = ref(0)

function increment() {
  count.value++
}
</script>

<template>
  <button @click="increment">
    {{ count }} // 不需要调用.value
  </button>
</template>

需要注意的是,解包只作用于一级属性,下边的代码会返回[object Object]

<script setup>
import { ref } from 'vue'
const object = { foo: ref(1) }
</script>
<template>
  {{ object.foo + 1 }}  // [object Object]
</template>

正确使用.value需要时间,初学者偶尔会忘记它。在使用时,要注意只在合适的场景下使用它。

4. Emitted事件

从Vue发布以来,子组件可以通过emits与父组件通信,只需要添加一个自定义的监听器来监听事件即可。

this.$emit('my-event')
<my-component @my-event="doSomething" />

Vue3中,需要使用编译器宏defineEmits来声明emits。

const emit = defineEmits(['my-event'])
 emit('my-event')
 </script>

在setup语法糖下,defineEmits和defineProps会被自动引入。其它情况下,需要主动引入。

<script setup>
const props = defineProps({
  foo: String
})

const emit = defineEmits(['change', 'delete'])
// setup code
</script>

最后,由于Vue3中,事件必须声明,因此再需要使用.native修饰符,该修饰符已被移除。

5.声明组件选项

setup不支持如下组件选项声明:

  • name
  • inheritAttrs
  • customOptions

如果需要继续使用这些属性,可以声明多个script脚本,如下所示:

<script>
  export default {
    name: 'CustomName',
    inheritAttrs: false,
    customOptions: {}
  }
</script>

<script setup>
  // script setup logic
</script>

6.使用Reactivity Transform

Reactivity Transform是Vue3中一个预览属性,有一定的争议性,默认是禁用的。它主要用来简化组件的声明方式。这个想法是利用编译时转换来自动解包ref,从而避免使用.value。从Vue3.3中已经删除该功能,作为一个扩展包提供。由于它不是Vue的核心部分,且目前风险还是比较多,建议先不要在此方面投入太多事件。感兴趣可以参考文章:Reactivity Transform

7. 定义异步组件

异步组件以前是通过将它们包含在方法中来声明的。

const asyncModal = () => import('./Modal.vue')

Vue3中需要使用defineAsyncComponent来声明异步组件。

import { defineAsyncComponent } from 'vue'
 const asyncModal = defineAsyncComponent(() 
=> import('./Modal.vue'))

8. template中使用不必要的包装元素

<!-- Layout.vue -->
<template>
  <div>
    <header>...</header>
    <main>...</main>
    <footer>...</footer>
  </div>
</template>

Vue3中支持多个根元素,不再需要使用外层div元素包裹。

<template>
  <header>...</header>
  <main v-bind="$attrs">...</main>
  <footer>...</footer>
</template>

9. 生命周期函数

所有组件生命周期函数都通过添加on前缀或完全更名实现,下图详细列出了具体的变化:

10. 产品文档

官方对文档已经做了更新,补充更新了API,并包含很多有价值的注释、指南和最佳实践。即使你现在使用的是Vue2,通过阅读新的文档也会学到一些新知识。

总结

每个框架都有学习曲线,Vue3相对Vue2更加陡峭,在框架切换之间也会有一定的学习成本。但Vue3组合式API相对Vue2选项式API确实更加简洁易用。如果您在使用过程中有什么疑问,也欢迎留言交流。

本文翻译自文章:

https://fadamakis.com/10-mistakes-to-avoid-when-starting-with-vue-3-1d1ced8552ae

扩展链接:

Spring Boot框架下实现Excel服务端导入导出

项目实战:在线报价采购系统(React +SpreadJS+Echarts)

React + Springboot + Quartz,从0实现Excel报表自动化