当前位置:   article > 正文

收集.NET6中一些常用组件的配置_app.environment.isdevelopment()

app.environment.isdevelopment()

介绍

.NET 6的CoreApp框架,用来学习.NET6的一些变动和新特性,使用EFCore,等一系列组件的运用;

软件架构

分为模型层,服务层,接口层来做测试使用

0.如何使用IConfiguration、Environment

直接在builder后的主机中使用。

  1. builder.Configuration
  2. builder.Environment

1.如何使用Swagger

.NET 6 自带模板已经默认添加Swagger,直接使用即可。

  1. builder.Services.AddSwaggerGen();
  2. if (app.Environment.IsDevelopment())
  3. {
  4. app.UseSwagger();
  5. app.UseSwaggerUI();
  6. }

2. 如何添加EFCore到.NET 6中

按照EFCore常规使用方法,申明表的Entity及Dbcontext后,在program.cs文件中添加

  1. builder.Services.AddDbContext<Service.DataContext>(opt => {
  2. opt.UseSqlServer(builder.Configuration.GetConnectionString("Default"));
  3. });

即可在其他地方注入使用 DataContext

使用Sqlite数据库,需要引用 Microsoft.EntityFrameworkCore.Sqlite,
并在添加服务时,改为

opt.UseSqlite(builder.Configuration.GetConnectionString("Default"));

包管理控制台数据库结构生成方法:
使用 add-migration 创建迁移
使用 update-database 更新数据结构

3.如何注入一个服务

builder.Services.AddScoped<UserIdentyService>();

4.如何定义全局的using引用

在根目录下新建一个 cs文件,比如Globalusing.cs,在里面添加你的全局引用,和常规引用不同的是,在using前面添加 global

  1. global using Service;
  2. global using Entity;
  3. global using Entity.Dto;

5.如何使用Autofac

添加 Nuget 引用

Autofac.Extensions.DependencyInjection

program.cs文件添加autofac的使用和注入配置

  1. builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
  2. builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
  3. {
  4. Assembly assembly = Assembly.Load("Service.dll");
  5. builder.RegisterAssemblyTypes(assembly)
  6. //.AsImplementedInterfaces()// 无接口的注入方式
  7. .InstancePerDependency();
  8. });

此时即可构造函数注入使用。

6.如何使用Log4Net

添加引用

Microsoft.Extensions.Logging.Log4Net.AspNetCore

新建配置文件 log4net.config;
添加service配置

  1. //注入Log4Net
  2. builder.Services.AddLogging(cfg =>
  3. {
  4. //默认的配置文件路径是在根目录,且文件名为log4net.config
  5. //cfg.AddLog4Net();
  6. //如果文件路径或名称有变化,需要重新设置其路径或名称
  7. //比如在项目根目录下创建一个名为config的文件夹,将log4net.config文件移入其中,并改名为log4net.config
  8. //则需要使用下面的代码来进行配置
  9. cfg.AddLog4Net(new Log4NetProviderOptions()
  10. {
  11. Log4NetConfigFileName = "config/log4net.config",
  12. Watch = true
  13. });
  14. });

即可在需要的地方定义使用

_logger = LogManager.GetLogger(typeof(UserController));

7.如何使用全局异常过滤器

首先新建 GlobalExceptionFilter 全局异常过滤器,继承于 ExceptionFilter ,用于接收处理抛出的异常

  1. public class GlobalExceptionFilter : IExceptionFilter
  2. {
  3. readonly IWebHostEnvironment hostEnvironment;
  4. readonly ILog logger;
  5. public GlobalExceptionFilter(IWebHostEnvironment _hostEnvironment)
  6. {
  7. this.hostEnvironment = _hostEnvironment;
  8. this.logger = LogManager.GetLogger(typeof(GlobalExceptionFilter));
  9. }
  10. public void OnException(ExceptionContext context)
  11. {
  12. if (!context.ExceptionHandled)//如果异常没有处理
  13. {
  14. var result = new ApiResult
  15. {
  16. Code = 500,
  17. IsSuccess = false,
  18. Message = "服务器发生未处理的异常"
  19. };
  20. if (hostEnvironment.IsDevelopment())
  21. {
  22. result.Message += "," + context.Exception.Message;
  23. result.Data = context.Exception.StackTrace;
  24. }
  25. logger.Error(result);
  26. context.Result = new JsonResult(result);
  27. context.ExceptionHandled = true;//异常已处理
  28. }
  29. }
  30. }

然后在Service中添加全局异常过滤器

  1. builder.Services.AddControllers(option =>
  2. {
  3. option.Filters.Add<GlobalExceptionFilter>();
  4. }
  5. );

添加控制器方法完成测试

  1. [HttpGet("exception")]
  2. public ApiResult ExceptionAction()
  3. {
  4. throw new NotImplementedException();
  5. }

8.如何使用redis做缓存

使用 StackExchange.Redis 作为缓存组件(其他组件类似的使用方式)。nuget 安装 StackExchange.Redis.Extensions.Core
首先,先建立一个类 RedisClient ,用于管理redis的连接和操作,再建立一个 RedisClientFactory 类,用于创建 redis的连接;

  1. public class RedisClient{...}
  2. public class RedisClientFactory{...}

appsettings.json 中添加redis的配置

  1. "RedisConfig": {
  2. "Redis_Default": {
  3. "Connection": "127.0.0.1:6379",
  4. "InstanceName": "Redis1:"
  5. },
  6. "Redis_6": {
  7. "Connection": "127.0.0.1:6379",
  8. "DefaultDatabase": 6,
  9. "InstanceName": "Redis2:"
  10. }
  11. }

service中添加 redis客户端的引用

  1. //添加redis的使用
  2. builder.Services.AddSingleton<RedisClient>(_=> RedisClientFactory.GetInstance(builder.Configuration));

一顿操作后,就可以在你想要使用redis的地方引用了

  1. RedisClient redisClient
  2. ...
  3. this.redisDb = redisClient.GetDatabase("Redis_Default");
  4. redisDb.StringSet("clientId", "clientId", TimeSpan.FromSeconds(10));

要使用redis做分布式缓存,先引用 Microsoft.Extensions.Caching.StackExchangeRedis

  1. //将Redis分布式缓存服务添加到服务中
  2. builder.Services.AddStackExchangeRedisCache(options =>
  3. {
  4. //用于连接Redis的配置 Configuration.GetConnectionString("RedisConnectionString")读取配置信息的串
  5. options.Configuration = "Redis_6";// Configuration.GetConnectionString("RedisConnectionString");
  6. //Redis实例名RedisDistributedCache
  7. options.InstanceName = "RedisDistributedCache";
  8. });

引用自 "分布式 Redis 缓存"

9. 如何添加使用定时任务组件

此处使用 Hangfire 定时任务组件,轻便,可持久化,还有面板。
引用 Hangfire 后,即可新增定时任务。

  1. //启用Hangfire服务.
  2. builder.Services.AddHangfire(x => x.UseStorage(new MemoryStorage()));
  3. builder.Services.AddHangfireServer();
  4. ...
  5. //启用Hangfire面板
  6. app.UseHangfireDashboard();
  7. //开启一个定时任务
  8. RecurringJob.AddOrUpdate("test",() => Console.WriteLine("Recurring!"), Cron.Minutely());

访问 https://localhost:7219/hangfire 即可看到任务面板

10. 如何使用业务锁锁住下单或者支付操作

首先,做这个事需要能先构建出一个锁出来,这个锁有个锁的标识key,可以根据这个key判定key对应的锁是否存在,
这样的话,在某个用户支付或者下单减库存啥的时候,就可以按照这个key先上锁,后面有用户走其他渠道进行同样的操作的时候,就可以根据是否上锁了,来判断操作能否继续。

比如一个支付订单的业务,可以在手机上操作,也可以在电脑上操作,这个时候就可以给支付接口上锁,只要一个支付过程存在着,并且没有超时,那就不能在其他渠道进行操作。
我们上面已经使用了redis,下面就用redis构建个锁来模拟这个操作,具体看代码:

  1. /// <summary>
  2. /// 测试业务锁
  3. /// </summary>
  4. /// <returns></returns>
  5. [HttpGet("lockhandle")]
  6. public async Task<ApiResult> LockHandle(int userId)
  7. {
  8. var key = "user";
  9. var token = $"ID:{userId}";
  10. try
  11. {
  12. if (redisDb.LockTake(key, token, TimeSpan.FromSeconds(50)))
  13. {
  14. await Task.Delay(30 * 1000);
  15. return await Task.FromResult(ApiResult.Success($"ID:{userId} 获取到锁了,操作正常,connectId:{Request.HttpContext.Connection.Id}"));
  16. }
  17. else
  18. {
  19. return await Task.FromResult(ApiResult.Fail($"有正在操作的锁,connectId:{Request.HttpContext.Connection.Id}"));
  20. }
  21. }
  22. catch (Exception)
  23. {
  24. throw;
  25. }
  26. finally
  27. {
  28. redisDb.LockRelease(key, token);
  29. }
  30. }

11. 如何配置跨域

此处主要记录全局跨域,不包括指定api跨域。先增加一个配置 "Cors": "http:127.0.0.1:5001",配置可以跨域的url,也可以使用默认跨域配置。
host配置以下服务,按需使用:

  1. builder.Services.AddCors(delegate (CorsOptions options)
  2. {
  3. options.AddPolicy("CorsPolicy", delegate (CorsPolicyBuilder corsBuilder)
  4. {
  5. //指定url跨域
  6. corsBuilder.WithOrigins(builder.Configuration.GetValue<string>("Cors").Split(','));
  7. //默认跨域
  8. corsBuilder.SetIsOriginAllowed((string _) => true).AllowAnyMethod().AllowAnyHeader()
  9. .AllowCredentials();
  10. });
  11. });

12. 如何使用NewtonsoftJson

.NET6 默认的系列化库是内置的 System.Text.Json,使用中如果有诸多不熟悉的地方,那肯定是想换回 NewtonsoftJson,需要nuget 引用 Microsoft.AspNetCore.Mvc.NewtonsoftJson 来配置使用,
常用配置包括日期格式、大小写规则、循环引用配置。。。等,下面是一个配置

  1. builder.Services.AddControllers(option =>
  2. {
  3. option.Filters.Add<GlobalExceptionFilter>();
  4. }
  5. ).AddNewtonsoftJson(options =>
  6. {
  7. options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); //序列化时key为驼峰样式
  8. options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
  9. options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
  10. options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;//忽略循环引用
  11. });

13. 如何使用SignalR

首先添加一个 ChatHub 作为 交互中心处理器

  1. public class ChatHub : Hub
  2. {
  3. public async Task SendMessage(string user, string message)
  4. {
  5. await Clients.All.SendAsync("ReceiveMessage", user, message);
  6. }
  7. }

在主机中使用服务

  1. builder.Services.AddSignalR();
  2. ...
  3. app.UseEndpoints(endpoints =>
  4. {
  5. endpoints.MapHub<ChatHub>("/chatHub");
  6. });

14. 如何使用Dapper

Dapper是大家常用的一个数据库连接扩展组件,下面介绍下,如何使用常规扩展,来在.net Core中使用Dapper。
首先,建立一个DbComponent ,来获取由 .netCore 提供的 Configuration 配置文件,并用 DbProviderFactories 工厂,创建数据库连接,此类只管创建连接,又其他使用类进行销毁。

  1. /// <summary>
  2. /// 创建连接处理
  3. /// </summary>
  4. public class DbComponent
  5. {
  6. /// 数据库连接配置
  7. private static ConnectionStringSettings connectionSetting;
  8. public static void InitDapper(ConnectionStringSettings connectionStringSettings)
  9. {
  10. connectionSetting = connectionStringSettings;
  11. }
  12. //通过工厂模式创建Connection连接 此连接已打开
  13. public static IDbConnection GetConnection()
  14. {
  15. get {
  16. var cnnection = DbProviderFactories.GetFactory(connectionSetting.ProviderName).CreateConnection();
  17. if (cnnection == null)
  18. throw new Exception("数据库链接获取失败!");
  19. cnnection.ConnectionString = connectionSetting.ConnectionString;
  20. cnnection.Open();
  21. return cnnection;
  22. }
  23. }
  24. }

使用前,需要在program中初始化一下组件

  1. /// <summary>
  2. /// 初始化Dapper组件
  3. /// </summary>
  4. DbProviderFactories.RegisterFactory("Microsoft.Data.Sqlite", Microsoft.Data.Sqlite.SqliteFactory.Instance);
  5. DbComponent.InitDapper(new System.Configuration.ConnectionStringSettings
  6. {
  7. ConnectionString = builder.Configuration.GetConnectionString("Default"),
  8. ProviderName = "Microsoft.Data.Sqlite"
  9. });

程序启动后,就可以在需要的地方使用

  1. public class UserIdentyService
  2. {
  3. public ApiResult DapperList()
  4. {
  5. using (var connect = DbComponent.Connection)
  6. {
  7. var users= connect.Query<User>("SELECT * FROM Users").ToList();
  8. return ApiResult.Success(users);
  9. }
  10. }
  11. }

15. 如何添加自定义配置文件

有时候我们不想把配置全部放在 appsettings.json ,我们想自己建立一个文件夹来存储其他配置文件,比如config/...json之类的,咋整呢,
我们新建个文件夹 config,下面建立一个配置文件app.json,里面存几个配置以便验证。

使用前添加如下代码即可

  1. builder.Configuration.AddJsonFile("config/app.json");
  2. Console.WriteLine(builder.Configuration.GetValue<string>("weixin"));

16. 如何简单上传文件

上传文件是每个api框架都会实现的功能,我们先实现一个简单的文件上传。
首先做个配置文件,存储上传的文件存储位置、大小及格式限制等的配置

  1. public class UploadConfig
  2. {
  3. /// <summary>
  4. /// 最大值
  5. /// </summary>
  6. public int MaxSize { get; set; } = 1024 * 1024 * 1024;
  7. /// <summary>
  8. /// 存储路径
  9. /// </summary>
  10. public string UploadDir { get; set; } = @"D://Upload";
  11. /// <summary>
  12. /// 站点名称
  13. /// </summary>
  14. public string WebSite { get; set; }
  15. }

添加测试action,完成文件上传,并返回文件访问路径

  1. /// <summary>
  2. /// 上传文件测试
  3. /// </summary>
  4. /// <param name="files"></param>
  5. /// <returns></returns>
  6. [HttpPost("upload")]
  7. public async Task<ApiResult> Upload([FromForm(Name ="file")] List<IFormFile> files)
  8. {
  9. var config = configuration.GetSection("UploadConfig").Get<UploadConfig>();
  10. if (files.Count == 0)
  11. {
  12. return ApiResult.Fail("没有需要上传的文件");
  13. }
  14. var dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, config.UploadDir);
  15. if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
  16. //验证大小或者格式之类
  17. foreach (var file in files)
  18. {
  19. var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName;
  20. var fileSize = file.Length;
  21. if (fileSize > config.MaxSize)
  22. {
  23. return ApiResult.Fail($"{fileName}文件过大");
  24. }
  25. }
  26. //存储文件
  27. var result = new List<string>();
  28. foreach (var file in files)
  29. {
  30. var fileName = file.FileName;
  31. using (var stream = System.IO.File.Create(Path.Combine(dir, fileName)))
  32. {
  33. await file.CopyToAsync(stream);
  34. }
  35. result.Add(string.Join('/',config.WebSite,"upload/view" fileName));
  36. }
  37. return ApiResult.Success(result);
  38. }

上述文件访问路径需要配置静态目录来进行访问

  1. //启动www静态目录
  2. app.UseStaticFiles();
  3. //启动上传文件目录
  4. app.UseStaticFiles(new StaticFileOptions {
  5. FileProvider = new PhysicalFileProvider(builder.Configuration.GetValue<string>("UploadConfig:UploadDir")),
  6. RequestPath = "/upload/view"
  7. });

至此,文件上传及访问已添加完成

17. 如何添加验证码

验证码是常用的一个api功能,需要完成2步,先生成验证码字符串值及存储,后需要输出验证码图片到前端,即验证码功能需要2个api,1.获取验证码,2.验证验证码。
先实现一个类,用于获取随机值及生成图片
为了方便部署到unix系统,使用 ImageSharp 相关类库

  1. public class CaptchaHelper
  2. {
  3. private const string Letters = "1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,J,K,L,M,N,P,Q,R,S,T,U,V,W,X,Y,Z";
  4. /// <summary>
  5. /// 生成验证码随机值
  6. /// </summary>
  7. /// <param name="codeLength"></param>
  8. /// <returns></returns>
  9. public Task<string> GenerateRandomCaptchaAsync(int codeLength = 4)
  10. {
  11. var array = Letters.Split(new[] { ',' });
  12. var random = new Random();
  13. var temp = -1;
  14. var captcheCode = string.Empty;
  15. for (int i = 0; i < codeLength; i++)
  16. {
  17. if (temp != -1)
  18. random = new Random(i * temp * unchecked((int)DateTime.Now.Ticks));
  19. var index = random.Next(array.Length);
  20. if (temp != -1 && temp == index)
  21. return GenerateRandomCaptchaAsync(codeLength);
  22. temp = index;
  23. captcheCode += array[index];
  24. }
  25. return Task.FromResult(captcheCode);
  26. }
  27. /// <summary>
  28. /// 生成验证码及图片
  29. /// </summary>
  30. /// <param name="captchaCode"></param>
  31. /// <param name="width"></param>
  32. /// <param name="height"></param>
  33. /// <returns></returns>
  34. public Task<(string code, MemoryStream ms)> GenerateCaptchaImageAsync(string captchaCode, int width = 0, int height = 30)
  35. {
  36. //验证码颜色集合
  37. Color[] colors = { Color.Black, Color.Red, Color.DarkBlue, Color.Green, Color.Orange, Color.Brown, Color.DarkCyan, Color.Purple };
  38. //验证码字体集合
  39. string[] fonts = { "Verdana", "Microsoft Sans Serif", "Comic Sans MS", "Arial" };
  40. var r = new Random();
  41. if(width == 0) { width = captchaCode.Length * 25; }
  42. //定义图像的大小,生成图像的实例
  43. using var image = new Image<Rgba32>(width, height);
  44. // 字体
  45. var font = SystemFonts.CreateFont(SystemFonts.Families.First().Name, 25, FontStyle.Bold);
  46. image.Mutate(ctx =>
  47. {
  48. // 白底背景
  49. ctx.Fill(Color.White);
  50. // 画验证码
  51. for (int i = 0; i < captchaCode.Length; i++)
  52. {
  53. ctx.DrawText(captchaCode[i].ToString()
  54. , font
  55. , colors[r.Next(colors.Length)]
  56. , new PointF(20 * i + 10, r.Next(2, 12)));
  57. }
  58. // 画干扰线
  59. for (int i = 0; i < 10; i++)
  60. {
  61. var pen = new Pen(colors[r.Next(colors.Length)], 1);
  62. var p1 = new PointF(r.Next(width), r.Next(height));
  63. var p2 = new PointF(r.Next(width), r.Next(height));
  64. ctx.DrawLines(pen, p1, p2);
  65. }
  66. // 画噪点
  67. for (int i = 0; i < 80; i++)
  68. {
  69. var pen = new Pen(colors[r.Next(colors.Length)], 1);
  70. var p1 = new PointF(r.Next(width), r.Next(height));
  71. var p2 = new PointF(p1.X + 1f, p1.Y + 1f);
  72. ctx.DrawLines(pen, p1, p2);
  73. }
  74. });
  75. using var ms = new MemoryStream();
  76. // gif 格式
  77. image.SaveAsGif(ms);
  78. return Task.FromResult((captchaCode, ms));
  79. }
  80. }

测试代码

  1. /// <summary>
  2. /// 测试验证码
  3. /// </summary>
  4. /// <returns></returns>
  5. [HttpGet("captcha")]
  6. public async Task<IActionResult> GetCaptcha()
  7. {
  8. var captchaHelper = new CaptchaHelper();
  9. var captcha = await captchaHelper.GenerateCaptchaImageAsync();
  10. this.HttpContext.Session.SetString("captcha", captcha.code);
  11. return File(captcha.ms.ToArray(), "image/gif");
  12. }

18. 如何发布到windows

发布到windows比较简单,一步步选择发布,只要注意是按照框架依赖还是独立发布就好了,框架依赖的意思是, 你的服务器已经安装好了个.NetCore的运行时,你就可以直接框架依赖来发布就好了,如果你的服务器没有安装运行时, 或者是需要兼容以前的老的运行时不能更新啥的,你就使用独立发布。

19. 如何发布到linux

发布到linux其实也比较简单,首先,需要有待发布的程序文件,和windows一样,做好框架依赖和独立发布的安装包,目标运行时记得选择linux.64 或者linux-arm,编译发布,获得程序文件。
服务器运行时,和windows一样,框架依赖需要安装下.NET Core的运行时,注意匹配版本号,运行时安装好之后,把程序通过xshell 或者 scp压缩包上传到linux服务器你想要的目录下面,完成程序文件部署,在运行时版本没问题的情况下,dotnet xxx.dll 即可启动站点,还有一些其他的配置可能需要注意,比如后台运行,比如开放防火墙端口之类的,照着整就可以了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/582655
推荐阅读
相关标签
  

闽ICP备14008679号