CSharp-Single-EFCore/Bunny.WebApi/ReadMe/ReadMe.md

376 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 基础配置
基础配置可以看下这个,里面整合几乎这个模板所有的配置。
![image-20240902231627836](./images/image-20240902231627836.png)
### 数据库配置
![image-20240902231720819](./images/image-20240902231720819.png)
### 中间件配置
![image-20240902231705133](./images/image-20240902231705133.png)
## 后台服务设置
### Quartz写法
- 持久化存储生成数据SQL语句参考
- https://github.com/quartznet/quartznet/tree/main/database/tables
### 原生写法
- 设置每秒执行多少,设置后台服务,原生写法
```csharp
using Microsoft.Extensions.Hosting;
namespace Bunny.Service.BackgroundModule;
/// <summary>
/// 定时任务
/// </summary>
public class TemplateBackgroundModule : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
Console.WriteLine("TemplateService started");
await Task.Delay(1000, stoppingToken);
await Task.Run(() => { Console.WriteLine("执行了。。。"); }, stoppingToken);
await Task.Delay(1000, stoppingToken);
Console.WriteLine("--------------------------------");
}
}
}
```
## 验证码生成
- 验证码生成相关配置
```csharp
var service = builder.Services;
// 使用图形验证码
service.AddDistributedMemoryCache();
// 验证码相关配置内容
service.AddCaptcha(options =>
{
options.CaptchaType = CaptchaType.DEFAULT; // 验证码类型
options.CodeLength = 4; // 验证码长度
options.ExpirySeconds = 60; // 过期时间(单位/秒)
options.IgnoreCase = true; // 比较忽略大小写
options.ImageOption.Animation = true; // 是否启用动画
options.ImageOption.Width = 130; // 验证码宽度
options.ImageOption.Height = 48; // 验证码高度
options.ImageOption.BackgroundColor = SKColors.White;
options.ImageOption.BubbleCount = 6; // 气泡数量
options.ImageOption.BubbleMinRadius = 2; // 气泡最小半径
options.ImageOption.BubbleMaxRadius = 6; // 气泡最大半径
options.ImageOption.BubbleThickness = 2; // 气泡边沿厚度
options.ImageOption.InterferenceLineCount = 2; // 干扰线数量
options.ImageOption.FontSize = 36; // 字体大小
options.ImageOption.FontFamily = DefaultFontFamilys.Instance.Kaiti; // 字体中文使用kaiti其他字符可根据喜好设置
});
}
```
## AutoFac配置
### 自动注入按照名称注入
**AutoFac配置详情**
```csharp
using Autofac;
using Bunny.Common.Attribute;
using Microsoft.AspNetCore.Mvc;
namespace Bunny.WebApi.Config;
public static class AddAutofacConfig
{
/// <summary>
/// AutoFac自动注入约定名称
/// 接口以Service结尾注入到IOC中
/// </summary>
/// <param name="builder"></param>
public static void BuildContainer(ContainerBuilder builder)
{
// 扫描所以前缀Bunny的命名空间
var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(t => t.FullName!.StartsWith("Bunny"));
// 遍历注入服务
foreach (var assembly in assemblies)
{
var types = assembly.GetTypes();
// 注入Service
var serviceTypes = types.Where(t => t.GetCustomAttributes(typeof(ServiceAttribute), false).Length != 0);
foreach (var serviceType in serviceTypes)
{
var interfaces = serviceType.GetInterfaces();
builder.RegisterType(serviceType).As(interfaces).InstancePerDependency();
}
// 注入数据库相关
var mapperTypes = types.Where(t => t.GetCustomAttributes(typeof(MapperAttribute), false).Length != 0);
foreach (var mapperType in mapperTypes)
{
var interfaces = mapperType.GetInterfaces();
builder.RegisterType(mapperType).As(interfaces).InstancePerDependency();
}
// 注入后台服务
var componentTypes =
types.Where(t => t.GetCustomAttributes(typeof(ComponentAttribute), false).Length != 0);
foreach (var componentType in componentTypes)
builder.RegisterType(componentType).AsSelf().As<IHostedService>().SingleInstance();
}
// 如果不在在 Controller 层写构造函数可以打开这个,自动完成注入
var controllerBaseType = typeof(ControllerBase);
builder.RegisterAssemblyTypes(typeof(Program).Assembly)
.Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
.PropertiesAutowired(); //支持属性注入
}
}
```
**主程序入口配置**
```csharp
// 让控制器实例由容器创建
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
// 如果将Service放在 WebApi同级目录下可以完成Controller自动注入不需要写构造函数
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
builder.Services.AddControllers().AddJsonOptions(options =>
options.JsonSerializerOptions.Converters.Add(new JsonDateTimeConverter()));
```
## 日志配置
### 数据库生成
在这里面有数据库生成内容
![image-20240902230702816](./images/image-20240902230702816.png)
如果需要写入数据库放开这个注释
![image-20240902230759404](./images/image-20240902230759404.png)
> **配置参考**
>
> ```xml
> <?xml version="1.0" encoding="utf-8"?>
> <log4net>
> <!-- Define some output appenders -->
> <appender name="RollingAppender" type="log4net.Appender.RollingFileAppender">
> <file value="log4net\log4net.log"/>
> <!--追加日志内容-->
> <appendToFile value="true"/>
>
> <!--防止多线程时不能写Log,官方说线程非安全-->
> <lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
>
> <!--可以为:Once|Size|Date|Composite-->
> <!--Composite为Size和Date的组合-->
> <rollingStyle value="Composite"/>
>
> <!--当备份文件时,为文件名加的后缀-->
> <datePattern value="yyyyMMdd.TXT"/>
>
> <!--日志最大个数,都是最新的-->
> <!--rollingStyle节点为Size时,只能有value个日志-->
> <!--rollingStyle节点为Composite时,每天有value个日志-->
> <maxSizeRollBackups value="20"/>
>
> <!--可用的单位:KB|MB|GB-->
> <maximumFileSize value="3MB"/>
>
> <!--置为true,当前最新日志文件名永远为file节中的名字-->
> <staticLogFileName value="true"/>
>
> <!--输出级别在INFO和ERROR之间的日志-->
> <filter type="log4net.Filter.LevelRangeFilter">
> <param name="LevelMin" value="ALL"/>
> <param name="LevelMax" value="FATAL"/>
> </filter>
> <layout type="log4net.Layout.PatternLayout">
> <conversionPattern value="%date [%thread] %-5level %logger - %Message%newline"/>
> </layout>
> </appender>
>
> <!--SqlServer形式-->
> <!--log4net日志配置http://logging.apache.org/log4net/release/config-examples.html -->
> <appender name="AdoNetAppenderSqlServer" type="log4net.Appender.AdoNetAppender">
> <!--日志缓存写入条数 设置为0时只要有一条就立刻写到数据库-->
> <bufferSize value="0"/>
> <!--日志数据库连接串-->
> <connectionType
> value="System.Data.SqlClient.SqlConnection,System.Data.SqlClient, Version=4.6.1.3, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
> <connectionString
> value="Data Source=192.168.3.98;Initial Catalog=LogManager;Persist Security Info=True;User ID=sa;Password=abc1234."/>
> <commandText
> value="INSERT INTO Log4Net ([Date],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @thread, @log_level, @logger, @Message, @exception)"/>
> <parameter>
> <parameterName value="@log_date"/>
> <dbType value="DateTime"/>
> <layout type="log4net.Layout.RawTimeStampLayout"/>
> </parameter>
> <parameter>
> <parameterName value="@thread"/>
> <dbType value="String"/>
> <size value="255"/>
> <layout type="log4net.Layout.PatternLayout">
> <conversionPattern value="%thread"/>
> </layout>
> </parameter>
> <parameter>
> <parameterName value="@log_level"/>
> <dbType value="String"/>
> <size value="50"/>
> <layout type="log4net.Layout.PatternLayout">
> <conversionPattern value="%level"/>
> </layout>
> </parameter>
> <parameter>
> <parameterName value="@logger"/>
> <dbType value="String"/>
> <size value="255"/>
> <layout type="log4net.Layout.PatternLayout">
> <conversionPattern value="%logger"/>
> </layout>
> </parameter>
> <parameter>
> <parameterName value="@Message"/>
> <dbType value="String"/>
> <size value="4000"/>
> <layout type="log4net.Layout.PatternLayout">
> <conversionPattern value="%Message"/>
> </layout>
> </parameter>
> <parameter>
> <parameterName value="@exception"/>
> <dbType value="String"/>
> <size value="2000"/>
> <layout type="log4net.Layout.ExceptionLayout"/>
> </parameter>
> </appender>
>
> <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
> <layout type="log4net.Layout.PatternLayout">
> <!-- 自定义日志格式 -->
> <conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/>
> </layout>
> </appender>
>
> <root>
>
> <!--控制级别,由低到高: ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF-->
> <!--OFF:0-->
> <!--FATAL:FATAL-->
> <!--ERROR: ERROR,FATAL-->
> <!--WARN: WARN,ERROR,FATAL-->
> <!--INFO: INFO,WARN,ERROR,FATAL-->
> <!--DEBUG: INFO,WARN,ERROR,FATAL-->
> <!--ALL: DEBUG,INFO,WARN,ERROR,FATAL-->
> <priority value="ALL"/>
> <!-- TODO 修改成你自己的类型 -->
> <level value="INFO"/>
>
> <!-- 写入文本文件 -->
> <appender-ref ref="RollingAppender"/>
> <!-- 写入数据库 -->
> <appender-ref ref="AdoNetAppenderSqlServer"/>
> <!-- 控制台输出 -->
> <appender-ref ref="ConsoleAppender"/>
> </root>
> </log4net>
> ```
## 统一日期返回格式
对于控制器来说,需要指定返回日期格式可以看下这个,
```csharp
/// <summary>
/// 自定义Json转换器用于将DateTime类型转换为JSON格式
/// </summary>
public class JsonDateTimeConverter : JsonConverter<DateTime>
{
/// <summary>
/// 重写读取方法将JSON数据转换为DateTime类型
/// </summary>
/// <param name="reader">JSON读取器</param>
/// <param name="typeToConvert">要转换的目标类型</param>
/// <param name="options">JSON序列化选项</param>
/// <returns>转换后的DateTime对象</returns>
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.TryParse(reader.GetString(), out var date) ? date : default;
}
/// <summary>
/// 重写写入方法将DateTime对象转换为JSON格式并写入JSON写入器
/// </summary>
/// <param name="writer">JSON写入器</param>
/// <param name="value">要写入的DateTime对象</param>
/// <param name="options">JSON序列化选项</param>
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
// 将DateTime对象转换为特定格式的字符串并写入JSON写入器
writer.WriteStringValue(value.ToString(LocalDateTimeConstant.DefaultDateTimeSecondFormat));
}
}
```
## 常见过滤器
Authorization Filter授权过滤器用于控制用户是否有权限访问特定的资源。
Action Filter动作过滤器在执行动作方法前后执行特定的逻辑。
Result Filter结果过滤器在执行结果前后执行特定的逻辑。
Exception Filter异常过滤器用于处理应用程序中发生的异常。
1. AuthorizationFilterAttribute
是所有过滤器中最先执行的。
用于执行授权检查。
只有一个方法OnAuthorization它在ActionFilterAttribute的OnActionExecuting之前执行。
如果授权失败可以抛出AuthorizationException或返回Challenge或Forbid结果来阻止请求继续执行。
2. ResourceFilterAttribute
用于在动作方法执行前后立即执行逻辑但比ActionFilterAttribute更早或更晚。
有两个方法OnResourceExecuting和OnResourceExecuted。
可以用来执行需要在动作方法执行前或后立即进行的操作,例如缓存。
3. ExceptionFilterAttribute
专门用于处理异常。
有两个方法OnException和OnExceptionAsync。
通常用于捕获和处理动作方法执行过程中抛出的异常。
由于它在异常发生后才执行,因此不适合用于清理资源,因为那时资源可能已经被破坏。
4. ResultFilterAttribute
用于在动作结果执行前后执行逻辑。
有两个方法OnResultExecuting和OnResultExecuted。
与ActionFilterAttribute类似但专注于动作结果的处理例如渲染视图或返回JSON数据。
5. FormatFilterAttribute
是一个内置的过滤器,用于处理内容协商。
自动添加到控制器或动作方法上,当请求指定媒体类型时触发。
6. ServiceFilterAttribute
用于从服务容器中解析并执行一个过滤器实例。
不是直接实现过滤器逻辑,而是引用一个已经注册到依赖注入容器中的过滤器实例。
7. TypeFilterAttribute
类似于ServiceFilterAttribute但它是通过反射创建过滤器实例不需要过滤器在DI容器中注册。
可以接受参数,用于构造过滤器实例。
![](https://learn.microsoft.com/zh-cn/aspnet/core/mvc/controllers/filters/_static/filter-pipeline-2.png?view=aspnetcore-8.0)