diff --git a/Apewer.Source/Apewer.Source.csproj b/Apewer.Source/Apewer.Source.csproj index ae9a2a0..c0664e0 100644 --- a/Apewer.Source/Apewer.Source.csproj +++ b/Apewer.Source/Apewer.Source.csproj @@ -11,7 +11,7 @@ - + diff --git a/Apewer.Source/Source/Sqlite.cs b/Apewer.Source/Source/Sqlite.cs index b794284..5405562 100644 --- a/Apewer.Source/Source/Sqlite.cs +++ b/Apewer.Source/Source/Sqlite.cs @@ -12,6 +12,7 @@ namespace Apewer.Source { /// 连接 SQLite 数据库的客户端。 + /// Require System.Data.SQLite.Core v1.0.110 public class Sqlite : DbClient { @@ -61,7 +62,7 @@ namespace Apewer.Source // 使用文件。 if (!File.Exists(path)) throw new FileNotFoundException("文件不存在。", path); - _connstr = $"data source='{_path}'; version=3; "; + _connstr = $"data source='{path}'; version=3; "; _path = path; if (!string.IsNullOrEmpty(pass)) { diff --git a/Apewer.Web/Web/AspNetCoreServer.cs b/Apewer.Web/Web/AspNetCoreServer.cs index 289aeed..1222f53 100644 --- a/Apewer.Web/Web/AspNetCoreServer.cs +++ b/Apewer.Web/Web/AspNetCoreServer.cs @@ -14,6 +14,7 @@ using System.Net.WebSockets; using System.Security.Cryptography.X509Certificates; using System.Net.Security; using System.Security.Authentication; +using Microsoft.Extensions.Logging.Console; namespace Apewer.Web { @@ -38,6 +39,24 @@ namespace Apewer.Web try { _builder = Host.CreateDefaultBuilder(); + + if (!LogConsole) + { + _builder.ConfigureLogging((context, builder) => + { + if (context.HostingEnvironment.IsProduction()) + { + foreach (var sd in builder.Services) + { + if (sd.ImplementationType.Equals(typeof(ConsoleLoggerProvider))) + { + builder.Services.Remove(sd); + break; + } + } + } + }); + } _builder.ConfigureWebHostDefaults((builder) => { Configure(builder); @@ -117,6 +136,10 @@ namespace Apewer.Web set { if (!_running) _compression = value; } } + /// 使用默认的控制台日志。 + /// 默认值:TRUE + public bool LogConsole { get; set; } = true; + #endregion } diff --git a/Apewer/Apewer.props b/Apewer/Apewer.props index 5099d3b..1d64731 100644 --- a/Apewer/Apewer.props +++ b/Apewer/Apewer.props @@ -9,7 +9,7 @@ Apewer Apewer Libraries - 6.7.1 + 6.7.2 diff --git a/Apewer/Json.cs b/Apewer/Json.cs index 2ba0525..a59f9b7 100644 --- a/Apewer/Json.cs +++ b/Apewer/Json.cs @@ -36,7 +36,7 @@ namespace Apewer private static bool _recursively = true; [NonSerialized] - private static bool _forceall = false; + private static bool _forceall = true; /// 允许产生 Exception,默认为不允许。 public static bool AllowException { get { return _throw; } set { _throw = value; } } @@ -1166,17 +1166,20 @@ namespace Apewer { if (list == null) return null; if (list is IToJson) return ((IToJson)list).ToJson(); - // IList 不再需要 SerializableAttribute 特性。 - // { - // var ltype = list.GetType(); - // var sas = ltype.GetCustomAttributes(typeof(SerializableAttribute), false); - // if (sas == null || sas.Length < 1) return null; - // } var checker = list as IToJsonChecker; var json = NewArray(); try { + if (list is Array array) + { + if (array != null && array.Rank > 1) + { + ToJson(json, array, array.Rank, new int[0]); + return json; + } + } + foreach (var item in list) { // 由 IToJsonChecker 实现的方法检查是否包含元素。 @@ -1584,6 +1587,38 @@ namespace Apewer } } + private static void ToJson(Json parent, Array array, int rank, int[] indices) + { + var dimension = indices.Length; + + var subIndices = new int[dimension + 1]; + if (dimension > 0) indices.CopyTo(subIndices, 0); + var subLength = array.GetLength(dimension); + + var end = dimension + 1 >= rank; + if (end) + { + var subLinear = new object[subLength]; + for (var i = 0; i < subLength; i++) + { + subIndices[dimension] = i; + subLinear[i] = array.GetValue(subIndices); + } + var subJson = Json.From(subLinear); + parent.Reset(subJson); + } + else + { + for (var i = 0; i < subLength; i++) + { + subIndices[dimension] = i; + var subJson = Json.NewArray(); + ToJson(subJson, array, rank, subIndices); + parent.AddItem(subJson); + } + } + } + #endregion #region Json -> String @@ -1624,7 +1659,7 @@ namespace Apewer } /// 将 Json 填充到数组列表,失败时返回 NULL 值。 - internal static T[] Array(Json json, bool ignoreCase = true, string ignoreCharacters = null, bool force = false) where T : class, new() + internal static T[] Array(Json json, bool ignoreCase = true, string ignoreCharacters = null, bool force = false) { if (json == null) return null; if (json._jtoken == null) return null; diff --git a/Apewer/Network/HttpClient.cs b/Apewer/Network/HttpClient.cs index 4167497..e313ac3 100644 --- a/Apewer/Network/HttpClient.cs +++ b/Apewer/Network/HttpClient.cs @@ -121,7 +121,9 @@ namespace Apewer.Network } /// 发送请求,并获取响应。 - public Exception Send() + /// 捕获发生的异常,将异常作为返回值。 + /// 发生的异常。 + public Exception Send(bool catchEx = true) { lock (_locker) { @@ -134,7 +136,12 @@ namespace Apewer.Network ResponseData = default; var url = Url; - if (url.IsEmpty()) return Return(new MissingMemberException("未指定 URL。")); + if (url.IsEmpty()) + { + var ex = new MissingMemberException("未指定 URL。"); + if (!catchEx) throw ex; + return Return(ex); + } var method = Method; if (method == HttpMethod.NULL) @@ -143,21 +150,30 @@ namespace Apewer.Network else method = HttpMethod.GET; } - try + if (catchEx) + { + try + { + _request = Prepare(url, method); + _response = _request.GetResponse() as HttpWebResponse; + Parse(_response); + } + catch (Exception ex) + { + _exception = ex; + if (ex is WebException webEx) Parse((HttpWebResponse)webEx.Response); + return ex; + } + } + else { _request = Prepare(url, method); _response = _request.GetResponse() as HttpWebResponse; Parse(_response); - return null; - } - catch (Exception ex) - { - _exception = ex; - if (ex is WebException webEx) Parse((HttpWebResponse)webEx.Response); - return ex; } - } + + return null; } /// diff --git a/Apewer/RuntimeUtility.cs b/Apewer/RuntimeUtility.cs index 282f495..88a1175 100644 --- a/Apewer/RuntimeUtility.cs +++ b/Apewer/RuntimeUtility.cs @@ -794,16 +794,9 @@ namespace Apewer if (action == null) return 0; var stopwatch = new Stopwatch(); stopwatch.Start(); - try - { - action.Invoke(); - stopwatch.Stop(); - return stopwatch.ElapsedMilliseconds; - } - finally - { - stopwatch.Stop(); - } + action.Invoke(); + stopwatch.Stop(); + return stopwatch.ElapsedMilliseconds; } #endregion diff --git a/Apewer/Web/ApiOptions.cs b/Apewer/Web/ApiOptions.cs index 2398f02..9911dc8 100644 --- a/Apewer/Web/ApiOptions.cs +++ b/Apewer/Web/ApiOptions.cs @@ -7,7 +7,7 @@ namespace Apewer.Web { /// 选项。 - public class ApiOptions + public sealed class ApiOptions { /// 允许压缩。 diff --git a/Apewer/Web/ApiProcessor.cs b/Apewer/Web/ApiProcessor.cs index fcb9991..a916057 100644 --- a/Apewer/Web/ApiProcessor.cs +++ b/Apewer/Web/ApiProcessor.cs @@ -1,11 +1,7 @@ using Apewer.Network; using Apewer.Source; using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; using System.Net; -using System.Reflection; using static Apewer.Web.ApiUtility; @@ -21,8 +17,7 @@ namespace Apewer.Web internal Action Catcher = null; ApiOptions Options = null; - Stopwatch Stopwatch = null; - + DateTime Begin = DateTime.UtcNow; Uri Url = null; HttpMethod Method = HttpMethod.NULL; ApiRequest ApiRequest = null; @@ -36,31 +31,21 @@ namespace Apewer.Web public string Run() { if (Options == null) Options = new ApiOptions(); - if (Options.WithDuration) - { - Stopwatch = new Stopwatch(); - Stopwatch.Start(); - } var error = Flow(); - if (Stopwatch != null) - { - Stopwatch.Stop(); - Stopwatch = null; - } return error; } string Flow() { + // 传入字段。 + if (Provider == null) return "服务程序无效。"; + if (Invoker == null) return "调用器无效。"; + if (Entries == null) return "入口无效。"; + Options = Invoker.Options ?? new ApiOptions(); + Provider.Options = Options; + try { - // 传入字段。 - if (Provider == null) return "服务程序无效。"; - if (Invoker == null) return "调用器无效。"; - if (Entries == null) return "入口无效。"; - Options = Invoker.Options ?? new ApiOptions(); - Provider.Options = Options; - // 检查执行的前提条件,获取 Method 和 URL。 var check = Check(); if (!string.IsNullOrEmpty(check)) return check; @@ -77,28 +62,36 @@ namespace Apewer.Web if (!string.IsNullOrEmpty(invoke)) return invoke; // 输出。 - if (Stopwatch != null) - { - Stopwatch.Stop(); - ApiResponse.Duration = Stopwatch.ElapsedMilliseconds; - Stopwatch = null; - } + ApiResponse.Duration = Duration(); Output(Provider, Options, ApiResponse, ApiRequest, Method); return null; } catch (Exception ex) { - if (Stopwatch != null) - { - Stopwatch.Stop(); - Stopwatch = null; - } var message = ex.Message(); Logger.Internals.Error(typeof(ApiInvoker), message); return message; } } + string Duration() + { + var span = DateTime.UtcNow - Begin; + var ms = span.TotalMilliseconds; + if (ms > 0D) + { + var s = span.TotalMilliseconds / 1000D; + if (s > 10D) return Math.Round(s, 1).ToString() + "s"; + if (s > 1D) return Math.Round(s, 2).ToString() + "s"; + if (ms > 10D) return Math.Round(ms, 0).ToString() + "ms"; + return Math.Round(ms, 1).ToString() + "ms"; + } + else + { + return null; + } + } + string Check() { // 服务程序检查。 @@ -147,7 +140,7 @@ namespace Apewer.Web Invoke(Entries.Get(appName)); - if (Stopwatch != null) ApiResponse.Duration = Stopwatch.ElapsedMilliseconds; + ApiResponse.Duration = Duration(); ApiResponse.Application = appName; ApiResponse.Function = funcName; ApiResponse.Random = random; @@ -489,7 +482,7 @@ namespace Apewer.Web // 包含 API 的处理时间。 if (options.WithDuration && response != null) { - merged.Add("Duration", response.Duration.ToString() + "ms"); + if (response.Duration.NotEmpty()) merged.Add("Duration", response.Duration); } } if (response != null) @@ -534,7 +527,7 @@ namespace Apewer.Web // 持续时间。 if (options.WithDuration) { - json.SetProperty("duration", response.Duration); + if (response.Duration.NotEmpty()) json.SetProperty("duration", response.Duration); } // 随机值。 diff --git a/Apewer/Web/ApiResponse.cs b/Apewer/Web/ApiResponse.cs index 9472be5..ac84c94 100644 --- a/Apewer/Web/ApiResponse.cs +++ b/Apewer/Web/ApiResponse.cs @@ -18,8 +18,8 @@ namespace Apewer.Web internal bool StopReturn = false; - /// API 的执行时间,以毫秒为单位。 - public long Duration { get; set; } + /// API 的执行时间。 + public string Duration { get; set; } /// Application。 public string Application { get; set; } diff --git a/Apewer/_Extensions.cs b/Apewer/_Extensions.cs index 2f006d7..58ab8d9 100644 --- a/Apewer/_Extensions.cs +++ b/Apewer/_Extensions.cs @@ -346,7 +346,7 @@ public static class Extensions /// 忽略属性名称大小写。 /// 忽略的属性名称字符。 /// 强制填充,忽略 的 Serializable 特性。 - public static T[] Array(this Json @this, bool ignoreCase = true, string ignoreChars = null, bool force = false) where T : class, new() => Apewer.Json.Array(@this, ignoreCase, ignoreChars, force); + public static T[] Array(this Json @this, bool ignoreCase = true, string ignoreChars = null, bool force = false) => Apewer.Json.Array(@this, ignoreCase, ignoreChars, force); /// 设置属性名称为小写。 public static Json Lower(this Json @this) => Apewer.Json.Lower(@this); diff --git a/ChangeLog.md b/ChangeLog.md index 3392e22..452fe58 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,16 @@  ### 最新提交 +### 6.7.2 +- 问题修正 + - 修正了 SQLite 的构造函数中的 path 参数; + - Apewer.Source 不再包含对 System.Data.SQLite.Core 的依赖,最终代码项目中需要额外添加引用。 +- 新特性 + - Json 强制序列化选项已修改为 true,以简化最终代码; + - Json 解析新增支持数组类型,提升性能; + - HttpClient 增加了参数 catchEx = true; + - AspNetCoreServer 新增了 LogConsole 属性,默认值为 true,用于配置控制台输出。 + ### 6.7.1 - 问题修正 - 修正了 Evaluate 可能不释放计时器的问题;