Giter Site home page Giter Site logo

xianhc / apevolo-api Goto Github PK

View Code? Open in Web Editor NEW
803.0 803.0 73.0 13.06 MB

.Net 8 、SqlSugar ORM、Vue 2.X、RBAC、前后端分离的开箱则用的企业级中后台权限管理系统

Home Page: https://www.apevolo.com

License: Apache License 2.0

C# 99.68% HTML 0.32%
aop async-await authentication authorization autofac automapper cors dependency-injection jwt log4net miniprofiler mysql netcore quartz-net rbac-roles redis sqlsugarcore swagger-ui

apevolo-api's People

Contributors

xianhc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

apevolo-api's Issues

\apevolo-api\ApeVolo.Repository\Base\SugarHandler.cs 为啥要创建2个private SqlSugarClient?

        public SugarHandler(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
            _dbBase = unitOfWork.GetDbClient();
        }

        internal ISqlSugarClient Db
        {
            get { return _db; }
        }

        private readonly IUnitOfWork _unitOfWork;
        private SqlSugarClient _dbBase;

        private ISqlSugarClient _db => _dbBase;

对于引用类型,两种变量可引用同一对象;因此,对一个变量执行的操作会影响另一个变量所引用的对象。
_db 和_dbBase 保留一个,_unitOfWork可以直接去掉

        public SugarHandler(IUnitOfWork unitOfWork)
        {
            //_unitOfWork = unitOfWork;
            _db = unitOfWork.GetDbClient();
        }

        internal ISqlSugarClient Db
        {
            get { return _db; }
        }

        //private readonly IUnitOfWork _unitOfWork;
        private SqlSugarClient _db;

        //private ISqlSugarClient _db => _dbBase;

btw:警告能不能都消一下?

1>D:\github\apevolo-api\ApeVolo.Common\SnowflakeIdHelper\SnowflakeIdWorker.cs(53,47,53,50): warning CS0628: “SnowflakeIdWorker.WorkerId.set”: 在密封类型中声明了新的保护成员
1>D:\github\apevolo-api\ApeVolo.Common\SnowflakeIdHelper\SnowflakeIdWorker.cs(54,51,54,54): warning CS0628: “SnowflakeIdWorker.DatacenterId.set”: 在密封类型中声明了新的保护成员
1>D:\github\apevolo-api\ApeVolo.Common\Extention\Ext.Byte.cs(163,20,163,45): warning SYSLIB0011: “IFormatter.Deserialize(Stream)”已过时:“BinaryFormatter serialization is obsolete and should not be used. See https://aka.ms/binaryformatter for more information.”
1>D:\github\apevolo-api\ApeVolo.Common\Caches\Redis\Service\MessageQueue\InitCore.cs(52,65,52,67): warning CS1998: 此异步方法缺少 "await" 运算符,将以同步方式运行。请考虑使用 "await" 运算符等待非阻止的 API 调用,或者使用 "await Task.Run(...)" 在后台线程上执行占用大量 CPU 的工作。
1>D:\github\apevolo-api\ApeVolo.Common\Helper\SaltKeyHelper.cs(18,28,18,52): warning SYSLIB0023: “RNGCryptoServiceProvider”已过时:“RNGCryptoServiceProvider is obsolete. To generate a random number, use one of the RandomNumberGenerator static methods instead.”
1>D:\github\apevolo-api\ApeVolo.Common\Extention\Ext.Object.cs(27,13,27,41): warning SYSLIB0011: “IFormatter.Serialize(Stream, object)”已过时:“BinaryFormatter serialization is obsolete and should not be used. See https://aka.ms/binaryformatter for more information.”
1>D:\github\apevolo-api\ApeVolo.Common\Extention\Ext.String.cs(186,29,186,54): warning SYSLIB0021: “SHA1CryptoServiceProvider”已过时:“Derived cryptographic types are obsolete. Use the Create method on the base type instead.”
1>D:\github\apevolo-api\ApeVolo.Common\Helper\HttpHelper.cs(160,50,160,75): warning SYSLIB0014: “WebRequest.Create(string)”已过时:“WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.”
1>D:\github\apevolo-api\ApeVolo.Common\Helper\HttpHelper.cs(321,61,321,83): warning SYSLIB0014: “WebRequest.Create(string)”已过时:“WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.”
1>D:\github\apevolo-api\ApeVolo.Common\Caches\Redis\Service\MessageQueue\InitCore.cs(175,69,175,71): warning CS1998: 此异步方法缺少 "await" 运算符,将以同步方式运行。请考虑使用 "await" 运算符等待非阻止的 API 调用,或者使用 "await Task.Run(...)" 在后台线程上执行占用大量 CPU 的工作。
1>D:\github\apevolo-api\ApeVolo.Common\Caches\Redis\Service\MessageQueue\InitCore.cs(96,45,96,47): warning CS1998: 此异步方法缺少 "await" 运算符,将以同步方式运行。请考虑使用 "await" 运算符等待非阻止的 API 调用,或者使用 "await Task.Run(...)" 在后台线程上执行占用大量 CPU 的工作。
1>D:\github\apevolo-api\ApeVolo.Common\Caches\Redis\Service\MessageQueue\ServiceCollectionExtensions.cs(19,77,19,87): warning CS1574: XML 注释中有未能解析的 cref 特性“CapOptions”
1>D:\github\apevolo-api\ApeVolo.Common\Caches\Redis\Service\MessageQueue\ServiceCollectionExtensions.cs(20,36,20,46): warning CS1574: XML 注释中有未能解析的 cref 特性“CapBuilder”

最后2个,我发了一个issue,wmowm/InitQ#8

Description = desc.IsNull() ? "" : ((DescriptionAttribute)desc).Description, IsNull这个有警告

AuditingFilter.cs(130,27,130,31): warning CS8604: “bool ExtObject.IsNull(object obj)”中的形参“obj”可能传入 null 引用实参。
Description = desc.IsNull() ? "" : ((DescriptionAttribute)desc).Description,
desc在此处可能为null。

需要修改为:
Description = desc == null ? "" : ((DescriptionAttribute)desc).Description,

或者修改为:(object加上?)

public static bool IsNotNull(this object? obj)
    {
        return obj != null;
    }

IsNull封装null的判断的确

FxAssemblies和EntityAssemblys硬编码可以去掉

    static readonly List<string> FxAssemblies = new List<string>
        { "ApeVolo.Repository", "ApeVolo.Business", "ApeVolo.IBusiness", "ApeVolo.Entity" };

    static readonly List<string> EntityAssemblys = new List<string> { "ApeVolo.Entity" };

这些代码硬编码,不够灵活了
参考文章:https://dotnetcoretutorials.com/2020/07/03/getting-assemblies-is-harder-than-you-think-in-c/

        var allTypes = new List<Type>();
        var assemblyNames = Assembly.GetEntryAssembly()?.GetReferencedAssemblies().Where(x => x.Name != null && x.Name.StartsWith("ApeVolo.")); // .Select(r => r.FullName)
        assemblyNames?.Select(r => Assembly.Load(r)).ToList().ForEach(a => allTypes.AddRange(a.GetTypes()));
        FxAllTypes = allTypes;

        // 实体
        List<Type> entityTypes = new List<Type>();
        Assembly.GetEntryAssembly()?.GetReferencedAssemblies()
            .Where(x => x.Name != null && x.Name.Contains(".Entity"))
            .Select(x => Assembly.Load(x)).ToList()
            .ForEach(e => { entityTypes.AddRange(e.GetTypes()); });
        //var entityAssemblys = EntityAssemblys.Select(x => Assembly.Load(x)).ToList();
        //entityAssemblys.ForEach(entityAssembly => { entityTypes.AddRange(entityAssembly.GetTypes()); });
        EntityTypes = entityTypes;

初始化数据库....
初始化数据库成功。

初始化数据表....
Entity:QuartzNet-->Table:sys_quartz_job-->Desc:作业调度-->创建完成!
Entity:QuartzNetLog-->Table:sys_quartz_job_log-->Desc:作业调度执行日志-->创建完成!
Entity:AuditLog-->Table:sys_audit_log-->Desc:审计日志表-->创建完成!
Entity:Log-->Table:sys_log-->Desc:系统日志表-->创建完成!
Entity:EmailAccount-->Table:sys_email_account-->Desc:邮件账户表-->创建完成!
Entity:MessageTemplate-->Table:sys_email_message_template-->Desc:邮件消息模板-->创建完成!
Entity:QueuedEmail-->Table:sys_queued_email-->Desc:邮件队列-->创建完成!
Entity:Dict-->Table:sys_dict-->Desc:字典表-->创建完成!
Entity:DictDetail-->Table:sys_dict_detail-->Desc:字典详细表-->创建完成!
Entity:AppSecret-->Table:sys_app_secret-->Desc:三方应用秘钥表-->创建完成!
Entity:Department-->Table:sys_department-->Desc:部门表-->创建完成!
Entity:FileRecord-->Table:sys_file_record-->Desc:文件记录表-->创建完成!
Entity:Job-->Table:sys_job-->Desc:岗位表-->创建完成!
Entity:Menu-->Table:sys_menu-->Desc:系统菜单表-->创建完成!
Entity:Role-->Table:sys_role-->Desc:角色表-->创建完成!
Entity:RoleMenu-->Table:sys_roles_menus-->Desc:角色与菜单关联表-->创建完成!
Entity:RolesDepartments-->Table:sys_roles_depts-->Desc:角色部门-->创建完成!
Entity:Setting-->Table:sys_setting-->Desc:系统设置表-->创建完成!
Entity:TestApeVolo-->Table:test_apevolo-->Desc:测试表-->创建完成!
Entity:User-->Table:sys_user-->Desc:系统用户表-->创建完成!
Entity:UserJobs-->Table:sys_users_jobs-->Desc:用户岗位关联表-->创建完成!
Entity:UserRoles-->Table:sys_users_roles-->Desc:用户角色关联表-->创建完成!
初始化数据表成功!

测试没有问题。

\ApeVolo.Common\Caches\Redis\Service\MessageQueue\InitCore.cs

await Task.Run(async () =>
                                    {
                                        if (parameterInfos.Length == 0)
                                        {
                                            consumerExecutorDescriptor.MethodInfo.Invoke(obj, null);
                                        }
                                        else
                                        {
                                            object[] parameters = { res };
                                            consumerExecutorDescriptor.MethodInfo.Invoke(obj, parameters);
                                        }
                                    });

这里的async需要去掉

计算SHA1摘要

    /// <summary>
    /// 计算SHA1摘要
    /// </summary>
    /// <param name="str">字符串</param>
    /// <param name="encoding">编码</param>
    /// <returns></returns>
    public static byte[] ToSha1Bytes(this string str, Encoding encoding)
    {
#pragma warning disable CS0618
        SHA1 sha1 = new SHA1CryptoServiceProvider();
#pragma warning restore CS0618
        byte[] inputBytes = encoding.GetBytes(str);
        byte[] outputBytes = sha1.ComputeHash(inputBytes);

        return outputBytes;
    }

可以修改为:

    /// <summary>
    /// 计算SHA1摘要
    /// </summary>
    /// <param name="str">字符串</param>
    /// <param name="encoding">编码</param>
    /// <returns>返回SHA1摘要</returns>
    public static byte[] ToSha1Bytes(this string str, Encoding encoding)
    {
        using var sha1 = SHA1.Create();
        byte[] inputBytes = encoding.GetBytes(str);
        return sha1.ComputeHash(inputBytes);
    }

其实我应该给你pr的...不过提交pr改了的比较多,怕乱了版本

问题不少,还有待迭代...

找轮子ing,被项目介绍吸引到了

直接下载源码,改Appsetting,vs2022直接跑,异常,_lock = null,看了下源码 :
public class LogHelper
{
static UsingLock _lock;
public LogHelper()
{
_lock = new UsingLock();
}
...
public static void WriteSqlLog(string filename, string[] dataParas, bool isHeader = true)
{
...
using (_lock.Write())//出问题的代码,未初始化直接使用
{
File.AppendAllText(logFilePath, logContent);
}
...
}
...
}

调用来源:LogHelper.WriteSqlLog(...);

这代码是不是有点不对 ?没有 new LogHelper >> 没初始化 _lock >> _lock.Write() 不就出错了 ?

默认配置理论上来说不少人跑过,没发现有人发现反馈,难道只有我出错了?我打开方式不对?

这问题倒是小问题,可直接跑默认设置都能出错,总感觉有点...

然后代码风格建议注意下,虽然说风格没有标准,但是项目中很多拼接和换行看起来还是怪怪的,比如这种:
if (isHeader)
{
logContent =
"--------------------------------\r\n" +
DateTime.Now + "|\r\n" +
string.Join("\r\n", dataParas) + "\r\n"
;
}

感觉这也太随意了点,身为深度强迫症的我表示...

还有,既然支持mssql,考虑下把兼容进一步处理下?不然切数据库还需要手动修正,也是个麻烦事儿,毕竟工程量也不小(这个纯个人建议,非bug,只是觉得如果项目主打易用性,这种小问题处理下更好).

话说回来,写文件好像不用锁? 设置共享模式,就没有文件占用问题?

\Controllers\Base\BaseController.cs中 HttpContext 不是线程安全型

Path = HttpContextCore.CurrentHttpContext.Request.Path.Value?.ToLower()

修改为:

Path = HttpContext.Request.Path.Value?.ToLower()

\Filter\AuditingFilter.cs 同样的问题,不需要通过AutofacHelper.GetService()去拿HttpContext

if (HttpContextCore.CurrentHttpContext.IsNotNull() && result != null)
                {
                    var reqUrlPath = HttpContextCore.CurrentHttpContext.Request.Path.Value?.ToLower();

修改为

if (context.HttpContext.IsNotNull() && result != null)
                {
                    var reqUrlPath = context.HttpContext.Request.Path.Value?.ToLower();

var auditLog = new AuditLog
        {
            Id = IdHelper.GetLongId(),
            CreateBy = _currentUser.Name ?? "",
            CreateTime = DateTime.Now,
            Area = routeValues["area"],
            Controller = routeValues["controller"],
            Action = routeValues["action"],
            Method = context.HttpContext.Request.Method,
            Description = desc == null ? "" : ((DescriptionAttribute)desc).Description,
            RequestUrl = HttpContextCore.CurrentHttpContext.Request.GetDisplayUrl() ?? "",
            RequestParameters = decs.ToJson(),
            BrowserName = browserName,
            BrowserVersion = browserVersion,
            BrowserOS = browserOS,
            BrowserDeviceType = browserDeviceType,
            RequestIp = remoteIpAddress,
            IpAddress = IpHelper.GetIpAddress(remoteIpAddress)
        };

        var reqUrl = HttpContextCore.CurrentHttpContext.Request.Path.Value?.ToLower();

修改为:

        var auditLog = new AuditLog
        {
            Id = IdHelper.GetLongId(),
            CreateBy = _currentUser.Name ?? "",
            CreateTime = DateTime.Now,
            Area = routeValues["area"],
            Controller = routeValues["controller"],
            Action = routeValues["action"],
            Method = context.HttpContext.Request.Method,
            Description = desc == null ? "" : ((DescriptionAttribute)desc).Description,
            RequestUrl = context.HttpContext.Request.GetDisplayUrl() ?? "",
            RequestParameters = decs.ToJson(),
            BrowserName = browserName,
            BrowserVersion = browserVersion,
            BrowserOS = browserOS,
            BrowserDeviceType = browserDeviceType,
            RequestIp = remoteIpAddress,
            IpAddress = IpHelper.GetIpAddress(remoteIpAddress)
        };

        var reqUrl = context.HttpContext.Request.Path.Value?.ToLower();

\Filter\GlobalExceptionFilter.cs
context.Result = new ContentResult

        {
            Content = new ActionResultVm
            {
                Status = statusCode,
                Error = GetExceptionError(statusCode),
                Message = throwMsg,
                Path = HttpContextCore.CurrentHttpContext.Request.Path.Value?.ToLower()
            }.ToJson(),
            ContentType = "application/json; charset=utf-8",
            StatusCode = statusCode
        };

修改为:

context.Result = new ContentResult
        {
            Content = new ActionResultVm
            {
                Status = statusCode,
                Error = GetExceptionError(statusCode),
                Message = throwMsg,
                Path = context.HttpContext.Request.Path.Value?.ToLower()
            }.ToJson(),
            ContentType = "application/json; charset=utf-8",
            StatusCode = statusCode
        };

WriteLog(ExceptionContext context) 这个方法
```
HttpContextCore.CurrentHttpContext.Request.GetDisplayUrl() ?? "", context.HttpContext.Request.Method,
HttpContextCore.CurrentHttpContext.Request.Body.ReadToString() ?? "", _currentUser.Name ?? "",


修改为:

            ```
context.HttpContext.Request.GetDisplayUrl() ?? "", context.HttpContext.Request.Method,
            context.HttpContext.Request.Body.ReadToString() ?? "", _currentUser.Name ?? "",

RequestUrl = HttpContextCore.CurrentHttpContext.Request.GetDisplayUrl() ?? "",
修改为:
RequestUrl = context.HttpContext.Request.GetDisplayUrl() ?? "",

关于HttpContext:

HttpContext 不是线程安全型

请勿在并行任务中引用 HttpContext 数据。 在开始并行任务之前,从上下文中提取所需的数据。

在处理请求之外读取或写入 HttpContext 的属性可能会导致 [NullReferenceException]
(https://docs.microsoft.com/zh-cn/dotnet/api/system.nullreferenceexception)。

HttpContextCore.CurrentHttpContext 是静态调用,这个在后台处理的部分代码会抛NullReferenceException,HttpContext 是不太推荐被静态使用,主要是这里的问题。

共享使用单一实例HttpContext我记得是有问题的,不太推荐这种静态调用的写法

GetManifestResourceStream的返回值有可能为null,UseSwaggerMiddleware的参数Stream需要加上?

//Swagger UI
        app.UseSwaggerMiddleware(() =>
            GetType().GetTypeInfo().Assembly.GetManifestResourceStream("ApeVolo.Api.index.html"));

GetManifestResourceStream的返回值有可能为null

UseSwaggerMiddleware 的定义 需要修改为
public static void UseSwaggerMiddleware(this IApplicationBuilder app, Func<Stream?> streamHtml)

//
        // 摘要:
        //     Loads the specified manifest resource from this assembly.
        //
        // 参数:
        //   name:
        //     The case-sensitive name of the manifest resource being requested.
        //
        // 返回结果:
        //     The manifest resource; or null if no resources were specified during compilation
        //     or if the resource is not visible to the caller.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     The name parameter is null.
        //
        //   T:System.ArgumentException:
        //     The name parameter is an empty string ("").
        //
        //   T:System.IO.FileLoadException:
        //     A file that was found could not be loaded. Note: In .NET for Windows Store apps
        //     or the Portable Class Library, catch the base class exception, System.IO.IOException,
        //     instead.
        //
        //   T:System.IO.FileNotFoundException:
        //     name was not found.
        //
        //   T:System.BadImageFormatException:
        //     name is not a valid assembly.
        //
        //   T:System.NotImplementedException:
        //     Resource length is greater than System.Int64.MaxValue.
        public virtual Stream? GetManifestResourceStream(string name);

随机盐的算法

    /// <summary>
    /// Creates a salt
    /// </summary>
    /// <param name="size">A salt size</param>
    /// <returns>A salt</returns>
    public static string CreateSalt(int size)
    {
#pragma warning disable CS0618
        var provider = new RNGCryptoServiceProvider();
#pragma warning restore CS0618
        byte[] data = new byte[size];
        provider.GetBytes(data);
        return Convert.ToBase64String(data);
    }

RNGCryptoServiceProvider过期了,随机盐的算法可以修改为:

    /// <summary>
    /// Creates a salt
    /// </summary>
    /// <param name="size">A salt size</param>
    /// <returns>A salt</returns>
    public static string CreateSalt(int size)
    {
        var random = RandomNumberGenerator.Create();
        byte[] bytes = new byte[size];
        random.GetNonZeroBytes(bytes);
        return Convert.ToBase64String(bytes);
    }

运行这个代码:
var salt = SaltKeyHelper.CreateSalt(128);
salt的值:
MDk9cFCouGfI2LSMEiJ6ct0Bijmk7NLYnUitgrxgPjJ/Gl5itTIvSmp+C3Vq6vNw91MWFqCWJkx8qpqFXc4GQ47gSyaaaYYBaYwLswcLhVAtfu5ans+lx+PElls6vL1iqikmAZ9zJfozNBE6lExNMCcEjtPY4kM5B2w9GjnpxjE=

如果使用

    /// <summary>
    /// Creates a salt
    /// </summary>
    /// <param name="size">A salt size</param>
    /// <returns>A salt</returns>
    public static string CreateSalt(int size)
    {
        var random = RandomNumberGenerator.Create();
        byte[] bytes = new byte[size];
        random.GetNonZeroBytes(bytes);
        return BitConverter.ToString(bytes).Replace("-", "");
    }

再运行这个代码:
var salt = SaltKeyHelper.CreateSalt(128);
salt的值:
559B8B08B3E016F70C13D5755E553562FE60E76A9AF055E1A2C3DD1E200E32E2BD1B4C91A9B44E4EB83320B94FE14343A417D4114F05F41C0CB83D191F9CB2ACA04D18332FEFEA67F29E245867F97A4D08485DF4456B2032792FEA0D12CDBCF15FD3D55892205C50DC939A3DDF82966531827E470974D4820D6E4EA6B85A5324

你喜欢哪个随机盐呢?base64编码看上去用的字符数量要少一些

GetIp 可以改的更简洁些

    /// <summary>
    /// 获取客户端IP地址
    /// </summary>
    /// <returns></returns>
    public static string GetIp()
    {
        if (HttpContextCore.CurrentHttpContext.Connection.RemoteIpAddress != null)
            return HttpContextCore.CurrentHttpContext.Connection.RemoteIpAddress.ToString();

        return "0.0.0.0";
    }

修改为:

    /// <summary>
    /// 获取客户端IP地址
    /// </summary>
    /// <returns></returns>
    public static string GetIp()
    {
        return HttpContextCore.CurrentHttpContext.Connection.RemoteIpAddress?.ToString() ?? "0.0.0.0";
    }

我对CurrentHttpContext这里的定义是有点疑问的,因为这个是静态类,如果高并发的时候,会不会拿不到正确的值?
我建议在控制器Controller里获取以后,作为参数传入,从Controller拿到的客户端IP肯定是正确的。

例如:\Controllers\AuthorizationController.cs

bool isTrue = await _onlineUserService.SaveAsync(jwtUserVo, token.ToString().Replace("Bearer ", ""));

修改为:

        // 保存在线信息
        string ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString() ?? "0.0.0.0";
        bool isTrue = await _onlineUserService.SaveAsync(jwtUserVo, token.ToString().Replace("Bearer ", ""), ipAddress);

\Core\IOnlineUserService.cs方法SaveAsync加上参数string ipAddress

    /// <summary>
    /// 保存在线用户
    /// </summary>
    /// <param name="jwtUserVo"></param>
    /// <param name="token"></param>
    /// <param name="remoteIpAddress">客户端IP地址</param>
    Task<bool> SaveAsync(JwtUserVo jwtUserVo, string token, string remoteIpAddress);

\Core\OnlineUserService.cs 方法SaveAsync加上参数string remoteIpAddress

    /// <summary>
    /// 保存在线用户
    /// </summary>
    /// <param name="jwtUserVo"></param>
    /// <param name="token"></param>
    /// <param name="remoteIpAddress">客户端IP地址</param>
    public async Task<bool> SaveAsync(JwtUserVo jwtUserVo, string token, string remoteIpAddress)
    {
        string browser = IpHelper.GetBrowserName();
        string address = IpHelper.GetIpAddress();

        OnlineUser onlineUser = new OnlineUser
        {
            UserId = jwtUserVo.User.Id,
            UserName = jwtUserVo.User.Username,
            NickName = jwtUserVo.User.NickName,
            Dept = jwtUserVo.User.Dept.Name,
            Browser = browser,
            Ip = remoteIpAddress,

ApeVoloRequiredAttribute 的AttributeUsage警告

// [AttributeUsage(AttributeTargets.Method)]
public class ApeVoloRequiredAttribute : Attribute
{
    /// <summary>
    /// 验证失败说明内容
    /// </summary>
    public string? Message { get; set; }
}

修改为:

[AttributeUsage(AttributeTargets.Field|AttributeTargets.Property)]
public class ApeVoloRequiredAttribute : Attribute
{
    /// <summary>
    /// 验证失败说明内容
    /// </summary>
    public string? Message { get; set; }
}

我看了下ApeVoloRequired的代码,都是属性,改成下面也可以

[AttributeUsage(AttributeTargets.Property)]
public class ApeVoloRequiredAttribute : Attribute
{
    /// <summary>
    /// 验证失败说明内容
    /// </summary>
    public string? Message { get; set; }
}

关于AttributeTargets枚举参考:
https://docs.microsoft.com/zh-cn/dotnet/api/system.attributetargets?view=net-6.0

\Helper\RequiredHelper.cs 中(dynamic)value的问题

            var value = pi.GetValue(entity, null);
            var dtlType = pi.PropertyType.Name;
            if (dtlType == "Int32")
            {
                if ((dynamic)value < 0)
                {
                    isTrue = false;
                    break;
                }
            }

            if (dtlType == "List`1")
            {
                var list = (dynamic)value;
                if (list == null || list.Count < 1)
                {
                    isTrue = false;
                    break;
                }
            }

微信截图_20220405021254
微信截图_20220405021316

除了这个null的问题外,其实这里的逻辑是不是有点小问题?

既然是用 ApeVoloRequiredAttribute 来判断Required的逻辑

            if (value == null)
            {
                isTrue = false;
                break;
            }

这个代码是否应该放到 var value = pi.GetValue(entity, null);的后面?

\ApeVolo.Api\ActionExtension\Sign\VerifySignatureAttribute.cs

// ...
var redisCacheService = AutofacHelper.GetService<IRedisCacheService>();
// ...

// ...
        var appSecretModel = await AutofacHelper.GetService<IAppSecretService>()
            .QueryFirstAsync(x => x.IsDeleted == false && x.AppId == appId);
// ...

VerifySignatureAttribute是Attribute,注入有几种办法,参考下面链接
https://www.andybutland.dev/2020/06/dependency-injection-in-aspnet-core-attributes.html

最简单的就是直接用ActionExecutingContext filterContext

var redisCacheService = filterContext.HttpContext.RequestServices.GetRequiredService<IRedisCacheService>();
var appSecretModel = await filterContext.HttpContext.RequestServices.GetRequiredService<IAppSecretService>().FirstAsync(x => x.IsDeleted == false && x.AppId == appId);

对于 TokenFilterAttribute.cs

    //private readonly IHttpContextAccessor _accessor = AutofacHelper.GetService<IHttpContextAccessor>();
    //private readonly ICurrentUser _currentUser = AutofacHelper.GetService<ICurrentUser>();
    public override async Task OnActionExecuting(ActionExecutingContext context)
    {
        try
        {
            var accessor = context.HttpContext.RequestServices.GetRequiredService<IHttpContextAccessor>();
            var currentUser = context.HttpContext.RequestServices.GetRequiredService<ICurrentUser>();

下面基于TypeFilterAttribute的定义技巧我觉得也不错,这样将过滤器用于给定的控制器或操作方法不过于冗长
对这个感兴趣,可以阅读下文中的链接。

public class AddRandomNumberHeader2Attribute : TypeFilterAttribute
    {
        public AddRandomNumberHeader2Attribute() : base(typeof(AddRandomNumberHeader2Filter))
        {
        }

        private class AddRandomNumberHeader2Filter : IResultFilter
        {
            private readonly IRandomNumberService _randomNumberService;

            public AddRandomNumberHeader2Filter(IRandomNumberService randomNumberService)
            {
                _randomNumberService = randomNumberService;
            }

            public void OnResultExecuted(ResultExecutedContext context)
            {
            }

            public void OnResultExecuting(ResultExecutingContext context)
            {
                context.HttpContext.Response.Headers.Add("X_Random_Number_3", _randomNumberService.GetRandomInteger().ToString());
            }
        }
    }

        [AddRandomNumberHeader2]
        public IActionResult Index()
        {
            return Content("Hello world.");
        }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.