diff --git a/01-Core/Jinget.Core.DiScanner/Jinget.Core.DiScanner.csproj b/01-Core/Jinget.Core.DiScanner/Jinget.Core.DiScanner.csproj index 8527782..cc903af 100644 --- a/01-Core/Jinget.Core.DiScanner/Jinget.Core.DiScanner.csproj +++ b/01-Core/Jinget.Core.DiScanner/Jinget.Core.DiScanner.csproj @@ -19,9 +19,9 @@ - - - + + + diff --git a/01-Core/Jinget.Core/Attributes/ValidationAttributes/DateGreaterThanAttribute.cs b/01-Core/Jinget.Core/Attributes/ValidationAttributes/DateGreaterThanAttribute.cs index 55596c7..b7633ac 100644 --- a/01-Core/Jinget.Core/Attributes/ValidationAttributes/DateGreaterThanAttribute.cs +++ b/01-Core/Jinget.Core/Attributes/ValidationAttributes/DateGreaterThanAttribute.cs @@ -5,7 +5,7 @@ public class DateGreaterThanAttribute(string otherPropertyName) : ValidationAttr /// /// check whether is greater than /// - protected override ValidationResult IsValid(object value, ValidationContext validationContext) + protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) { if (value == null) return ValidationResult.Success; // Allow nulls (optional, adjust as needed) diff --git a/01-Core/Jinget.Core/Attributes/ValidationAttributes/TimeGreaterThanAttribute.cs b/01-Core/Jinget.Core/Attributes/ValidationAttributes/TimeGreaterThanAttribute.cs index a174cd3..9ef932b 100644 --- a/01-Core/Jinget.Core/Attributes/ValidationAttributes/TimeGreaterThanAttribute.cs +++ b/01-Core/Jinget.Core/Attributes/ValidationAttributes/TimeGreaterThanAttribute.cs @@ -5,7 +5,7 @@ public class TimeGreaterThanAttribute(string otherPropertyName) : ValidationAttr /// /// check whether is greater than /// - protected override ValidationResult IsValid(object value, ValidationContext validationContext) + protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) { if (value == null) return ValidationResult.Success; // Allow nulls (optional, adjust as needed) diff --git a/01-Core/Jinget.Core/CodeDom/JingetDynamicCode.cs b/01-Core/Jinget.Core/CodeDom/JingetDynamicCode.cs index 3962384..80b2c02 100644 --- a/01-Core/Jinget.Core/CodeDom/JingetDynamicCode.cs +++ b/01-Core/Jinget.Core/CodeDom/JingetDynamicCode.cs @@ -179,7 +179,10 @@ public class MethodOptions /// What should be return type of the dynamically injected Invoke method? /// If not set, void will be used /// - public Type ReturnType { get; set; } + /// + /// Default value: typeof(void) + /// + public Type ReturnType { get; set; } = typeof(void); /// /// What are the input parameters for dynamically injected Invoke method? @@ -188,9 +191,9 @@ public class MethodOptions public class ParameterOptions { - public Type Type { get; set; } - public string Name { get; set; } - public object Value { get; set; } + public Type? Type { get; set; } + public string? Name { get; set; } + public object? Value { get; set; } } } } \ No newline at end of file diff --git a/01-Core/Jinget.Core/Compiler/CompilerFeatureRequiredAttribute.cs b/01-Core/Jinget.Core/Compiler/CompilerFeatureRequiredAttribute.cs index 5cee541..fd4df06 100644 --- a/01-Core/Jinget.Core/Compiler/CompilerFeatureRequiredAttribute.cs +++ b/01-Core/Jinget.Core/Compiler/CompilerFeatureRequiredAttribute.cs @@ -3,6 +3,8 @@ /// /// add support for 'required' keyword using latest lang version and .net standard 2.1 /// +#pragma warning disable CS9113 // Parameter is unread. public class CompilerFeatureRequiredAttribute(string name) : Attribute +#pragma warning restore CS9113 // Parameter is unread. { } \ No newline at end of file diff --git a/01-Core/Jinget.Core/ExpressionToSql/Internal/OrderBy.cs b/01-Core/Jinget.Core/ExpressionToSql/Internal/OrderBy.cs index aecaad2..66dd65e 100644 --- a/01-Core/Jinget.Core/ExpressionToSql/Internal/OrderBy.cs +++ b/01-Core/Jinget.Core/ExpressionToSql/Internal/OrderBy.cs @@ -13,7 +13,7 @@ public class OrderBy /// /// The order by expression /// - public virtual Expression> Name { get; set; } + public virtual Expression>? Name { get; set; } public OrderBy() { } public OrderBy(string name) => Name = ExpressionUtility.ToExpression(name, "x"); @@ -29,12 +29,14 @@ public OrderBy() { } /// public override string ToString() { + if (Name == null) + return ""; StringBuilder orderByClause = new(); orderByClause.Append('['); - + if (Name.Body is MemberExpression expression && - expression.Expression.NodeType != ExpressionType.Convert && - expression.Expression.NodeType != ExpressionType.Parameter) + expression.Expression?.NodeType != ExpressionType.Convert && + expression.Expression?.NodeType != ExpressionType.Parameter) orderByClause.Append(Expression.Lambda(expression).Compile().DynamicInvoke()); else orderByClause.Append(Name.Stringfy()); diff --git a/01-Core/Jinget.Core/ExpressionToSql/Internal/Where.cs b/01-Core/Jinget.Core/ExpressionToSql/Internal/Where.cs index 74fa0ef..4268e38 100644 --- a/01-Core/Jinget.Core/ExpressionToSql/Internal/Where.cs +++ b/01-Core/Jinget.Core/ExpressionToSql/Internal/Where.cs @@ -59,7 +59,7 @@ private static WherePart Recurse(ref int i, Expression expression, bool isUnary return WherePart.Concat(Where.Recurse(ref i, member), "=", WherePart.IsParameter(i++, true)); } - if (((MemberExpression)expression).Expression.NodeType == ExpressionType.Constant) + if (((MemberExpression)expression).Expression?.NodeType == ExpressionType.Constant) { goto case_ConstantExpression; } @@ -260,6 +260,9 @@ public static WherePart IsCollection(ref int countStart, IEnumerable values) public static WherePart Concat(WherePart left, string @operator, WherePart right) { + if (right.Sql == null) + throw new Exception("Jinget Says: Right-side expression can not be null here"); + //these operators does not need to append @ List excludedList = ["IN", "AND", "OR"]; var rightExpr = !excludedList.Contains(@operator) && !right.Sql.StartsWith("@") diff --git a/01-Core/Jinget.Core/ExtensionMethods/Dapper/DynamicParametersExtensions.cs b/01-Core/Jinget.Core/ExtensionMethods/Dapper/DynamicParametersExtensions.cs index 02b9b21..9ca8316 100644 --- a/01-Core/Jinget.Core/ExtensionMethods/Dapper/DynamicParametersExtensions.cs +++ b/01-Core/Jinget.Core/ExtensionMethods/Dapper/DynamicParametersExtensions.cs @@ -14,23 +14,39 @@ public static class DynamicParametersExtensions /// public static List GetSQLValues(this DynamicParameters parameters) { - List lstValues = []; - var t = parameters.GetType().GetField("parameters", BindingFlags.NonPublic | BindingFlags.Instance); + List lstValues = []; + var parametersField = parameters.GetType().GetField("parameters", BindingFlags.NonPublic | BindingFlags.Instance); - if (t == null) + if (parametersField == null) return lstValues; - foreach (DictionaryEntry dictionaryEntry in (IDictionary)t.GetValue(parameters)) + if (parametersField.GetValue(parameters) is IDictionary parameterDictionary) { - var dbType = (DbType)dictionaryEntry.Value.GetValue("DbType"); - if (dbType.IsBooleanDbType()) - lstValues.Add(parameters.Get(dictionaryEntry.Key.ToString()) == true ? 1 : 0); - else if (dbType.IsNumericDbType()) - lstValues.Add(parameters.Get(dictionaryEntry.Key.ToString())); - else if (dbType.IsUnicodeDbType()) - lstValues.Add("N'" + parameters.Get(dictionaryEntry.Key.ToString()) + "'"); - else - lstValues.Add("'" + parameters.Get(dictionaryEntry.Key.ToString()) + "'"); + foreach (DictionaryEntry dictionaryEntry in parameterDictionary) + { + var parameterValue = dictionaryEntry.Value; + if (parameterValue != null) + { + var dbTypeValue = parameterValue.GetType().GetProperty("DbType")?.GetValue(parameterValue); + if (dbTypeValue is DbType dbType) + { + var parameterKeyValue = dictionaryEntry.Key.ToString(); + if (parameterKeyValue != null) + { + var parameterValueFromParameters = parameters.Get(parameterKeyValue); + + if (dbType.IsBooleanDbType()) + lstValues.Add(parameterValueFromParameters == true ? 1 : 0); + else if (dbType.IsNumericDbType()) + lstValues.Add(parameterValueFromParameters); + else if (dbType.IsUnicodeDbType()) + lstValues.Add("N'" + parameterValueFromParameters + "'"); + else + lstValues.Add("'" + parameterValueFromParameters + "'"); + } + } + } + } } return lstValues; } diff --git a/01-Core/Jinget.Core/ExtensionMethods/Database/SqlClient/IDbConnectionExtensions.cs b/01-Core/Jinget.Core/ExtensionMethods/Database/SqlClient/IDbConnectionExtensions.cs index ad0d359..ec12438 100644 --- a/01-Core/Jinget.Core/ExtensionMethods/Database/SqlClient/IDbConnectionExtensions.cs +++ b/01-Core/Jinget.Core/ExtensionMethods/Database/SqlClient/IDbConnectionExtensions.cs @@ -34,18 +34,27 @@ internal static (string queryText, Dictionary? queryParameters) countQueryText = queryText[queryText.IndexOf(" FROM ", StringComparison.CurrentCultureIgnoreCase)..]; countQueryText = "Select Count(*) " + countQueryText; - string pagingStatement = ""; + string? pagingStatement = ""; if (orderBy == null) { var iOrderByInterface = param.GetType().GetInterface(typeof(IOrderBy<>).Name); - var orderByProperty = iOrderByInterface.GetProperty("OrderBy"); - var orderByValue = orderByProperty.GetValue(param); - pagingStatement = typeof(PagingExtensions).Call( - "GetPaging", - [typeof(Paging), orderByValue.GetType()], - [paging, orderByValue], - param.GetType() - ).ToString(); + if (iOrderByInterface != null) + { + var orderByProperty = iOrderByInterface.GetProperty("OrderBy"); + if (orderByProperty != null) + { + var orderByValue = orderByProperty.GetValue(param); + if (orderByValue != null) + { + pagingStatement = typeof(PagingExtensions).Call( + "GetPaging", + [typeof(Paging), orderByValue.GetType()], + [paging, orderByValue], + param.GetType() + )?.ToString(); + } + } + } } else { diff --git a/01-Core/Jinget.Core/ExtensionMethods/Expressions/BooleanExpressionExtensions.cs b/01-Core/Jinget.Core/ExtensionMethods/Expressions/BooleanExpressionExtensions.cs index 1b6214c..41bf7b7 100644 --- a/01-Core/Jinget.Core/ExtensionMethods/Expressions/BooleanExpressionExtensions.cs +++ b/01-Core/Jinget.Core/ExtensionMethods/Expressions/BooleanExpressionExtensions.cs @@ -55,7 +55,12 @@ static Expression> CreateBinaryExpression( if (expr2 is null) return expr1; var parameter = Expression.Parameter(typeof(T), parameterName); (Expression? LeftExpression, Expression? RightExpression) = Visit(expr1, expr2, parameter); + if (LeftExpression == null || RightExpression == null) + { + throw new ArgumentNullException("Visit method returned null expression"); + } BinaryExpression binaryExpr; + if (expressionType == ExpressionType.AndAlso) binaryExpr = Expression.AndAlso(LeftExpression, RightExpression); else if (expressionType == ExpressionType.OrElse) diff --git a/01-Core/Jinget.Core/ExtensionMethods/Generics/GenericTypeExtensions.cs b/01-Core/Jinget.Core/ExtensionMethods/Generics/GenericTypeExtensions.cs index 55e33fb..2f22af3 100644 --- a/01-Core/Jinget.Core/ExtensionMethods/Generics/GenericTypeExtensions.cs +++ b/01-Core/Jinget.Core/ExtensionMethods/Generics/GenericTypeExtensions.cs @@ -15,7 +15,9 @@ public static bool IsSubclassOfRawGeneric(this Type derivedType, Type parentType { return true; } +#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. derivedType = derivedType.BaseType; +#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. } return false; } diff --git a/01-Core/Jinget.Core/ExtensionMethods/HttpContextExtensions.cs b/01-Core/Jinget.Core/ExtensionMethods/HttpContextExtensions.cs index 9df1e18..fbd15c8 100644 --- a/01-Core/Jinget.Core/ExtensionMethods/HttpContextExtensions.cs +++ b/01-Core/Jinget.Core/ExtensionMethods/HttpContextExtensions.cs @@ -1,22 +1,94 @@ -namespace Jinget.Core.ExtensionMethods; +using System.Net; + +namespace Jinget.Core.ExtensionMethods; public static class HttpContextExtensions { /// - /// Check if given request is a multipart request or not + /// Determines whether the current HTTP request has a multipart content type (specifically "multipart/form-data"). /// - public static bool IsMultipartContentType(this HttpContext context) => - context.Request.GetTypedHeaders().ContentType != null && - context.Request.GetTypedHeaders().ContentType.MediaType.Value.ToLower().StartsWith("multipart/form-data"); + /// The HttpContext representing the current HTTP request. + /// True if the content type is multipart/form-data; otherwise, false. + /// + /// This method checks the Content-Type header of the request to determine if it starts with "multipart/form-data". + /// It uses the GetTypedHeaders() extension method for robust header parsing and performs a case-insensitive comparison. + /// + public static bool IsMultipartContentType(this HttpContext context) + { + // Retrieve the parsed Content-Type header from the request. + var contentTypeHeader = context.Request.GetTypedHeaders().ContentType; + + // Check if the Content-Type header exists. + if (contentTypeHeader != null) + { + // Retrieve the media type value from the parsed header. + var mediaType = contentTypeHeader.MediaType.Value; + + // Check if the media type value exists and starts with "multipart/form-data" (case-insensitive). + if (mediaType != null && mediaType.StartsWith("multipart/form-data", StringComparison.CurrentCultureIgnoreCase)) + { + return true; // It's a multipart/form-data request. + } + } + + return false; // It's not a multipart/form-data request. + } /// - /// Get request connecton ip address + /// Retrieves the client's IP address from the HttpContext. /// - public static string GetIpAddress(this HttpContext context) => - context.Connection.RemoteIpAddress == null - ? "Unknown" - : context.Connection.RemoteIpAddress.ToString(); + /// The HttpContext from which to retrieve the IP address. + /// + /// The header name containing the client's IP address (e.g., "X-Forwarded-For"). + /// Defaults to "X-Forwarded-For". + /// + /// + /// The client's IP address as a string, or "Unknown" if the IP address cannot be determined. + /// + /// + /// This method first attempts to retrieve the IP address from the specified custom header. + /// If the header is not found or the IP address in the header is invalid, it falls back to the + /// RemoteIpAddress property of the HttpContext.Connection. + /// The RemoteIpAddress property represents the IP address of the immediate connection to the server. + /// In environments with reverse proxies or load balancers, the RemoteIpAddress may not represent + /// the actual client's IP address. + /// The method also validates the IP address retrieved from the custom header using IPAddress.TryParse. + /// + public static string GetClientIpAddress(this HttpContext context, string customClientIpHeader = "X-Forwarded-For") + { + // Get the IP address of the immediate connection to the server. + // This may be the proxy or load balancer's IP, not the client's. + var ipAddress = context.Connection.RemoteIpAddress?.ToString(); + + // Check if the specified custom header contains the client's IP address. + if (context.Request.Headers.TryGetValue(customClientIpHeader, out var clientIpAddress)) + { + // Get the first IP address from the header (in case of multiple IPs). + var firstIp = clientIpAddress.ToString().Split(',')[0].Trim(); - public static bool EndpointIsAuthorized(this HttpContext httpContext) + // Validate the IP address format. + if (IPAddress.TryParse(firstIp, out _)) + { + // If valid, use the IP address from the header. + ipAddress = firstIp; + } + } + + // Return the retrieved IP address, or "Unknown" if not found. + return ipAddress ?? "Unknown"; + } + + /// + /// Checks if the endpoint associated with the current HTTP request has the [Authorize] attribute. + /// + /// The HttpContext for the current request. + /// True if the endpoint has the [Authorize] attribute; otherwise, false. + /// + /// This method retrieves the endpoint from the HttpContext and checks for the presence of the AuthorizeAttribute. + /// It returns true if the attribute is found; otherwise, it returns false. + /// This method does not evaluate authorization policies. It only checks for the existence of the attribute. + /// + public static bool EndpointIsDecoratedWithAuthorizeAttribute(this HttpContext httpContext) => httpContext.GetEndpoint()?.Metadata.GetMetadata() != null; + } \ No newline at end of file diff --git a/01-Core/Jinget.Core/ExtensionMethods/ObjectExtensions.cs b/01-Core/Jinget.Core/ExtensionMethods/ObjectExtensions.cs index 0a538d3..2751240 100644 --- a/01-Core/Jinget.Core/ExtensionMethods/ObjectExtensions.cs +++ b/01-Core/Jinget.Core/ExtensionMethods/ObjectExtensions.cs @@ -217,7 +217,7 @@ public static bool HasDefaultValue(this object value) if (Nullable.GetUnderlyingType(type) != null) return false; - object defaultValue = Activator.CreateInstance(type); + object? defaultValue = Activator.CreateInstance(type); return value.Equals(defaultValue); } } \ No newline at end of file diff --git a/01-Core/Jinget.Core/ExtensionMethods/Reflection/AssemblyExtensions.cs b/01-Core/Jinget.Core/ExtensionMethods/Reflection/AssemblyExtensions.cs index 103e926..5ecddae 100644 --- a/01-Core/Jinget.Core/ExtensionMethods/Reflection/AssemblyExtensions.cs +++ b/01-Core/Jinget.Core/ExtensionMethods/Reflection/AssemblyExtensions.cs @@ -4,12 +4,12 @@ public static class AssemblyExtensions { public class AssemblyInfo { - public string Summary { get; set; } = ""; - public string TypeName { get; set; } = ""; - public string AssemblyName { get; set; } = ""; - public string MethodName { get; set; } = ""; - public string Claim { get; set; } = ""; - public string ParentTitle { get; set; } = ""; + public string? Summary { get; set; } = ""; + public string? TypeName { get; set; } = ""; + public string? AssemblyName { get; set; } = ""; + public string? MethodName { get; set; } = ""; + public string? Claim { get; set; } = ""; + public string? ParentTitle { get; set; } = ""; } /// @@ -25,7 +25,7 @@ public static List GetTypes(this Assembly assembly, Type resourceT .Where(resourceType.IsAssignableFrom) .Select(x => new AssemblyInfo { - Summary = ((SummaryAttribute)x.GetCustomAttributes().FirstOrDefault(a => a.GetType() == methodSummaryAttribute))?.Description.Trim(), + Summary = (x.GetCustomAttributes().FirstOrDefault(a => a.GetType() == methodSummaryAttribute) as SummaryAttribute)?.Description.Trim(), TypeName = Regex.Replace(x.Name, normalizingPattern, string.Empty, RegexOptions.IgnoreCase), //x.Name.Replace("Controller", string.Empty).Trim(), AssemblyName = assembly.GetName().Name }).ToList(); @@ -49,26 +49,28 @@ public static List GetMethods(this Assembly executingAssembly, Typ type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public)) .Where(m => !m.GetCustomAttributes(typeof(CompilerGeneratedAttribute), true).Any()); +#pragma warning disable CS8604 // Possible null reference argument. var authorizedMethods = allMethods.Where(m => !onlyAuthorizedMethods || (//methods marked as Authorize m.GetCustomAttributes().Any(x => x.GetType() == typeof(AuthorizeAttribute)) || //class marked as Authorize m.DeclaringType.GetCustomAttributes().Any(x => x.GetType() == typeof(AuthorizeAttribute)))); +#pragma warning restore CS8604 // Possible null reference argument. return [ .. authorizedMethods.Select(x => new AssemblyInfo { - Summary = ((SummaryAttribute)x.GetCustomAttributes() - .FirstOrDefault(a => a.GetType() == methodSummaryAttribute))?.Description.Trim(), + Summary = (x.GetCustomAttributes() + .FirstOrDefault(a => a.GetType() == methodSummaryAttribute) as SummaryAttribute)?.Description.Trim(), TypeName = x.DeclaringType?.Name.Replace("Controller", string.Empty).Trim(), AssemblyName = executingAssembly.GetName().Name, MethodName = x.Name.Trim(), - Claim = ((ClaimAttribute)x.GetCustomAttributes() - .FirstOrDefault(a => a.GetType() == typeof(ClaimAttribute)))?.Title, - ParentTitle = ((SummaryAttribute)x.DeclaringType?.GetCustomAttributes() - .FirstOrDefault(a => a.GetType() == methodSummaryAttribute))?.Description + Claim = (x.GetCustomAttributes() + .FirstOrDefault(a => a.GetType() == typeof(ClaimAttribute)) as ClaimAttribute)?.Title, + ParentTitle = (x.DeclaringType?.GetCustomAttributes() + .FirstOrDefault(a => a.GetType() == methodSummaryAttribute) as SummaryAttribute)?.Description }) .OrderBy(x => x.Summary).ThenBy(x => x.MethodName).ThenBy(x => x.Claim), ]; diff --git a/01-Core/Jinget.Core/ExtensionMethods/Reflection/MethodInfoExtensions.cs b/01-Core/Jinget.Core/ExtensionMethods/Reflection/MethodInfoExtensions.cs index 6a6016e..79bcc51 100644 --- a/01-Core/Jinget.Core/ExtensionMethods/Reflection/MethodInfoExtensions.cs +++ b/01-Core/Jinget.Core/ExtensionMethods/Reflection/MethodInfoExtensions.cs @@ -5,16 +5,18 @@ public static class MethodInfoExtensions /// /// Dynamically invoke async method /// - public static object InvokeAsync(this MethodInfo method, object obj, params object[] parameters) +#pragma warning disable VSTHRD200 // Use "Async" suffix for async methods + public static object? InvokeAsync(this MethodInfo method, object obj, params object[] parameters) +#pragma warning restore VSTHRD200 // Use "Async" suffix for async methods { using var taskContext = new JoinableTaskContext(); var joinableTaskFactory = new JoinableTaskFactory(taskContext); return joinableTaskFactory.Run(async () => { - dynamic awaitable = method.Invoke(obj, parameters); + dynamic? awaitable = method.Invoke(obj, parameters); await awaitable; - return awaitable.GetAwaiter().GetResult(); + return awaitable?.GetAwaiter().GetResult(); }); } } diff --git a/01-Core/Jinget.Core/ExtensionMethods/Reflection/PropertiesExtensions.cs b/01-Core/Jinget.Core/ExtensionMethods/Reflection/PropertiesExtensions.cs index e448865..0ac4f6f 100644 --- a/01-Core/Jinget.Core/ExtensionMethods/Reflection/PropertiesExtensions.cs +++ b/01-Core/Jinget.Core/ExtensionMethods/Reflection/PropertiesExtensions.cs @@ -26,7 +26,7 @@ private static IEnumerable GetDeclaredProperties(Type t) => t.GetTypeInfo().DeclaredProperties; #endif - private static bool IsSimpleType(Type t) + private static bool IsSimpleType(Type? t) { while (true) { @@ -50,7 +50,7 @@ private static bool IsSimpleType(Type t) { return true; } - if (t.BaseType == typeof(Enum)) + if (t != null && t.BaseType == typeof(Enum)) { return true; } @@ -58,7 +58,10 @@ private static bool IsSimpleType(Type t) { return true; } - t = Nullable.GetUnderlyingType(t); + if (t != null) + { + t = Nullable.GetUnderlyingType(t); + } if (t is null) { break; @@ -67,11 +70,14 @@ private static bool IsSimpleType(Type t) return false; } - private static bool IsPrimitive(Type t) => + private static bool IsPrimitive(Type? t) + { + if (t == null) return false; #if NET45 return t.IsPrimitive; #else - t.GetTypeInfo().IsPrimitive; + return t.GetTypeInfo().IsPrimitive; #endif + } } \ No newline at end of file diff --git a/01-Core/Jinget.Core/ExtensionMethods/Reflection/TypeExtensions.cs b/01-Core/Jinget.Core/ExtensionMethods/Reflection/TypeExtensions.cs index d646fdb..64c86b7 100644 --- a/01-Core/Jinget.Core/ExtensionMethods/Reflection/TypeExtensions.cs +++ b/01-Core/Jinget.Core/ExtensionMethods/Reflection/TypeExtensions.cs @@ -45,7 +45,7 @@ public static bool IsAnonymousType(this Type type) /// Invoke the method and return the method's return value public static object? Call(this Type type, object? caller, string name, BindingFlags bindingFlags, Type[]? parameterTypes, object?[] parameterValues, params Type[] generics) { - MethodInfo method; + MethodInfo? method; if (generics != null) { if (parameterTypes == null) diff --git a/01-Core/Jinget.Core/Jinget.Core.csproj b/01-Core/Jinget.Core/Jinget.Core.csproj index 1436084..4a22c57 100644 --- a/01-Core/Jinget.Core/Jinget.Core.csproj +++ b/01-Core/Jinget.Core/Jinget.Core.csproj @@ -16,16 +16,16 @@ jinget, extension, xml, expression, utility, codedom - + - - + + - - - + + + diff --git a/01-Core/Jinget.Core/Utilities/Compression/ZipUtility.cs b/01-Core/Jinget.Core/Utilities/Compression/ZipUtility.cs index b581966..c16ef99 100644 --- a/01-Core/Jinget.Core/Utilities/Compression/ZipUtility.cs +++ b/01-Core/Jinget.Core/Utilities/Compression/ZipUtility.cs @@ -64,7 +64,7 @@ public static async Task CompressAsync( } // Split the encrypted file into chunks - await SplitZipFileAsync(finalResultFile, chunkSize, file.Directory.FullName); + await SplitZipFileAsync(finalResultFile, chunkSize, file.Directory?.FullName); } finally { @@ -112,8 +112,9 @@ static async Task EncryptFileAsync(string inputFile, string outputFile, string p } csEncrypt.FlushFinalBlock(); } - static async Task SplitZipFileAsync(string zipFile, int chunkSize, string destinationDirectory) + static async Task SplitZipFileAsync(string zipFile, int chunkSize, string? destinationDirectory) { + if (destinationDirectory == null) destinationDirectory = ""; FileInfo fileInfo = new(zipFile); string fileName = Path.GetFileNameWithoutExtension(zipFile); @@ -258,7 +259,9 @@ static async Task DecompressFileAsync(string compressedFile, string path) foreach (ZipArchiveEntry entry in archive.Entries) { string filePath = Path.Combine(path, entry.FullName); - Directory.CreateDirectory(Path.GetDirectoryName(filePath)); + string? directoryPath = Path.GetDirectoryName(filePath); + if (directoryPath != null) + Directory.CreateDirectory(directoryPath); using FileStream fileStream = File.Create(filePath); using Stream entryStream = entry.Open(); diff --git a/01-Core/Jinget.Core/Utilities/Enum/EnumUtility.cs b/01-Core/Jinget.Core/Utilities/Enum/EnumUtility.cs index 62b662c..ea249e8 100644 --- a/01-Core/Jinget.Core/Utilities/Enum/EnumUtility.cs +++ b/01-Core/Jinget.Core/Utilities/Enum/EnumUtility.cs @@ -79,7 +79,14 @@ public static TValue GetMinValue() where TValue : struct, IConver if (enumValues.Any()) { var minVal = enumValues.First(); - return (TValue)Convert.ChangeType(minVal, typeof(TValue)); + var convertedValue = Convert.ChangeType(minVal, typeof(TValue)); + + if (convertedValue is TValue result) // Null check and pattern match + { + return result; + } + + throw new InvalidCastException($"Cannot convert '{minVal}' to type '{typeof(TValue)}'."); } throw new InvalidEnumArgumentException("Enum is empty and contains no value"); @@ -96,7 +103,14 @@ public static TValue GetMaxValue() where TValue : struct, IConver if (enumValues.Any()) { var maxVal = enumValues.Last(); - return (TValue)Convert.ChangeType(maxVal, typeof(TValue)); + var convertedValue = Convert.ChangeType(maxVal, typeof(TValue)); + + if (convertedValue is TValue result) // Null check and pattern match + { + return result; + } + + throw new InvalidCastException($"Cannot convert '{maxVal}' to type '{typeof(TValue)}'."); } throw new InvalidEnumArgumentException("Enum is empty and contains no value"); diff --git a/01-Core/Jinget.Core/Utilities/Expressions/ExpressionUtility.cs b/01-Core/Jinget.Core/Utilities/Expressions/ExpressionUtility.cs index 23814dd..8227b3d 100644 --- a/01-Core/Jinget.Core/Utilities/Expressions/ExpressionUtility.cs +++ b/01-Core/Jinget.Core/Utilities/Expressions/ExpressionUtility.cs @@ -9,7 +9,7 @@ public static class ExpressionUtility /// internal static Expression Transform(Expression source, Type type) { - if (source.Type != type && source is NewExpression newExpr && newExpr.Members.Count > 0) + if (source.Type != type && source is NewExpression newExpr && newExpr.Members?.Count > 0) { #pragma warning disable CS8604 // Possible null reference argument. #pragma warning disable CS8602 // Dereference of a possibly null reference. diff --git a/01-Core/Jinget.Core/Utilities/IP/IPUtility.cs b/01-Core/Jinget.Core/Utilities/IP/IPUtility.cs index a52dcfa..608131a 100644 --- a/01-Core/Jinget.Core/Utilities/IP/IPUtility.cs +++ b/01-Core/Jinget.Core/Utilities/IP/IPUtility.cs @@ -33,7 +33,7 @@ public static bool IsIPInRange(string ipAddress, string cidrRange) return false; //Unknown IP format } - if (!IPAddress.TryParse(ipAddress, out IPAddress targetAddress)) return false; + if (!IPAddress.TryParse(ipAddress, out IPAddress? targetAddress)) return false; if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) //IPv4 { diff --git a/01-Core/Jinget.Core/Utilities/SOAPUtility.cs b/01-Core/Jinget.Core/Utilities/SOAPUtility.cs index 91c08bf..1b023d0 100644 --- a/01-Core/Jinget.Core/Utilities/SOAPUtility.cs +++ b/01-Core/Jinget.Core/Utilities/SOAPUtility.cs @@ -11,7 +11,7 @@ public abstract class SOAPRequestBase { public SOAPRequestBase() { } - public abstract (TEnvelope envelope, TRequest request) CreateEnvelope(); + public abstract (TEnvelope? envelope, TRequest? request) CreateEnvelope(); } public abstract class SOAPEnvelopeBase diff --git a/01-Core/Jinget.Core/Utilities/XmlUtility.cs b/01-Core/Jinget.Core/Utilities/XmlUtility.cs index 81102b0..a552c55 100644 --- a/01-Core/Jinget.Core/Utilities/XmlUtility.cs +++ b/01-Core/Jinget.Core/Utilities/XmlUtility.cs @@ -29,10 +29,12 @@ public static string SerializeToXml(object input, bool omitXmlDeclaration = fals public static string SerializeToSoapXml(object input) { using MemoryStream memStream = new(); +#pragma warning disable SYSLIB0050 // Type or member is obsolete SoapFormatter formatter = new() { AssemblyFormat = FormatterAssemblyStyle.Simple }; +#pragma warning restore SYSLIB0050 // Type or member is obsolete formatter.Serialize(memStream, input); return Encoding.UTF8.GetString(memStream.GetBuffer()); } diff --git a/02-Handlers/Jinget.Handlers.ExternalServiceHandlers/DefaultServiceHandler/JingetServiceHandler.cs b/02-Handlers/Jinget.Handlers.ExternalServiceHandlers/DefaultServiceHandler/JingetServiceHandler.cs index da320f4..0eca228 100644 --- a/02-Handlers/Jinget.Handlers.ExternalServiceHandlers/DefaultServiceHandler/JingetServiceHandler.cs +++ b/02-Handlers/Jinget.Handlers.ExternalServiceHandlers/DefaultServiceHandler/JingetServiceHandler.cs @@ -5,54 +5,67 @@ public JingetServiceHandler(string baseUri, bool ignoreSslErrors = false) : base(baseUri, ignoreSslErrors) { } public JingetServiceHandler(string baseUri, TimeSpan timeout, bool ignoreSslErrors = false) : base(baseUri, timeout, ignoreSslErrors) { } - private async Task ProcessTaskAsync(Func> task) + private async Task ProcessTaskAsync(Func> task) { - TResponseModel responseModel = null; + TResponseModel? responseModel = default; + try { var response = await task(); - Events.OnServiceCalled(response); + Events?.OnServiceCalled(response); + response.EnsureSuccessStatusCode(); string rawResponse = await response.Content.ReadAsStringAsync(); - Events.OnRawResponseReceived(rawResponse); + Events?.OnRawResponseReceived(rawResponse); - switch (response.Content.Headers.ContentType.MediaType) + if (response.Content.Headers.ContentType != null) + { + switch (response.Content.Headers.ContentType.MediaType) + { + case MediaTypeNames.Application.Json: + responseModel = JsonConvert.DeserializeObject(rawResponse); + break; + case MediaTypeNames.Application.Xml: + case MediaTypeNames.Text.Xml: + responseModel = DeserializeXmlDescendantsFirst(rawResponse); + break; + default: + // Handle unknown media type. + Events?.OnExceptionOccurred(new InvalidOperationException($"Unsupported media type: {response.Content.Headers.ContentType.MediaType}")); + break; + } + } + else { - case MediaTypeNames.Application.Json: - responseModel = JsonConvert.DeserializeObject(rawResponse); - break; - case MediaTypeNames.Application.Xml: - case MediaTypeNames.Text.Xml: - responseModel = DeserializeXmlDescendantsFirst(rawResponse); - break; - default: - break; + Events?.OnExceptionOccurred(new InvalidOperationException("content type was null")); } - Events.OnResponseDeserialized(responseModel); + Events?.OnResponseDeserialized(responseModel); // Null check } catch (Exception ex) { - Events.OnExceptionOccurred(ex); + Events?.OnExceptionOccurred(ex); // Null check + return default; // or throw the exception. } - return responseModel; + return responseModel; } - public async Task GetAsync(string url, Dictionary headers = null) + + public async Task GetAsync(string url, Dictionary? headers = null) => await ProcessTaskAsync(async () => await HttpClientFactory.GetAsync(url, headers)); - public async Task PostAsync(object content = null, Dictionary headers = null) + public async Task PostAsync(object? content = null, Dictionary? headers = null) => await ProcessTaskAsync(async () => await HttpClientFactory.PostAsync("", content, headers)); - public async Task PostAsync(string url, object content = null, Dictionary headers = null) + public async Task PostAsync(string url, object? content = null, Dictionary? headers = null) => await ProcessTaskAsync(async () => await HttpClientFactory.PostAsync(url, content, headers)); - public async Task UploadFileAsync(string url, List files = null, Dictionary headers = null) + public async Task UploadFileAsync(string url, List? files = null, Dictionary? headers = null) => await ProcessTaskAsync(async () => await HttpClientFactory.UploadFileAsync(url, files, headers)); - public async Task UploadFileAsync(string url, MultipartFormDataContent multipartFormData = null, Dictionary headers = null) + public async Task UploadFileAsync(string url, MultipartFormDataContent? multipartFormData = null, Dictionary? headers = null) => await ProcessTaskAsync(async () => await HttpClientFactory.UploadFileAsync(url, multipartFormData, headers)); - public async Task SendAsync(HttpRequestMessage message) => await ProcessTaskAsync(async () => await HttpClientFactory.SendAsync(message)); + public async Task SendAsync(HttpRequestMessage message) => await ProcessTaskAsync(async () => await HttpClientFactory.SendAsync(message)); } diff --git a/02-Handlers/Jinget.Handlers.ExternalServiceHandlers/DefaultServiceHandler/JingetServiceHandlerEvents.cs b/02-Handlers/Jinget.Handlers.ExternalServiceHandlers/DefaultServiceHandler/JingetServiceHandlerEvents.cs index 3f36787..6959a39 100644 --- a/02-Handlers/Jinget.Handlers.ExternalServiceHandlers/DefaultServiceHandler/JingetServiceHandlerEvents.cs +++ b/02-Handlers/Jinget.Handlers.ExternalServiceHandlers/DefaultServiceHandler/JingetServiceHandlerEvents.cs @@ -2,14 +2,14 @@ public class JingetServiceHandlerEvents where TResponseModel : class, new() { - public event EventHandler ServiceCalled; - public event EventHandler RawResponseReceived; - public event EventHandler ResponseDeserialized; - public event EventHandler ExceptionOccurred; + public event EventHandler? ServiceCalled; + public event EventHandler? RawResponseReceived; + public event EventHandler? ResponseDeserialized; + public event EventHandler? ExceptionOccurred; public virtual void OnServiceCalled(HttpResponseMessage e) => ServiceCalled?.Invoke(this, e); public virtual void OnRawResponseReceived(string e) => RawResponseReceived?.Invoke(this, e); - public virtual void OnResponseDeserialized(TResponseModel e) => ResponseDeserialized?.Invoke(this, e); + public virtual void OnResponseDeserialized(TResponseModel? e) => ResponseDeserialized?.Invoke(this, e); public virtual void OnExceptionOccurred(Exception e) => ExceptionOccurred?.Invoke(this, e); } diff --git a/02-Handlers/Jinget.Handlers.ExternalServiceHandlers/ServiceHandler/Factory/HttpClientFactory.cs b/02-Handlers/Jinget.Handlers.ExternalServiceHandlers/ServiceHandler/Factory/HttpClientFactory.cs index cb6df05..d66a948 100644 --- a/02-Handlers/Jinget.Handlers.ExternalServiceHandlers/ServiceHandler/Factory/HttpClientFactory.cs +++ b/02-Handlers/Jinget.Handlers.ExternalServiceHandlers/ServiceHandler/Factory/HttpClientFactory.cs @@ -23,7 +23,7 @@ internal HttpClientFactory(string baseUri, bool ignoreSslErrors = false) internal HttpClientFactory(string baseUri, TimeSpan timeout, bool ignoreSslErrors = false) : this(baseUri, ignoreSslErrors) => client.Timeout = timeout; - private Uri GetUrl(string url) => new($"{client.BaseAddress.ToString().TrimEnd('/')}/{url}".TrimEnd('/')); + private Uri GetUrl(string url) => new($"{client.BaseAddress?.ToString().TrimEnd('/')}/{url}".TrimEnd('/')); #nullable enable private void SetHeaders(Dictionary? headers) diff --git a/02-Handlers/Jinget.Handlers.ExternalServiceHandlers/ServiceHandler/ServiceHandler.cs b/02-Handlers/Jinget.Handlers.ExternalServiceHandlers/ServiceHandler/ServiceHandler.cs index 4406975..229296a 100644 --- a/02-Handlers/Jinget.Handlers.ExternalServiceHandlers/ServiceHandler/ServiceHandler.cs +++ b/02-Handlers/Jinget.Handlers.ExternalServiceHandlers/ServiceHandler/ServiceHandler.cs @@ -6,7 +6,9 @@ protected HttpClientFactory HttpClientFactory { get; set; } +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. private ServiceHandler() => Events = new T(); +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. protected ServiceHandler(string baseUri, bool ignoreSslErrors = false) : this() => HttpClientFactory = new HttpClientFactory(baseUri, ignoreSslErrors); protected ServiceHandler(string baseUri, TimeSpan timeout, bool ignoreSslErrors = false) : this() => HttpClientFactory = new HttpClientFactory(baseUri, timeout, ignoreSslErrors); diff --git a/03-Infrastructure/Jinget.ExceptionHandler/Entities/BaseEntity.cs b/03-Infrastructure/Jinget.ExceptionHandler/Entities/BaseEntity.cs index 63e4486..2a2a8b4 100644 --- a/03-Infrastructure/Jinget.ExceptionHandler/Entities/BaseEntity.cs +++ b/03-Infrastructure/Jinget.ExceptionHandler/Entities/BaseEntity.cs @@ -4,7 +4,9 @@ namespace Jinget.ExceptionHandler.Entities; public abstract class BaseEntity { +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. protected BaseEntity() { } +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. protected BaseEntity(TKeyType id) : this() => Id = id; diff --git a/03-Infrastructure/Jinget.ExceptionHandler/Entities/BaseSettingModel.cs b/03-Infrastructure/Jinget.ExceptionHandler/Entities/BaseSettingModel.cs index 5f61347..df263be 100644 --- a/03-Infrastructure/Jinget.ExceptionHandler/Entities/BaseSettingModel.cs +++ b/03-Infrastructure/Jinget.ExceptionHandler/Entities/BaseSettingModel.cs @@ -11,4 +11,18 @@ public class BaseSettingModel /// if set to true then http request exception handler will be used which in turn will be handle the 4xx responses /// public bool Handle4xxResponses { get; set; } = false; + + const long MB_10 = 1024 * 1024 * 10; + + /// + /// maximum request body size to log. + /// request body larger than this value will be logged as `--REQUEST BODY TOO LARGE--` string + /// + public long MaxRequestBodySize { get; set; } = MB_10; + + /// + /// maximum response body size to log. + /// response body larger than this value will be logged as `--REQUEST BODY TOO LARGE--` string + /// + public long MaxResponseBodySize { get; set; } = MB_10; } diff --git a/03-Infrastructure/Jinget.ExceptionHandler/Entities/Log/LogModel.cs b/03-Infrastructure/Jinget.ExceptionHandler/Entities/Log/LogModel.cs index c00f6f3..b839d03 100644 --- a/03-Infrastructure/Jinget.ExceptionHandler/Entities/Log/LogModel.cs +++ b/03-Infrastructure/Jinget.ExceptionHandler/Entities/Log/LogModel.cs @@ -34,7 +34,7 @@ internal static LogModel GetNew(HttpContext? context = null) log.Username = context.User.Identity?.Name; log.Method = context.Request.Method; log.Url = context.Request.GetDisplayUrl(); - log.IP = context.GetIpAddress(); + log.IP = context.GetClientIpAddress(); var requestDateTime = context.GetRequestDateTime(); log.ElapsedMilliseconds = requestDateTime == null ? 0 : (DateTime.Now - requestDateTime.Value).TotalMilliseconds; @@ -83,9 +83,9 @@ public static LogModel GetNewResponseObject(HttpContext context, string body, st } public DateTime TimeStamp { get; set; } - public string Url { get; set; } - public string Description { get; set; } - public string EnvironmentInfo { get; set; } + public string? Url { get; set; } + public string? Description { get; set; } + public string? EnvironmentInfo { get; set; } /// /// How many milliseconds have passed since the start of the request? @@ -95,51 +95,51 @@ public static LogModel GetNewResponseObject(HttpContext context, string body, st /// /// /// - public string SubSystem { get; set; } + public string? SubSystem { get; set; } /// /// This is used when the CreateIndexPerPartition is set to true. /// This property is specific to Elasticsearch logging /// - public string ParitionKey { get; set; } + public string? ParitionKey { get; set; } /// /// unique identifier for a request and response. value is read from HttpContext.TraceIdentifier /// - public string TraceIdentifier { get; set; } + public string? TraceIdentifier { get; set; } /// /// Http Method /// - public string Method { get; set; } + public string? Method { get; set; } /// /// Request or response body /// - public string Body { get; set; } + public string? Body { get; set; } /// /// Request/Response headers. Blacklist headers will not be logged /// - public string Headers { get; set; } + public string? Headers { get; set; } /// /// Request ip /// - public string IP { get; set; } + public string? IP { get; set; } /// /// Is the record for request or response? /// public LogType Type { get; set; } - public string TypeDescription => Type.GetDescription(); + public string? TypeDescription => Type.GetDescription(); /// /// Page url initiating the request. /// Read page url from 'Referer' header /// - public string PageUrl { get; set; } + public string? PageUrl { get; set; } /// /// total length of the request or response @@ -149,22 +149,22 @@ public static LogModel GetNewResponseObject(HttpContext context, string body, st /// /// Request or response 'AdditionalData' header data plus HttpContext.Items['AdditionalData'] value /// - public string AdditionalData { get; set; } + public string? AdditionalData { get; set; } /// /// Request username /// - public string Username { get; set; } + public string? Username { get; set; } /// /// /// - public string Severity { get; set; } = LogLevel.Information.ToString(); + public string? Severity { get; set; } = LogLevel.Information.ToString(); /// /// This property only filled whenever calling method. /// - public string CallerFilePath { get; set; } = null; + public string? CallerFilePath { get; set; } = null; /// /// This property only filled whenever calling method. @@ -174,5 +174,5 @@ public static LogModel GetNewResponseObject(HttpContext context, string body, st /// /// This property only filled whenever calling method. /// - public string CallerMember { get; set; } = null; + public string? CallerMember { get; set; } = null; } \ No newline at end of file diff --git a/03-Infrastructure/Jinget.ExceptionHandler/Extensions/ApplicationBuilderExtensions.cs b/03-Infrastructure/Jinget.ExceptionHandler/Extensions/ApplicationBuilderExtensions.cs index 90a031a..827a9bf 100644 --- a/03-Infrastructure/Jinget.ExceptionHandler/Extensions/ApplicationBuilderExtensions.cs +++ b/03-Infrastructure/Jinget.ExceptionHandler/Extensions/ApplicationBuilderExtensions.cs @@ -14,9 +14,9 @@ public static IApplicationBuilder UseJingetExceptionHandler(this IApplicationBui { var baseSetting = ctx.RequestServices.GetJingetService(); await next(); - if (baseSetting.Handle4xxResponses) + if (baseSetting != null && baseSetting.Handle4xxResponses) { - if (ctx.Response.StatusCode is >= 400 and < 500) + if (ctx.Response.StatusCode is >= 400 and < 500 && ctx.Response.StatusCode != 429) { throw new HttpRequestException(ReasonPhrases.GetReasonPhrase(ctx.Response.StatusCode), null, (System.Net.HttpStatusCode?)ctx.Response.StatusCode); } diff --git a/03-Infrastructure/Jinget.ExceptionHandler/Extensions/HttpContextExtensions.cs b/03-Infrastructure/Jinget.ExceptionHandler/Extensions/HttpContextExtensions.cs index 1dcc165..8631115 100644 --- a/03-Infrastructure/Jinget.ExceptionHandler/Extensions/HttpContextExtensions.cs +++ b/03-Infrastructure/Jinget.ExceptionHandler/Extensions/HttpContextExtensions.cs @@ -35,7 +35,7 @@ public static void SetRequestDateTime(this HttpContext context, DateTime dateTim /// /// Get the partition key used for logging /// - public static string GetLoggerPartitionKey(this HttpContext context) => + public static string? GetLoggerPartitionKey(this HttpContext context) => context.Items["jinget.log.partitionkey"]?.ToString(); /// @@ -47,15 +47,26 @@ public static void SetLoggerPartitionKey(this HttpContext context, string key) = /// /// set additional data used for logging /// - public static string GetLoggerAdditionalData(this HttpContext context, bool isRequestData) + public static string? GetLoggerAdditionalData(this HttpContext context, bool isRequestData) { var rawHeaders = isRequestData ? context.Request.Headers : context.Response.Headers; var additionalData = new List(); - if (!string.IsNullOrWhiteSpace(rawHeaders["AdditionalData"].ToString())) - additionalData.Add(rawHeaders["AdditionalData"]); - if (!string.IsNullOrWhiteSpace(context.Items["AdditionalData"]?.ToString())) - additionalData.Add(context.Items["AdditionalData"]?.ToString()); + if (rawHeaders != null && rawHeaders.TryGetValue("AdditionalData", out StringValues headerValues) && !StringValues.IsNullOrEmpty(headerValues)) + { + foreach (var value in headerValues) + { + if (!string.IsNullOrEmpty(value)) + { + additionalData.Add(value); + } + } + } + + if (context.Items["AdditionalData"] is string itemValue && !string.IsNullOrEmpty(itemValue)) + { + additionalData.Add(itemValue); + } return additionalData.Any() ? JsonConvert.SerializeObject(additionalData) : null; } @@ -63,15 +74,15 @@ public static string GetLoggerAdditionalData(this HttpContext context, bool isRe /// /// get request/response headers used for logging /// - public static string GetLoggerHeaders(this HttpContext context, List blackListHeaders, List whiteListHeaders, bool isRequestHeader) + public static string GetLoggerHeaders(this HttpContext context, List? blackListHeaders, List? whiteListHeaders, bool isRequestHeader) { string headers; var rawHeaders = isRequestHeader ? context.Request.Headers : context.Response.Headers; - if (blackListHeaders.Any()) + if (blackListHeaders != null && blackListHeaders.Any()) headers = JsonConvert.SerializeObject(rawHeaders .Where(x => !blackListHeaders.Contains(x.Key.ToLower())) .Select(x => x.ToString()), Formatting.Indented); - else if (whiteListHeaders.Any()) + else if (whiteListHeaders != null && whiteListHeaders.Any()) headers = JsonConvert.SerializeObject(rawHeaders .Where(x => whiteListHeaders.Contains(x.Key.ToLower())) .Select(x => x.ToString()), Formatting.Indented); diff --git a/03-Infrastructure/Jinget.ExceptionHandler/Handlers/CoreExceptionHandler.cs b/03-Infrastructure/Jinget.ExceptionHandler/Handlers/CoreExceptionHandler.cs index 24d6164..cd08632 100644 --- a/03-Infrastructure/Jinget.ExceptionHandler/Handlers/CoreExceptionHandler.cs +++ b/03-Infrastructure/Jinget.ExceptionHandler/Handlers/CoreExceptionHandler.cs @@ -4,7 +4,7 @@ namespace Jinget.ExceptionHandler.Handlers; public abstract class CoreExceptionHandler(ILogger logger, IHostEnvironment env, bool useGlobalExceptionHandler) { - protected async ValueTask HandleAsync(HttpContext httpContext, Exception exception, int statusCode, CancellationToken cancellationToken) + protected async ValueTask HandleAsync(HttpContext httpContext, Exception exception, int? statusCode, CancellationToken cancellationToken) { var logEntity = LogModel.GetNewErrorObject(httpContext); logEntity.Description = JsonConvert.SerializeObject(new @@ -16,16 +16,20 @@ protected async ValueTask HandleAsync(HttpContext httpContext, Exception e logger.LogError(JsonConvert.SerializeObject(logEntity)); if (useGlobalExceptionHandler) { - httpContext.Response.StatusCode = statusCode; - var problemDetails = new ResponseResult(CreateProblemDetails(httpContext, exception, statusCode)); - await httpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken); + if (!httpContext.Response.HasStarted) + { + var httpStatusCode = statusCode ?? (exception == null ? 204 : 500); + httpContext.Response.StatusCode = httpStatusCode; + var problemDetails = new ResponseResult(CreateProblemDetails(httpContext, exception, httpStatusCode)); + await httpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken); + } return true; } else return false; } - protected virtual ProblemDetails CreateProblemDetails(in HttpContext context, in Exception exception, int statusCode) + protected virtual ProblemDetails CreateProblemDetails(in HttpContext context, in Exception? exception, int statusCode) { var reasonPhrase = ReasonPhrases.GetReasonPhrase(statusCode); if (string.IsNullOrEmpty(reasonPhrase)) @@ -38,18 +42,19 @@ protected virtual ProblemDetails CreateProblemDetails(in HttpContext context, in Status = statusCode, Title = reasonPhrase }; - problemDetails.Extensions.Add("message", exception.Message); + problemDetails.Extensions.Add("message", exception?.Message); + problemDetails.Extensions.Add("data", exception?.Data); + problemDetails.Extensions.Add("traceId", context.TraceIdentifier); + problemDetails.Extensions.Add("nodeId", Environment.MachineName); if (env.IsProduction()) { return problemDetails; } - - problemDetails.Detail = exception.ToString(); - problemDetails.Extensions.Add("traceId", context.TraceIdentifier); - problemDetails.Extensions.Add("data", exception.Data); - problemDetails.Extensions.Add("nodeId", Environment.MachineName); - - return problemDetails; + else + { + problemDetails.Detail = exception?.ToString(); + return problemDetails; + } } } \ No newline at end of file diff --git a/03-Infrastructure/Jinget.ExceptionHandler/Handlers/HttpRequestExceptionHandler.cs b/03-Infrastructure/Jinget.ExceptionHandler/Handlers/HttpRequestExceptionHandler.cs index 928da00..7c6ad11 100644 --- a/03-Infrastructure/Jinget.ExceptionHandler/Handlers/HttpRequestExceptionHandler.cs +++ b/03-Infrastructure/Jinget.ExceptionHandler/Handlers/HttpRequestExceptionHandler.cs @@ -4,10 +4,9 @@ public sealed class HttpRequestExceptionHandler (ILogger logger, IHostEnvironment env, BaseSettingModel baseSetting) : CoreExceptionHandler(logger, env, baseSetting.UseGlobalExceptionHandler), IExceptionHandler { public async ValueTask TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) - => exception is HttpRequestException httpRequestException - ? await HandleAsync(httpContext, httpRequestException, (int)httpRequestException.StatusCode, cancellationToken) - : false; - protected override ProblemDetails CreateProblemDetails(in HttpContext context, in Exception exception, int statusCode) + => exception is HttpRequestException httpRequestException && + await HandleAsync(httpContext, httpRequestException, (int?)httpRequestException.StatusCode, cancellationToken); + protected override ProblemDetails CreateProblemDetails(in HttpContext context, in Exception? exception, int statusCode) { var problemDetails = base.CreateProblemDetails(context, exception, statusCode); problemDetails.Extensions.Add("url", context.Request.GetDisplayUrl()); diff --git a/03-Infrastructure/Jinget.Logger/Configuration/ElasticSearch/ElasticSearchConfiguration.cs b/03-Infrastructure/Jinget.Logger/Configuration/ElasticSearch/ElasticSearchConfiguration.cs index 6627d4f..f992972 100644 --- a/03-Infrastructure/Jinget.Logger/Configuration/ElasticSearch/ElasticSearchConfiguration.cs +++ b/03-Infrastructure/Jinget.Logger/Configuration/ElasticSearch/ElasticSearchConfiguration.cs @@ -5,7 +5,7 @@ namespace Jinget.Logger.Configuration.ElasticSearch; public static class ElasticSearchConfiguration { public static void ConfigureElasticSearchLogger(this IServiceCollection services, - ElasticSearchSettingModel elasticSearchSetting = null) + ElasticSearchSettingModel? elasticSearchSetting = null) { services.ConfigureJingetLoggerPrerequisites(elasticSearchSetting); services.TryAddScoped(); diff --git a/03-Infrastructure/Jinget.Logger/Configuration/ElasticSearch/ElasticSearchSettingModel.cs b/03-Infrastructure/Jinget.Logger/Configuration/ElasticSearch/ElasticSearchSettingModel.cs index d9f9cdd..4f90962 100644 --- a/03-Infrastructure/Jinget.Logger/Configuration/ElasticSearch/ElasticSearchSettingModel.cs +++ b/03-Infrastructure/Jinget.Logger/Configuration/ElasticSearch/ElasticSearchSettingModel.cs @@ -10,12 +10,12 @@ public class ElasticSearchSettingModel : BaseSettingModel /// /// username, if basic authentication enabled on Elasticsearch search service /// - public string UserName { get; set; } + public string? UserName { get; set; } /// /// password, if basic authentication enabled on Elasticsearch search service /// - public string Password { get; set; } + public string? Password { get; set; } /// /// Set whether to use SSL while connecting to Elasticsearch or not diff --git a/03-Infrastructure/Jinget.Logger/Extensions/IApplicationBuilderExtensions.cs b/03-Infrastructure/Jinget.Logger/Extensions/IApplicationBuilderExtensions.cs index 7a5e805..0c69db6 100644 --- a/03-Infrastructure/Jinget.Logger/Extensions/IApplicationBuilderExtensions.cs +++ b/03-Infrastructure/Jinget.Logger/Extensions/IApplicationBuilderExtensions.cs @@ -14,8 +14,10 @@ public static IApplicationBuilder UseJingetLogging(this IApplicationBuilder app) if (settings != null && settings.UseGlobalExceptionHandler) app.UseJingetExceptionHandler(); - app.UseMiddleware(); - app.UseMiddleware(); + + app.UseMiddleware(); + //app.UseMiddleware(); + //app.UseMiddleware(); return app; } diff --git a/03-Infrastructure/Jinget.Logger/Extensions/IServiceCollectionExtensions.cs b/03-Infrastructure/Jinget.Logger/Extensions/IServiceCollectionExtensions.cs index b2a9a21..5184819 100644 --- a/03-Infrastructure/Jinget.Logger/Extensions/IServiceCollectionExtensions.cs +++ b/03-Infrastructure/Jinget.Logger/Extensions/IServiceCollectionExtensions.cs @@ -4,9 +4,10 @@ namespace Jinget.Logger.Extensions; public static class IServiceCollectionExtensions { - public static void ConfigureJingetLoggerPrerequisites(this IServiceCollection services, BaseSettingModel baseSetting) + public static void ConfigureJingetLoggerPrerequisites(this IServiceCollection services, BaseSettingModel? baseSetting) { services.TryAddSingleton(); - services.ConfigureJingetExceptionHandler(baseSetting); + if (baseSetting != null) + services.ConfigureJingetExceptionHandler(baseSetting); } } \ No newline at end of file diff --git a/03-Infrastructure/Jinget.Logger/GlobalUsings.cs b/03-Infrastructure/Jinget.Logger/GlobalUsings.cs index 10d83ef..55fa76d 100644 --- a/03-Infrastructure/Jinget.Logger/GlobalUsings.cs +++ b/03-Infrastructure/Jinget.Logger/GlobalUsings.cs @@ -27,5 +27,4 @@ global using Jinget.Logger.Providers.FileProvider; global using Microsoft.Extensions.Hosting; global using Jinget.ExceptionHandler.Entities; -global using Jinget.ExceptionHandler.Entities.Log; -global using System.Collections.Generic; \ No newline at end of file +global using Jinget.ExceptionHandler.Entities.Log; \ No newline at end of file diff --git a/03-Infrastructure/Jinget.Logger/Handlers/CommandHandlers/ElasticSearchLoggingDomainService.cs b/03-Infrastructure/Jinget.Logger/Handlers/CommandHandlers/ElasticSearchLoggingDomainService.cs index 746efd7..fa521b7 100644 --- a/03-Infrastructure/Jinget.Logger/Handlers/CommandHandlers/ElasticSearchLoggingDomainService.cs +++ b/03-Infrastructure/Jinget.Logger/Handlers/CommandHandlers/ElasticSearchLoggingDomainService.cs @@ -1,14 +1,12 @@ namespace Jinget.Logger.Handlers.CommandHandlers; -public class ElasticSearchLoggingDomainService : IElasticSearchLoggingDomainService +public class ElasticSearchLoggingDomainService(IElasticSearchLoggingRepository repository) : IElasticSearchLoggingDomainService { - protected readonly IElasticSearchLoggingRepository Repository; - - public ElasticSearchLoggingDomainService(IElasticSearchLoggingRepository repository) => Repository = repository; + protected readonly IElasticSearchLoggingRepository Repository = repository; public virtual async Task CreateAsync(LogModel param) => await Repository.IndexAsync(param); public virtual async Task BulkCreateAsync(IList @params) => await Repository.BulkIndexAsync(@params); - public virtual async Task FetchLatestAsync() => await Repository.GetLatestAsync(); + public virtual async Task FetchLatestAsync() => await Repository.GetLatestAsync(); public virtual async Task> SearchAsync( string partitionKey, string searchString, diff --git a/03-Infrastructure/Jinget.Logger/Handlers/CommandHandlers/IElasticSearchLoggingDomainService.cs b/03-Infrastructure/Jinget.Logger/Handlers/CommandHandlers/IElasticSearchLoggingDomainService.cs index 3406988..7961791 100644 --- a/03-Infrastructure/Jinget.Logger/Handlers/CommandHandlers/IElasticSearchLoggingDomainService.cs +++ b/03-Infrastructure/Jinget.Logger/Handlers/CommandHandlers/IElasticSearchLoggingDomainService.cs @@ -2,7 +2,7 @@ public interface IElasticSearchLoggingDomainService { - Task FetchLatestAsync(); + Task FetchLatestAsync(); Task CreateAsync(LogModel param); diff --git a/03-Infrastructure/Jinget.Logger/Handlers/ElasticSearchLoggingRepository.cs b/03-Infrastructure/Jinget.Logger/Handlers/ElasticSearchLoggingRepository.cs index 3c70a5f..3d9e9c6 100644 --- a/03-Infrastructure/Jinget.Logger/Handlers/ElasticSearchLoggingRepository.cs +++ b/03-Infrastructure/Jinget.Logger/Handlers/ElasticSearchLoggingRepository.cs @@ -1,27 +1,22 @@ namespace Jinget.Logger.Handlers; -public class ElasticSearchLoggingRepository : IElasticSearchLoggingRepository +public class ElasticSearchLoggingRepository(IElasticClient elasticClient, ElasticSearchSettingModel settings) : IElasticSearchLoggingRepository { - private readonly IElasticClient _elasticClient; - private readonly ElasticSearchSettingModel settings; - - public ElasticSearchLoggingRepository(IElasticClient elasticClient, ElasticSearchSettingModel settings) - { - _elasticClient = elasticClient; - this.settings = settings; - } - public async Task IndexAsync(LogModel param) { - if (settings.CreateIndexPerPartition) - await CreateIndexAsync(param.ParitionKey); - string indexName = GetIndexName(param.ParitionKey); + if (!string.IsNullOrWhiteSpace(param.ParitionKey)) + { + if (settings.CreateIndexPerPartition) + await CreateIndexAsync(param.ParitionKey); + string indexName = GetIndexName(param.ParitionKey); - var result = await _elasticClient.IndexAsync(param, i => i.Index(indexName).Refresh(settings.RefreshType)); + var result = await elasticClient.IndexAsync(param, i => i.Index(indexName).Refresh(settings.RefreshType)); - if (result.IsValid) - return result.IsValid; - throw new JingetException("Jinget Says: " + result.OriginalException.ToString()); + if (result.IsValid) + return result.IsValid; + throw new JingetException("Jinget Says: " + result.OriginalException.ToString()); + } + throw new JingetException("Jinget Says: ParitionKey is null or empty"); } string GetIndexName(string partitionKey) { @@ -40,9 +35,9 @@ public async Task CreateIndexAsync(string indexName) return false; indexName = GetIndexName(indexName); - if (!_elasticClient.Indices.Exists(indexName.ToLower()).Exists) + if (!elasticClient.Indices.Exists(indexName.ToLower()).Exists) { - var indexCreationResult = await _elasticClient.Indices + var indexCreationResult = await elasticClient.Indices .CreateAsync(indexName.ToLower(), index => index.Map(m => m.AutoMap(typeof(LogModel)).NumericDetection(true))); if (!indexCreationResult.IsValid) throw new JingetException("Jinget Says: " + indexCreationResult.OriginalException); @@ -54,22 +49,26 @@ public async Task BulkIndexAsync(IList @params) { foreach (var item in @params.GroupBy(g => g.ParitionKey)) { - if (settings.CreateIndexPerPartition) - await CreateIndexAsync(item.Key.ToString()); - string indexName = GetIndexName(item.Key.ToString()); - - var result = await _elasticClient.BulkAsync(i => i.Index(indexName).CreateMany(item)); - - if (!result.IsValid) - throw new JingetException("Jinget Says: " + result.OriginalException.ToString()); + if (!string.IsNullOrWhiteSpace(item.Key)) + { + if (settings.CreateIndexPerPartition) + await CreateIndexAsync(item.Key.ToString()); + string indexName = GetIndexName(item.Key.ToString()); + + var result = await elasticClient.BulkAsync(i => i.Index(indexName).CreateMany(item)); + + if (!result.IsValid) + throw new JingetException("Jinget Says: " + result.OriginalException.ToString()); + } + throw new JingetException("Jinget Says: ParitionKey is null or empty"); } return true; } - public async Task GetLatestAsync(Func, IPromise>> orderBy = null, string partitionKey = "") + public async Task GetLatestAsync(Func, IPromise>>? orderBy = null, string partitionKey = "") { string indexName = GetIndexName(partitionKey); - var lastRecord = await _elasticClient.SearchAsync(i => + var lastRecord = await elasticClient.SearchAsync(i => { var expr = i.Index(indexName).From(0).Take(1).MatchAll(); return expr.Sort(orderBy ?? (s => s.Descending(d => d.TimeStamp))); @@ -86,7 +85,7 @@ public async Task> SearchAsync( string username = "", string origin = "") { - var searchResult = await _elasticClient + var searchResult = await elasticClient .SearchAsync(i => i.Index(GetIndexName(partitionKey)) .Query(x => diff --git a/03-Infrastructure/Jinget.Logger/Handlers/IElasticSearchLoggingRepository.cs b/03-Infrastructure/Jinget.Logger/Handlers/IElasticSearchLoggingRepository.cs index b97b3c9..c614b81 100644 --- a/03-Infrastructure/Jinget.Logger/Handlers/IElasticSearchLoggingRepository.cs +++ b/03-Infrastructure/Jinget.Logger/Handlers/IElasticSearchLoggingRepository.cs @@ -23,7 +23,7 @@ public interface IElasticSearchLoggingRepository /// /// /// - Task GetLatestAsync(Func, IPromise>> orderBy = null, string partitionKey = ""); + Task GetLatestAsync(Func, IPromise>>? orderBy = null, string partitionKey = ""); /// /// Get list of logs diff --git a/03-Infrastructure/Jinget.Logger/ILoggerExtensions.cs b/03-Infrastructure/Jinget.Logger/ILoggerExtensions.cs index 0a37066..f12f36f 100644 --- a/03-Infrastructure/Jinget.Logger/ILoggerExtensions.cs +++ b/03-Infrastructure/Jinget.Logger/ILoggerExtensions.cs @@ -5,13 +5,15 @@ namespace Jinget.Logger; public static class ILoggerExtensions { /// - /// generates a custom log. This log's severity is + /// generates a custom log. This log's severity is /// - public static void LogCustom(this ILogger logger, HttpContext httpContext, string message, + public static void LogCustom(this ILogger logger, HttpContext? httpContext, string message, [CallerFilePath] string callerFilePath = "", [CallerLineNumber] long callerLineNumber = 0, [CallerMemberName] string callerMember = "") { + if (httpContext == null) + return; var log = LogModel.GetNewCustomObject(httpContext); log.Description = message; log.CallerFilePath = callerFilePath; @@ -21,17 +23,20 @@ public static void LogCustom(this ILogger logger, HttpContext httpContext, strin logger.Log(LogLevel.Information, JsonConvert.SerializeObject(log)); } - public static void LogInformation(this ILogger logger, HttpContext httpContext, string message) + public static void LogInformation(this ILogger logger, HttpContext? httpContext, string message) { + if (httpContext == null) + return; var log = LogModel.GetNewCustomObject(httpContext); log.Description = message; logger.Log(LogLevel.Information, JsonConvert.SerializeObject(log)); } - public static void LogError(this ILogger logger, HttpContext httpContext, string message, - Exception exception = null) + public static void LogError(this ILogger logger, HttpContext? httpContext, string message, Exception? exception = null) { + if (httpContext == null) + return; var log = LogModel.GetNewErrorObject(httpContext); log.AdditionalData = JsonConvert.SerializeObject(new { diff --git a/03-Infrastructure/Jinget.Logger/Log.cs b/03-Infrastructure/Jinget.Logger/Log.cs index 9893920..f4dca79 100644 --- a/03-Infrastructure/Jinget.Logger/Log.cs +++ b/03-Infrastructure/Jinget.Logger/Log.cs @@ -1,22 +1,28 @@ -namespace Jinget.Logger; +//using Jinget.Logger.Providers; -public class Log -{ - protected readonly ILogger Logger; - protected readonly RequestDelegate Next; +//namespace Jinget.Logger; - protected List BlackListHeaders; - protected List WhiteListHeaders; +//public class Log +//{ +// protected readonly ILogger Logger; +// protected readonly RequestDelegate Next; +// protected readonly IOptions LoggingOptions; - protected Log(RequestDelegate next, ILogger logger, IOptions blackListHeaders, IOptions whiteListHeaders) - { - Next = next; - Logger = logger; +// protected List? BlackListHeaders; +// protected List? WhiteListHeaders; - BlackListHeaders = blackListHeaders.Value.Headers?.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.ToLower()).ToList(); - BlackListHeaders ??= new List(); +// protected Log(RequestDelegate next, ILogger logger, +// IOptions loggingOptions, +// IOptions blackListHeaders, IOptions whiteListHeaders) +// { +// Next = next; +// Logger = logger; +// LoggingOptions = loggingOptions; + +// BlackListHeaders = blackListHeaders.Value.Headers?.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.ToLower()).ToList(); +// BlackListHeaders ??= []; - WhiteListHeaders = whiteListHeaders.Value.Headers?.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.ToLower()).ToList(); - WhiteListHeaders ??= new List(); - } -} \ No newline at end of file +// WhiteListHeaders = whiteListHeaders.Value.Headers?.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.ToLower()).ToList(); +// WhiteListHeaders ??= []; +// } +//} \ No newline at end of file diff --git a/03-Infrastructure/Jinget.Logger/LogMessage.cs b/03-Infrastructure/Jinget.Logger/LogMessage.cs index 2489220..9d48e77 100644 --- a/03-Infrastructure/Jinget.Logger/LogMessage.cs +++ b/03-Infrastructure/Jinget.Logger/LogMessage.cs @@ -3,9 +3,9 @@ public class LogMessage { public DateTime Timestamp { get; set; } = DateTime.Now; - public string Description { get; set; } + public string? Description { get; set; } public Microsoft.Extensions.Logging.LogLevel Severity { get; set; } - public string Exception { get; set; } + public string? Exception { get; set; } public override string ToString() { diff --git a/03-Infrastructure/Jinget.Logger/Loggers/RequestLogger.cs b/03-Infrastructure/Jinget.Logger/Loggers/RequestLogger.cs deleted file mode 100644 index a648b9c..0000000 --- a/03-Infrastructure/Jinget.Logger/Loggers/RequestLogger.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Jinget.ExceptionHandler.Extensions; -using Jinget.Logger; - -namespace Jinget.Logger.Loggers; - -public class RequestLogger( - RequestDelegate next, - ILogger logger, - IOptions blackListHeaders, - IOptions whiteListHeaders) : Log(next, logger, blackListHeaders, whiteListHeaders), ILog -{ - public async Task LogAsync(HttpContext context) - { - string requestBodyText; - if (context.IsMultipartContentType()) - { - requestBodyText = "--REQUEST BODY TRIMMED BY LOGGER-- multipart/form-data"; - } - else - { - context.Request.EnableBuffering(); - using var reader = new StreamReader(context.Request.Body, Encoding.UTF8, leaveOpen: true); - requestBodyText = await reader.ReadToEndAsync(); - context.Request.Body.Seek(0, SeekOrigin.Begin); - } - - SetLog(context, requestBodyText); - await Next(context); - } - - private void SetLog(HttpContext context, string requestBodyText) - { - var model = LogModel.GetNewRequestObject(context, requestBodyText, - context.GetLoggerHeaders(BlackListHeaders, WhiteListHeaders, isRequestHeader: true)); - - if (context.Request.Method != "OPTIONS") - Logger.LogInformation(JsonConvert.SerializeObject(model)); - } -} \ No newline at end of file diff --git a/03-Infrastructure/Jinget.Logger/Loggers/ResponseLogger.cs b/03-Infrastructure/Jinget.Logger/Loggers/ResponseLogger.cs deleted file mode 100644 index cc0b98f..0000000 --- a/03-Infrastructure/Jinget.Logger/Loggers/ResponseLogger.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Jinget.ExceptionHandler.Extensions; -using Jinget.Logger; - -namespace Jinget.Logger.Loggers; - -public class ResponseLogger( - RequestDelegate next, - ILogger logger, - IOptions blackListHeaders, - IOptions whiteListHeaders) : Log(next, logger, blackListHeaders, whiteListHeaders), ILog -{ - public async Task LogAsync(HttpContext context) - { - var originalResponseBody = context.Response?.Body; - var responseBodyStream = new MemoryStream(); - context.Response.Body = responseBodyStream; - try - { - await Next(context); - string responseBodyText; - if (context.IsMultipartContentType()) - responseBodyText = "--RESPONSE BODY TRIMMED BY LOGGER-- multipart/form-data"; - else - { - // Reset the body position to read it - responseBodyStream.Seek(0, SeekOrigin.Begin); - responseBodyText = await new StreamReader(responseBodyStream).ReadToEndAsync(); - responseBodyStream.Seek(0, SeekOrigin.Begin); - await responseBodyStream.CopyToAsync(originalResponseBody); - } - - SetLog(context, responseBodyText); - } - finally - { - context.Response.Body = originalResponseBody; - } - } - - private void SetLog(HttpContext context, string responseBody) - { - if (context.Request.Method == "OPTIONS") - return; - var model = LogModel.GetNewResponseObject( - context, - responseBody, - context.GetLoggerHeaders(BlackListHeaders, WhiteListHeaders, isRequestHeader: false)); - - if (context.Response.StatusCode < 400) - Logger.LogInformation(JsonConvert.SerializeObject(model)); - } -} \ No newline at end of file diff --git a/03-Infrastructure/Jinget.Logger/Middlewares/LogRequestMiddleware.cs b/03-Infrastructure/Jinget.Logger/Middlewares/LogRequestMiddleware.cs deleted file mode 100644 index 508c253..0000000 --- a/03-Infrastructure/Jinget.Logger/Middlewares/LogRequestMiddleware.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Jinget.Logger.Loggers; - -namespace Jinget.Logger.Middlewares; - -public class LogRequestMiddleware( - RequestDelegate next, - ILogger logger, - IOptions blackListHeaders, - IOptions whiteListHeaders) -{ - private readonly ILog _logger = new RequestLogger( - next, - logger, - blackListHeaders, - whiteListHeaders); - - public async Task InvokeAsync(HttpContext context) => await _logger.LogAsync(context); -} \ No newline at end of file diff --git a/03-Infrastructure/Jinget.Logger/Middlewares/LogResponseMiddleware.cs b/03-Infrastructure/Jinget.Logger/Middlewares/LogResponseMiddleware.cs deleted file mode 100644 index 32bc337..0000000 --- a/03-Infrastructure/Jinget.Logger/Middlewares/LogResponseMiddleware.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Jinget.Logger.Loggers; - -namespace Jinget.Logger.Middlewares; - -public class LogResponseMiddleware( - RequestDelegate next, - ILogger logger, - IOptions blackListHeaders, - IOptions whiteListHeaders) -{ - private readonly ILog _logger = new ResponseLogger(next, logger, blackListHeaders, whiteListHeaders); - - public async Task InvokeAsync(HttpContext context) => await _logger.LogAsync(context); -} \ No newline at end of file diff --git a/03-Infrastructure/Jinget.Logger/Middlewares/RequestResponseLoggingMiddleware.cs b/03-Infrastructure/Jinget.Logger/Middlewares/RequestResponseLoggingMiddleware.cs new file mode 100644 index 0000000..c2434f2 --- /dev/null +++ b/03-Infrastructure/Jinget.Logger/Middlewares/RequestResponseLoggingMiddleware.cs @@ -0,0 +1,128 @@ +using Jinget.ExceptionHandler.Extensions; +using Jinget.Logger.Providers; + +namespace Jinget.Logger.Middlewares; + +public class RequestResponseLoggingMiddleware +{ + protected readonly ILogger _logger; + protected readonly RequestDelegate _next; + protected readonly IOptions _loggingOptions; + + protected List? _blackListHeaders; + protected List? _whiteListHeaders; + + public RequestResponseLoggingMiddleware( + RequestDelegate next, + ILogger logger, + IOptions loggingOptions, + IOptions blackListHeaders, + IOptions whiteListHeaders) + { + _next = next; + _logger = logger; + _loggingOptions = loggingOptions; + + _blackListHeaders = blackListHeaders.Value.Headers?.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.ToLower()).ToList(); + _blackListHeaders ??= []; + + _whiteListHeaders = whiteListHeaders.Value.Headers?.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.ToLower()).ToList(); + _whiteListHeaders ??= []; + } + + public async Task InvokeAsync(HttpContext context) + { + if (context.Request.Method == "OPTIONS") + return; + await LogRequestAsync(context); + + var originalBodyStream = context.Response.Body; + + using (var responseBody = new MemoryStream()) + { + context.Response.Body = responseBody; + + try + { + await _next(context); + + await LogResponseAsync(context); + + // Always copy the MemoryStream back to the original body. + responseBody.Seek(0, SeekOrigin.Begin); + await responseBody.CopyToAsync(originalBodyStream); + } + finally + { + // Ensure the original body is restored, even if an exception occurs. + context.Response.Body = originalBodyStream; + } + } + } + + private async Task LogRequestAsync(HttpContext context) + { + string requestBodyText = ""; + if (context.IsMultipartContentType()) + { + requestBodyText = "--REQUEST BODY TRIMMED BY LOGGER-- multipart/form-data"; + } + else if (context.Request.ContentLength > _loggingOptions.Value.MaxRequestBodySize) + { + requestBodyText = $"--REQUEST BODY TOO LARGE--EXCEEDS {_loggingOptions.Value.MaxRequestBodySize} BYTE"; + } + else + { + context.Request.EnableBuffering(); + var body = context.Request.Body; + + var buffer = new byte[Convert.ToInt64(context.Request.ContentLength)]; + await context.Request.Body.ReadAsync(buffer, 0, buffer.Length); + + requestBodyText = Encoding.UTF8.GetString(buffer); + body.Seek(0, SeekOrigin.Begin); + context.Request.Body = body; + } + + SetRequestLog(context, requestBodyText); + } + + private async Task LogResponseAsync(HttpContext context) + { + string responseBodyText = ""; + if (context.IsMultipartContentType()) + { + responseBodyText = "--RESPONSE BODY TRIMMED BY LOGGER-- multipart/form-data"; + } + else if (context.Request.ContentLength > _loggingOptions.Value.MaxResponseBodySize) + { + responseBodyText = $"--RESPONSE BODY TOO LARGE--EXCEEDS {_loggingOptions.Value.MaxResponseBodySize} BYTE"; + } + else + { + context.Response.Body.Seek(0, SeekOrigin.Begin); + responseBodyText = await new StreamReader(context.Response.Body).ReadToEndAsync(); + context.Response.Body.Seek(0, SeekOrigin.Begin); + } + SetResponseLog(context, responseBodyText); + } + + private void SetResponseLog(HttpContext context, string responseBody) + { + var model = LogModel.GetNewResponseObject( + context, + responseBody, + context.GetLoggerHeaders(_blackListHeaders, _whiteListHeaders, isRequestHeader: false)); + + if (context.Response.StatusCode < 400 || context.Response.StatusCode == 429) + _logger.LogInformation(JsonConvert.SerializeObject(model)); + } + + private void SetRequestLog(HttpContext context, string requestBodyText) + { + var model = LogModel.GetNewRequestObject(context, requestBodyText, + context.GetLoggerHeaders(_blackListHeaders, _whiteListHeaders, isRequestHeader: true)); + + _logger.LogInformation(JsonConvert.SerializeObject(model)); + } +} \ No newline at end of file diff --git a/03-Infrastructure/Jinget.Logger/Providers/BatchingLogger.cs b/03-Infrastructure/Jinget.Logger/Providers/BatchingLogger.cs index e2f45b7..6361531 100644 --- a/03-Infrastructure/Jinget.Logger/Providers/BatchingLogger.cs +++ b/03-Infrastructure/Jinget.Logger/Providers/BatchingLogger.cs @@ -1,18 +1,12 @@ namespace Jinget.Logger.Providers; -public class BatchingLogger : ILogger +public class BatchingLogger(BatchingLoggerProvider loggerProvider) : ILogger { - private readonly BatchingLoggerProvider _provider; + public IDisposable? BeginScope(TState state) where TState : notnull => null; - public BatchingLogger(BatchingLoggerProvider loggerProvider) => _provider = loggerProvider; + public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel) => logLevel != Microsoft.Extensions.Logging.LogLevel.None; - public IDisposable BeginScope(TState state) => null; - - public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel) => - logLevel != Microsoft.Extensions.Logging.LogLevel.None; - - public void Log(Microsoft.Extensions.Logging.LogLevel logLevel, EventId eventId, TState state, Exception exception, - Func formatter) + public void Log(Microsoft.Extensions.Logging.LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { if (!IsEnabled(logLevel)) return; @@ -21,7 +15,7 @@ public void Log(Microsoft.Extensions.Logging.LogLevel logLevel, EventId } public void Log(DateTime timestamp, Microsoft.Extensions.Logging.LogLevel logLevel, TState state, - Exception exception, Func formatter) => _provider.AddMessage( + Exception? exception, Func formatter) => loggerProvider.AddMessage( new LogMessage { Description = formatter(state, exception), diff --git a/03-Infrastructure/Jinget.Logger/Providers/BatchingLoggerOptions.cs b/03-Infrastructure/Jinget.Logger/Providers/BatchingLoggerOptions.cs index cd95556..9099160 100644 --- a/03-Infrastructure/Jinget.Logger/Providers/BatchingLoggerOptions.cs +++ b/03-Infrastructure/Jinget.Logger/Providers/BatchingLoggerOptions.cs @@ -4,9 +4,23 @@ public class BatchingLoggerOptions { private int? _backgroundQueueSize; private int? _batchSize = 32; - private string[] _blacklistStrings = Array.Empty(); + private string[] _blacklistStrings = []; private TimeSpan _flushPeriod = TimeSpan.FromSeconds(1); + const long MB_10 = 1024 * 1024 * 10; + + /// + /// maximum request body size to log. + /// request body larger than this value will be logged as `--REQUEST BODY TOO LARGE--` string + /// + public long MaxRequestBodySize { get; set; } = MB_10; + + /// + /// maximum response body size to log. + /// response body larger than this value will be logged as `--REQUEST BODY TOO LARGE--` string + /// + public long MaxResponseBodySize { get; set; } = MB_10; + /// /// Defines the min log level that should processed /// Defaults to @@ -34,7 +48,7 @@ public TimeSpan FlushPeriod public string[] BlackListStrings { get => _blacklistStrings; - set => _blacklistStrings = value ?? Array.Empty(); + set => _blacklistStrings = value ?? []; } /// diff --git a/03-Infrastructure/Jinget.Logger/Providers/BatchingLoggerProvider.cs b/03-Infrastructure/Jinget.Logger/Providers/BatchingLoggerProvider.cs index 0f8947d..f5b2c45 100644 --- a/03-Infrastructure/Jinget.Logger/Providers/BatchingLoggerProvider.cs +++ b/03-Infrastructure/Jinget.Logger/Providers/BatchingLoggerProvider.cs @@ -1,17 +1,19 @@ -namespace Jinget.Logger.Providers; +using Microsoft.VisualStudio.Threading; + +namespace Jinget.Logger.Providers; public abstract class BatchingLoggerProvider : ILoggerProvider { private readonly Microsoft.Extensions.Logging.LogLevel _minAllowedLogLevel; private readonly int? _batchSize; private readonly string[] _blacklistStrings; - private readonly List _currentBatch = new(); + private readonly List _currentBatch = []; private readonly TimeSpan _interval; private readonly int? _queueSize; - private CancellationTokenSource _cancellationTokenSource; + private CancellationTokenSource? _cancellationTokenSource; - private BlockingCollection _messageQueue; - private Task _outputTask; + private BlockingCollection? _messageQueue; + private Task? _outputTask; protected BatchingLoggerProvider(IOptions options) { @@ -34,7 +36,8 @@ protected BatchingLoggerProvider(IOptions options) public void Dispose() { - StopAsync().GetAwaiter().GetResult(); + StopSynchronous(); + //StopAsync().GetAwaiter().GetResult(); GC.SuppressFinalize(this); } @@ -44,31 +47,34 @@ public void Dispose() private async Task ProcessLogQueueAsync() { - while (!_cancellationTokenSource.IsCancellationRequested) + if (_cancellationTokenSource != null && _messageQueue != null) { - var limit = _batchSize ?? int.MaxValue; - - while (limit > 0 && _messageQueue.TryTake(out var message)) + while (!_cancellationTokenSource.IsCancellationRequested) { - _currentBatch.Add(message); - limit--; - } + var limit = _batchSize ?? int.MaxValue; - if (_currentBatch.Count > 0) - { - try + while (limit > 0 && _messageQueue.TryTake(out var message)) { - await WriteMessagesAsync(_currentBatch, _cancellationTokenSource.Token); + _currentBatch.Add(message); + limit--; } - catch + + if (_currentBatch.Count > 0) { - // ignored + try + { + await WriteMessagesAsync(_currentBatch, _cancellationTokenSource.Token); + } + catch + { + // ignored + } + + _currentBatch.Clear(); } - _currentBatch.Clear(); + await IntervalAsync(_interval, _cancellationTokenSource.Token); } - - await IntervalAsync(_interval, _cancellationTokenSource.Token); } } @@ -83,16 +89,18 @@ internal void AddMessage(LogMessage message) //if log contains blacklist string then ignore it if (_blacklistStrings.Any(message.ToString().Contains)) return; - - if (!_messageQueue.IsAddingCompleted) - try - { - _messageQueue.Add(message, _cancellationTokenSource.Token); - } - catch - { - //cancellation token canceled or CompleteAdding called - } + if (_cancellationTokenSource != null && _messageQueue != null) + { + if (!_messageQueue.IsAddingCompleted) + try + { + _messageQueue.Add(message, _cancellationTokenSource.Token); + } + catch + { + //cancellation token canceled or CompleteAdding called + } + } } private void Start() @@ -109,21 +117,18 @@ private void Start() TaskScheduler.Default); } - private async Task StopAsync() + private void StopSynchronous() { - _cancellationTokenSource.Cancel(); - _messageQueue.CompleteAdding(); + _cancellationTokenSource?.Cancel(); + _messageQueue?.CompleteAdding(); - try - { - await _outputTask.WaitAsync(_interval); - } - catch (TaskCanceledException) + using var taskContext = new JoinableTaskContext(); + var joinableTaskFactory = new JoinableTaskFactory(taskContext); + joinableTaskFactory.Run(async () => { - } - catch (AggregateException ex) when (ex.InnerExceptions.Count == 1 && - ex.InnerExceptions[0] is TaskCanceledException) - { - } +#pragma warning disable CS8602 // Dereference of a possibly null reference. + await _outputTask?.WaitAsync(_interval); +#pragma warning restore CS8602 // Dereference of a possibly null reference. + }); } } \ No newline at end of file diff --git a/03-Infrastructure/Jinget.Logger/Providers/ElasticSearchProvider/ElasticSearchLoggerProvider.cs b/03-Infrastructure/Jinget.Logger/Providers/ElasticSearchProvider/ElasticSearchLoggerProvider.cs index c2feb1d..02dde56 100644 --- a/03-Infrastructure/Jinget.Logger/Providers/ElasticSearchProvider/ElasticSearchLoggerProvider.cs +++ b/03-Infrastructure/Jinget.Logger/Providers/ElasticSearchProvider/ElasticSearchLoggerProvider.cs @@ -4,22 +4,13 @@ /// An that writes logs /// [ProviderAlias("ElasticSearch")] -public class ElasticSearchLoggerProvider : BatchingLoggerProvider +public class ElasticSearchLoggerProvider(IServiceProvider serviceProvider, IOptions options) : BatchingLoggerProvider(options) { - private readonly IServiceProvider _serviceProvider; - - public ElasticSearchLoggerProvider( - IOptions options, - IServiceProvider serviceProvider) : base(options) - { - _serviceProvider = serviceProvider; - } - protected override async Task WriteMessagesAsync(IEnumerable messages, CancellationToken cancellationToken) { - var _logService = _serviceProvider.GetJingetService(); - + var _logService = serviceProvider.GetJingetService(); + if (_logService == null) return; foreach (var group in messages.GroupBy(GetGrouping)) { foreach (var item in group) @@ -27,7 +18,10 @@ protected override async Task WriteMessagesAsync(IEnumerable message var log = LogModel.GetNew(); try { - log = JsonConvert.DeserializeObject(item.Description); + if (!string.IsNullOrWhiteSpace(item.Description)) + { + log = JsonConvert.DeserializeObject(item.Description); + } } catch (JsonReaderException) //it means that the message is an error message(not error log) { @@ -40,9 +34,12 @@ protected override async Task WriteMessagesAsync(IEnumerable message } finally { - log.TimeStamp = item.Timestamp; - log.Severity = item.Severity.ToString(); - await _logService.CreateAsync(log); + if (log != null) + { + log.TimeStamp = item.Timestamp; + log.Severity = item.Severity.ToString(); + await _logService.CreateAsync(log); + } } } } diff --git a/03-Infrastructure/Jinget.Logger/ViewModels/LogSearchViewModel.cs b/03-Infrastructure/Jinget.Logger/ViewModels/LogSearchViewModel.cs index c5d8b06..3c1ece8 100644 --- a/03-Infrastructure/Jinget.Logger/ViewModels/LogSearchViewModel.cs +++ b/03-Infrastructure/Jinget.Logger/ViewModels/LogSearchViewModel.cs @@ -1,4 +1,3 @@ namespace Jinget.Logger.ViewModels; -// public record LogSearchViewModel(Guid RequestId, IEnumerable Logs); -public record LogSearchViewModel(string TraceIdentifier, IEnumerable Logs); \ No newline at end of file +public record LogSearchViewModel(string? TraceIdentifier, IEnumerable Logs); \ No newline at end of file diff --git a/Tests/Jinget.Core.DiScanner.Tests/DiScannerTest.cs b/Tests/Jinget.Core.DiScanner.Tests/DiScannerTest.cs index ef6cdce..6ab6476 100644 --- a/Tests/Jinget.Core.DiScanner.Tests/DiScannerTest.cs +++ b/Tests/Jinget.Core.DiScanner.Tests/DiScannerTest.cs @@ -12,15 +12,15 @@ public class DiScannerTest [TestMethod] public void Should_register_and_resolve_transient_di_using_calling_assembly() { - _services.RemoveAll(typeof(ICustomInterface)); - _services.Scan( + _services?.RemoveAll(typeof(ICustomInterface)); + _services?.Scan( s => s.FromCallingAssembly() .AddClasses() .AsImplementedInterfaces() .WithTransientLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService); } @@ -28,15 +28,15 @@ public void Should_register_and_resolve_transient_di_using_calling_assembly() [TestMethod] public void Should_register_and_resolve_scoped_di_using_calling_assembly() { - _services.RemoveAll(typeof(ICustomInterface)); - _services.Scan( + _services?.RemoveAll(typeof(ICustomInterface)); + _services?.Scan( s => s.FromCallingAssembly() .AddClasses() .AsImplementedInterfaces() .WithScopedLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService); } @@ -44,15 +44,15 @@ public void Should_register_and_resolve_scoped_di_using_calling_assembly() [TestMethod] public void Should_register_and_resolve_singleton_di_using_calling_assembly() { - _services.RemoveAll(typeof(ICustomInterface)); - _services.Scan( + _services?.RemoveAll(typeof(ICustomInterface)); + _services?.Scan( s => s.FromCallingAssembly() .AddClasses() .AsImplementedInterfaces() .WithSingletonLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService); } @@ -60,15 +60,15 @@ public void Should_register_and_resolve_singleton_di_using_calling_assembly() [TestMethod] public void Should_register_and_resolve_transient_di_using_executing_assembly() { - _services.RemoveAll(typeof(ISelector)); - _services.Scan( + _services?.RemoveAll(typeof(ISelector)); + _services?.Scan( s => s.FromExecutingAssembly() .AddClasses() .AsImplementedInterfaces() .WithTransientLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService); } @@ -76,15 +76,15 @@ public void Should_register_and_resolve_transient_di_using_executing_assembly() [TestMethod] public void Should_register_and_resolve_scoped_di_using_executing_assembly() { - _services.RemoveAll(typeof(ISelector)); - _services.Scan( + _services?.RemoveAll(typeof(ISelector)); + _services?.Scan( s => s.FromExecutingAssembly() .AddClasses() .AsImplementedInterfaces() .WithScopedLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService); } @@ -92,15 +92,15 @@ public void Should_register_and_resolve_scoped_di_using_executing_assembly() [TestMethod] public void Should_register_and_resolve_singleton_di_using_executing_assembly() { - _services.RemoveAll(typeof(ISelector)); - _services.Scan( + _services?.RemoveAll(typeof(ISelector)); + _services?.Scan( s => s.FromExecutingAssembly() .AddClasses() .AsImplementedInterfaces() .WithSingletonLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService); } @@ -108,18 +108,18 @@ public void Should_register_and_resolve_singleton_di_using_executing_assembly() [TestMethod] public void Should_register_and_resolve_transient_di_using_from_assemblies() { - _services.RemoveAll(typeof(ISelector)); - _services.RemoveAll(typeof(ICustomInterface)); + _services?.RemoveAll(typeof(ISelector)); + _services?.RemoveAll(typeof(ICustomInterface)); - _services.Scan( + _services?.Scan( s => s.FromAssemblies(typeof(ICustomInterface).Assembly, typeof(ISelector).Assembly) .AddClasses() .AsImplementedInterfaces() .WithTransientLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService1 = provider.GetRequiredService(); - var resolvedService2 = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService1 = provider?.GetRequiredService(); + var resolvedService2 = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService1); Assert.IsNotNull(resolvedService2); @@ -128,17 +128,17 @@ public void Should_register_and_resolve_transient_di_using_from_assemblies() [TestMethod] public void Should_register_and_resolve_scoped_di_using_from_assemblies() { - _services.RemoveAll(typeof(ISelector)); - _services.RemoveAll(typeof(ICustomInterface)); + _services?.RemoveAll(typeof(ISelector)); + _services?.RemoveAll(typeof(ICustomInterface)); - _services.Scan( + _services?.Scan( s => s.FromAssemblies(typeof(ICustomInterface).Assembly, typeof(ISelector).Assembly).AddClasses() .AsImplementedInterfaces() .WithScopedLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService1 = provider.GetRequiredService(); - var resolvedService2 = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService1 = provider?.GetRequiredService(); + var resolvedService2 = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService1); Assert.IsNotNull(resolvedService2); @@ -147,18 +147,18 @@ public void Should_register_and_resolve_scoped_di_using_from_assemblies() [TestMethod] public void Should_register_and_resolve_singleton_di_using_from_assemblies() { - _services.RemoveAll(typeof(ISelector)); - _services.RemoveAll(typeof(ICustomInterface)); + _services?.RemoveAll(typeof(ISelector)); + _services?.RemoveAll(typeof(ICustomInterface)); - _services.Scan( + _services?.Scan( s => s.FromAssemblies(typeof(ICustomInterface).Assembly, typeof(ISelector).Assembly) .AddClasses() .AsImplementedInterfaces() .WithSingletonLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService1 = provider.GetRequiredService(); - var resolvedService2 = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService1 = provider?.GetRequiredService(); + var resolvedService2 = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService1); Assert.IsNotNull(resolvedService2); @@ -167,15 +167,15 @@ public void Should_register_and_resolve_singleton_di_using_from_assemblies() [TestMethod] public void Should_register_and_resolve_transient_di_using_from_assembly() { - _services.RemoveAll(typeof(ISelector)); - _services.Scan( + _services?.RemoveAll(typeof(ISelector)); + _services?.Scan( s => s.FromAssembliesOf(typeof(TypeSourceSelector)) .AddClasses() .AsImplementedInterfaces() .WithTransientLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService); } @@ -183,15 +183,15 @@ public void Should_register_and_resolve_transient_di_using_from_assembly() [TestMethod] public void Should_register_and_resolve_scoped_di_using_from_assembly() { - _services.RemoveAll(typeof(ISelector)); - _services.Scan( + _services?.RemoveAll(typeof(ISelector)); + _services?.Scan( s => s.FromAssembliesOf(typeof(TypeSourceSelector)) .AddClasses() .AsImplementedInterfaces() .WithScopedLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService); } @@ -199,15 +199,15 @@ public void Should_register_and_resolve_scoped_di_using_from_assembly() [TestMethod] public void Should_register_and_resolve_singleton_di_using_from_assembly() { - _services.RemoveAll(typeof(ISelector)); - _services.Scan( + _services?.RemoveAll(typeof(ISelector)); + _services?.Scan( s => s.FromAssembliesOf(typeof(TypeSourceSelector)) .AddClasses() .AsImplementedInterfaces() .WithSingletonLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService); } @@ -219,15 +219,15 @@ public void Should_register_and_resolve_singleton_di_using_from_assembly() [TestMethod] public void Should_register_and_resolve_di_as_self() { - _services.RemoveAll(typeof(Sample)); - _services.Scan( + _services?.RemoveAll(typeof(Sample)); + _services?.Scan( s => s.FromAssembliesOf(typeof(Sample)) .AddClasses() .AsSelf() .WithSingletonLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService); } @@ -235,15 +235,15 @@ public void Should_register_and_resolve_di_as_self() [TestMethod] public void Should_register_and_resolve_di_as_custom() { - _services.RemoveAll(typeof(ICustomInterface)); - _services.Scan( + _services?.RemoveAll(typeof(ICustomInterface)); + _services?.Scan( s => s.FromAssembliesOf(typeof(Sample)) .AddClasses(x => x.AssignableTo(typeof(ICustomInterface))) .As() .WithSingletonLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService); } @@ -251,17 +251,17 @@ public void Should_register_and_resolve_di_as_custom() [TestMethod] public void Should_register_and_resolve_di_as_self_and_impl_interfaces() { - _services.RemoveAll(typeof(ICustomInterface)); - _services.RemoveAll(typeof(Sample)); - _services.Scan( + _services?.RemoveAll(typeof(ICustomInterface)); + _services?.RemoveAll(typeof(Sample)); + _services?.Scan( s => s.FromAssembliesOf(typeof(Sample)) .AddClasses() .AsSelfWithInterfaces() .WithSingletonLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService1 = provider.GetRequiredService(); - var resolvedService2 = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService1 = provider?.GetRequiredService(); + var resolvedService2 = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService1); Assert.IsNotNull(resolvedService2); @@ -272,14 +272,14 @@ public void Should_register_and_resolve_di_as_self_and_impl_interfaces() [TestMethod] public void Should_register_and_resolve_di__using_addtype() { - _services.RemoveAll(typeof(Sample)); - _services.Scan( + _services?.RemoveAll(typeof(Sample)); + _services?.Scan( s => s.AddType() .AsImplementedInterfaces() .WithSingletonLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService); } @@ -287,15 +287,15 @@ public void Should_register_and_resolve_di__using_addtype() [TestMethod] public void Should_register_and_resolve_di__using_addtypes() { - _services.RemoveAll(typeof(Sample)); - _services.Scan( + _services?.RemoveAll(typeof(Sample)); + _services?.Scan( s => s.AddTypes(typeof(Sample), typeof(TypeSourceSelector)) .AsImplementedInterfaces() .WithSingletonLifetime()); - var provider = _services.BuildServiceProvider(); - var resolvedService1 = provider.GetRequiredService(); - var resolvedService2 = provider.GetRequiredService(); + var provider = _services?.BuildServiceProvider(); + var resolvedService1 = provider?.GetRequiredService(); + var resolvedService2 = provider?.GetRequiredService(); Assert.IsNotNull(resolvedService1); Assert.IsNotNull(resolvedService2); diff --git a/Tests/Jinget.Core.DiScanner.Tests/Jinget.Core.DiScanner.Tests.csproj b/Tests/Jinget.Core.DiScanner.Tests/Jinget.Core.DiScanner.Tests.csproj index 382f67f..a1aecca 100644 --- a/Tests/Jinget.Core.DiScanner.Tests/Jinget.Core.DiScanner.Tests.csproj +++ b/Tests/Jinget.Core.DiScanner.Tests/Jinget.Core.DiScanner.Tests.csproj @@ -10,11 +10,11 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Tests/Jinget.Core.Tests/GlobalUsings.cs b/Tests/Jinget.Core.Tests/GlobalUsings.cs index e22e0ae..bfe9b56 100644 --- a/Tests/Jinget.Core.Tests/GlobalUsings.cs +++ b/Tests/Jinget.Core.Tests/GlobalUsings.cs @@ -4,7 +4,7 @@ global using System.Collections.Generic; global using System.ComponentModel; global using System.Data; -global using System.Data.SqlClient; +global using Microsoft.Data.SqlClient; global using System.IdentityModel.Tokens.Jwt; global using System.Linq; global using System.Linq.Expressions; diff --git a/Tests/Jinget.Core.Tests/Jinget.Core.Tests.csproj b/Tests/Jinget.Core.Tests/Jinget.Core.Tests.csproj index 8b31b56..d56d1a7 100644 --- a/Tests/Jinget.Core.Tests/Jinget.Core.Tests.csproj +++ b/Tests/Jinget.Core.Tests/Jinget.Core.Tests.csproj @@ -7,15 +7,15 @@ - + + - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/Tests/Jinget.Handlers.ExternalServiceHandlers.Tests/DefaultServiceHandler/JingetServiceHandlerTests.cs b/Tests/Jinget.Handlers.ExternalServiceHandlers.Tests/DefaultServiceHandler/JingetServiceHandlerTests.cs index fc65f76..4e716d5 100644 --- a/Tests/Jinget.Handlers.ExternalServiceHandlers.Tests/DefaultServiceHandler/JingetServiceHandlerTests.cs +++ b/Tests/Jinget.Handlers.ExternalServiceHandlers.Tests/DefaultServiceHandler/JingetServiceHandlerTests.cs @@ -16,7 +16,7 @@ public async Task Should_configure_httpclientfactory_by_timeoutAsync() var result = await jingetServiceHandler.GetAsync("users"); - Assert.IsTrue(result is null); + Assert.IsNull(result); } [TestMethod()] public async Task Should_call_get_restapiAsync() @@ -28,20 +28,20 @@ public async Task Should_call_get_restapiAsync() }; jingetServiceHandler.Events.RawResponseReceived += (sender, e) => { - Assert.IsFalse(e == ""); + Assert.AreNotEqual("", e); }; jingetServiceHandler.Events.ExceptionOccurred += (sender, e) => { - Assert.IsTrue(e is null); + Assert.IsNull(e); }; jingetServiceHandler.Events.ResponseDeserialized += (sender, e) => { - Assert.IsFalse(e is null); + Assert.IsNotNull(e); }; var result = await jingetServiceHandler.GetAsync("users"); - Assert.IsFalse(result is null); + Assert.IsNotNull(result); } [TestMethod()] @@ -54,15 +54,15 @@ public async Task Should_call_post_restapiAsync() }; jingetServiceHandler.Events.RawResponseReceived += (sender, e) => { - Assert.IsFalse(e == ""); + Assert.AreNotEqual("", e); }; jingetServiceHandler.Events.ExceptionOccurred += (sender, e) => { - Assert.IsTrue(e is null); + Assert.IsNull(e); }; jingetServiceHandler.Events.ResponseDeserialized += (sender, e) => { - Assert.IsFalse(e is null); + Assert.IsNotNull(e); }; var result = await jingetServiceHandler .PostAsync("posts", @@ -77,7 +77,7 @@ public async Task Should_call_post_restapiAsync() {"Content-type","application/json; charset=UTF-8" } }); - Assert.IsFalse(result is null); + Assert.IsNotNull(result); } [TestMethod()] @@ -90,15 +90,15 @@ public async Task Should_call_send_restapiAsync() }; jingetServiceHandler.Events.RawResponseReceived += (sender, e) => { - Assert.IsFalse(e == ""); + Assert.AreNotEqual("", e); }; jingetServiceHandler.Events.ExceptionOccurred += (sender, e) => { - Assert.IsTrue(e is null); + Assert.IsNull(e); }; jingetServiceHandler.Events.ResponseDeserialized += (sender, e) => { - Assert.IsFalse(e is null); + Assert.IsNotNull(e); }; var request = new HttpRequestMessage @@ -117,7 +117,7 @@ public async Task Should_call_send_restapiAsync() var result = await jingetServiceHandler.SendAsync(request); - Assert.IsFalse(result is null); + Assert.IsNotNull(result); } [TestMethod()] @@ -130,18 +130,20 @@ public async Task Should_call_get_soapAsync() }; jingetServiceHandler.Events.RawResponseReceived += (sender, e) => { - Assert.IsFalse(e == ""); + Assert.AreNotEqual("", e); }; jingetServiceHandler.Events.ExceptionOccurred += (sender, e) => { - Assert.IsTrue(e is null); + Assert.IsNull(e); }; jingetServiceHandler.Events.ResponseDeserialized += (sender, e) => { - Assert.IsFalse(e is null); + Assert.IsNotNull(e); }; var (envelope, request) = new SampleSOAPRequest().CreateEnvelope(); + Assert.IsNotNull(envelope); + Assert.IsNotNull(envelope.Body); envelope.Body.Add = new SampleSOAPRequest.SampleSOAPGet { intA = 1, intB = 2 }; var result = await jingetServiceHandler.PostAsync(envelope.ToString(), new Dictionary @@ -164,7 +166,7 @@ public async Task Should_post_multipart_formdataAsync() }; jingetServiceHandler.Events.ResponseDeserialized += (sender, e) => { - Assert.IsFalse(e is null); + Assert.IsNotNull(e); }; List files = [ @@ -173,7 +175,7 @@ public async Task Should_post_multipart_formdataAsync() ]; var response = await jingetServiceHandler.UploadFileAsync("something", files); - + Assert.IsNotNull(response); Assert.IsFalse(string.IsNullOrWhiteSpace(response.Status)); } } diff --git a/Tests/Jinget.Handlers.ExternalServiceHandlers.Tests/DefaultServiceHandler/SampleType/SampleSOAPRequest.cs b/Tests/Jinget.Handlers.ExternalServiceHandlers.Tests/DefaultServiceHandler/SampleType/SampleSOAPRequest.cs index 6796232..fe19019 100644 --- a/Tests/Jinget.Handlers.ExternalServiceHandlers.Tests/DefaultServiceHandler/SampleType/SampleSOAPRequest.cs +++ b/Tests/Jinget.Handlers.ExternalServiceHandlers.Tests/DefaultServiceHandler/SampleType/SampleSOAPRequest.cs @@ -5,7 +5,7 @@ /// public class SampleSOAPRequest : SOAPRequestBase { - public override (Envelope envelope, SampleSOAPGet request) CreateEnvelope() + public override (Envelope? envelope, SampleSOAPGet? request) CreateEnvelope() { var envelope = new Envelope { @@ -36,12 +36,12 @@ public override string ToString() /// /// defines the Header node inside the envelop node /// - public EnvelopeHeader Header { get; set; } + public EnvelopeHeader? Header { get; set; } /// /// Defines the Body node inside the envelop node /// - public EnvelopeBody Body { get; set; } + public EnvelopeBody? Body { get; set; } } /// @@ -60,7 +60,7 @@ public class EnvelopeHeader public class EnvelopeBody { [XmlElement(Namespace = "http://tempuri.org/")] - public SampleSOAPGet Add { get; set; } + public SampleSOAPGet? Add { get; set; } } /// @@ -69,7 +69,9 @@ public class EnvelopeBody [Serializable] public class SampleSOAPGet { +#pragma warning disable IDE1006 // Naming Styles public int intA { get; set; } public int intB { get; set; } +#pragma warning restore IDE1006 // Naming Styles } } diff --git a/Tests/Jinget.Handlers.ExternalServiceHandlers.Tests/Jinget.Handlers.ExternalServiceHandlers.Tests.csproj b/Tests/Jinget.Handlers.ExternalServiceHandlers.Tests/Jinget.Handlers.ExternalServiceHandlers.Tests.csproj index 9b7d421..34bf627 100644 --- a/Tests/Jinget.Handlers.ExternalServiceHandlers.Tests/Jinget.Handlers.ExternalServiceHandlers.Tests.csproj +++ b/Tests/Jinget.Handlers.ExternalServiceHandlers.Tests/Jinget.Handlers.ExternalServiceHandlers.Tests.csproj @@ -10,10 +10,10 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Tests/Jinget.WebAPI.Tests/Logs/Log20250224.txt b/Tests/Jinget.WebAPI.Tests/Logs/Log20250224.txt new file mode 100644 index 0000000..713dc07 --- /dev/null +++ b/Tests/Jinget.WebAPI.Tests/Logs/Log20250224.txt @@ -0,0 +1,63 @@ +2025-02-24 22:54:57.734 +03:30 [Information] Now listening on: http://localhost:5063 + + +2025-02-24 22:54:57.894 +03:30 [Information] Application started. Press Ctrl+C to shut down. + + +2025-02-24 22:54:57.897 +03:30 [Information] Hosting environment: Development + + +2025-02-24 22:54:57.899 +03:30 [Information] Content root path: E:\Source Codes\GitHub\Jinget\Tests\Jinget.WebAPI.Tests + + +2025-02-24 22:54:59.376 +03:30 [Information] {"TimeStamp":"2025-02-24T22:54:59.3370585+03:30","Url":"http://localhost:5063/","Description":null,"EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":0.0,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000001","Method":"GET","Body":"","Headers":"[\r\n \"[Accept, text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7]\",\r\n \"[Connection, keep-alive]\",\r\n \"[Host, localhost:5063]\",\r\n \"[User-Agent, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0]\",\r\n \"[Accept-Encoding, gzip, deflate, br, zstd]\",\r\n \"[Accept-Language, en-US,en;q=0.9,fa;q=0.8,he;q=0.7]\",\r\n \"[Upgrade-Insecure-Requests, 1]\",\r\n \"[sec-ch-ua, \\\"Not(A:Brand\\\";v=\\\"99\\\", \\\"Microsoft Edge\\\";v=\\\"133\\\", \\\"Chromium\\\";v=\\\"133\\\"]\",\r\n \"[sec-ch-ua-mobile, ?0]\",\r\n \"[sec-ch-ua-platform, \\\"Windows\\\"]\",\r\n \"[Sec-Fetch-Site, none]\",\r\n \"[Sec-Fetch-Mode, navigate]\",\r\n \"[Sec-Fetch-User, ?1]\",\r\n \"[Sec-Fetch-Dest, document]\"\r\n]","IP":"::1","Type":1,"TypeDescription":"request","PageUrl":null,"ContentLength":650,"AdditionalData":null,"Username":null,"Severity":"Information","CallerFilePath":null,"CallerLineNumber":null,"CallerMember":null,"Id":0} + + +2025-02-24 22:54:59.483 +03:30 [Error] {"TimeStamp":"2025-02-24T22:54:59.4730267+03:30","Url":"http://localhost:5063/","Description":"{\"Message\":\"Not Found\",\"StackTrace\":\" at Jinget.ExceptionHandler.Extensions.ApplicationBuilderExtensions.<>c.<b__0_0>d.MoveNext() in E:\\\\Source Codes\\\\GitHub\\\\Jinget\\\\03-Infrastructure\\\\Jinget.ExceptionHandler\\\\Extensions\\\\ApplicationBuilderExtensions.cs:line 21\\r\\n--- End of stack trace from previous location ---\\r\\n at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.g__Awaited|10_0(ExceptionHandlerMiddlewareImpl middleware, HttpContext context, Task task)\",\"Data\":{}}","EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":136.3231,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000001","Method":"GET","Body":null,"Headers":null,"IP":"::1","Type":4,"TypeDescription":"error","PageUrl":null,"ContentLength":0,"AdditionalData":null,"Username":null,"Severity":"Error","CallerFilePath":null,"CallerLineNumber":null,"CallerMember":null,"Id":0} + + +2025-02-24 22:55:03.761 +03:30 [Information] {"TimeStamp":"2025-02-24T22:55:03.7569339+03:30","Url":"http://localhost:5063/ratelimit","Description":null,"EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":0.0,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000002","Method":"GET","Body":"","Headers":"[\r\n \"[Accept, text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7]\",\r\n \"[Connection, keep-alive]\",\r\n \"[Host, localhost:5063]\",\r\n \"[User-Agent, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0]\",\r\n \"[Accept-Encoding, gzip, deflate, br, zstd]\",\r\n \"[Accept-Language, en-US,en;q=0.9,fa;q=0.8,he;q=0.7]\",\r\n \"[Upgrade-Insecure-Requests, 1]\",\r\n \"[sec-ch-ua, \\\"Not(A:Brand\\\";v=\\\"99\\\", \\\"Microsoft Edge\\\";v=\\\"133\\\", \\\"Chromium\\\";v=\\\"133\\\"]\",\r\n \"[sec-ch-ua-mobile, ?0]\",\r\n \"[sec-ch-ua-platform, \\\"Windows\\\"]\",\r\n \"[Sec-Fetch-Site, none]\",\r\n \"[Sec-Fetch-Mode, navigate]\",\r\n \"[Sec-Fetch-User, ?1]\",\r\n \"[Sec-Fetch-Dest, document]\"\r\n]","IP":"::1","Type":1,"TypeDescription":"request","PageUrl":null,"ContentLength":650,"AdditionalData":null,"Username":null,"Severity":"Information","CallerFilePath":null,"CallerLineNumber":null,"CallerMember":null,"Id":0} + + +2025-02-24 22:55:03.768 +03:30 [Information] {"TimeStamp":"2025-02-24T22:55:03.7639121+03:30","Url":"http://localhost:5063/ratelimit","Description":"{\"StatusCode\":429}","EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":7.5728,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000002","Method":"GET","Body":"{\"isSuccess\":true,\"isFailure\":false,\"effectedRowsCount\":0,\"data\":[\"Rate Limit applied\"]}","Headers":"[\r\n \"[Content-Type, application/json; charset=utf-8]\"\r\n]","IP":"::1","Type":2,"TypeDescription":"response","PageUrl":null,"ContentLength":132,"AdditionalData":null,"Username":null,"Severity":"Information","CallerFilePath":null,"CallerLineNumber":null,"CallerMember":null,"Id":0} + + +2025-02-24 22:55:07.068 +03:30 [Information] {"TimeStamp":"2025-02-24T22:55:07.0648165+03:30","Url":"http://localhost:5063/customlog","Description":null,"EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":0.0,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000003","Method":"GET","Body":"","Headers":"[\r\n \"[Accept, text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7]\",\r\n \"[Connection, keep-alive]\",\r\n \"[Host, localhost:5063]\",\r\n \"[User-Agent, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0]\",\r\n \"[Accept-Encoding, gzip, deflate, br, zstd]\",\r\n \"[Accept-Language, en-US,en;q=0.9,fa;q=0.8,he;q=0.7]\",\r\n \"[Upgrade-Insecure-Requests, 1]\",\r\n \"[sec-ch-ua, \\\"Not(A:Brand\\\";v=\\\"99\\\", \\\"Microsoft Edge\\\";v=\\\"133\\\", \\\"Chromium\\\";v=\\\"133\\\"]\",\r\n \"[sec-ch-ua-mobile, ?0]\",\r\n \"[sec-ch-ua-platform, \\\"Windows\\\"]\",\r\n \"[Sec-Fetch-Site, none]\",\r\n \"[Sec-Fetch-Mode, navigate]\",\r\n \"[Sec-Fetch-User, ?1]\",\r\n \"[Sec-Fetch-Dest, document]\"\r\n]","IP":"::1","Type":1,"TypeDescription":"request","PageUrl":null,"ContentLength":650,"AdditionalData":null,"Username":null,"Severity":"Information","CallerFilePath":null,"CallerLineNumber":null,"CallerMember":null,"Id":0} + + +2025-02-24 22:55:07.073 +03:30 [Information] {"TimeStamp":"2025-02-24T22:55:07.0699081+03:30","Url":"http://localhost:5063/customlog","Description":"Sample Custom message!","EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":5.4556,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000003","Method":"GET","Body":null,"Headers":null,"IP":"::1","Type":3,"TypeDescription":"customlog","PageUrl":null,"ContentLength":0,"AdditionalData":null,"Username":null,"Severity":"Information","CallerFilePath":null,"CallerLineNumber":null,"CallerMember":null,"Id":0} + + +2025-02-24 22:55:07.078 +03:30 [Information] {"TimeStamp":"2025-02-24T22:55:07.074324+03:30","Url":"http://localhost:5063/customlog","Description":"Sample Custom message2!","EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":9.9832,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000003","Method":"GET","Body":null,"Headers":null,"IP":"::1","Type":3,"TypeDescription":"customlog","PageUrl":null,"ContentLength":0,"AdditionalData":null,"Username":null,"Severity":"Information","CallerFilePath":"E:\\Source Codes\\GitHub\\Jinget\\Tests\\Jinget.WebAPI.Tests\\Program.cs","CallerLineNumber":89,"CallerMember":"
$","Id":0} + + +2025-02-24 22:55:07.084 +03:30 [Information] {"TimeStamp":"2025-02-24T22:55:07.0807677+03:30","Url":"http://localhost:5063/customlog","Description":"{\"StatusCode\":200}","EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":16.2906,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000003","Method":"GET","Body":"custom log saved","Headers":"[\r\n \"[Content-Type, text/plain; charset=utf-8]\"\r\n]","IP":"::1","Type":2,"TypeDescription":"response","PageUrl":null,"ContentLength":54,"AdditionalData":null,"Username":null,"Severity":"Information","CallerFilePath":null,"CallerLineNumber":null,"CallerMember":null,"Id":0} + + +2025-02-24 22:55:21.427 +03:30 [Information] {"TimeStamp":"2025-02-24T22:55:21.424275+03:30","Url":"http://localhost:5063/unauthorized","Description":null,"EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":0.0,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000004","Method":"GET","Body":"","Headers":"[\r\n \"[Accept, text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7]\",\r\n \"[Connection, keep-alive]\",\r\n \"[Host, localhost:5063]\",\r\n \"[User-Agent, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0]\",\r\n \"[Accept-Encoding, gzip, deflate, br, zstd]\",\r\n \"[Accept-Language, en-US,en;q=0.9,fa;q=0.8,he;q=0.7]\",\r\n \"[Upgrade-Insecure-Requests, 1]\",\r\n \"[sec-ch-ua, \\\"Not(A:Brand\\\";v=\\\"99\\\", \\\"Microsoft Edge\\\";v=\\\"133\\\", \\\"Chromium\\\";v=\\\"133\\\"]\",\r\n \"[sec-ch-ua-mobile, ?0]\",\r\n \"[sec-ch-ua-platform, \\\"Windows\\\"]\",\r\n \"[Sec-Fetch-Site, none]\",\r\n \"[Sec-Fetch-Mode, navigate]\",\r\n \"[Sec-Fetch-User, ?1]\",\r\n \"[Sec-Fetch-Dest, document]\"\r\n]","IP":"::1","Type":1,"TypeDescription":"request","PageUrl":null,"ContentLength":650,"AdditionalData":null,"Username":null,"Severity":"Information","CallerFilePath":null,"CallerLineNumber":null,"CallerMember":null,"Id":0} + + +2025-02-24 22:55:21.486 +03:30 [Error] {"TimeStamp":"2025-02-24T22:55:21.4827952+03:30","Url":"http://localhost:5063/unauthorized","Description":"{\"Message\":\"Unauthorized\",\"StackTrace\":\" at Jinget.ExceptionHandler.Extensions.ApplicationBuilderExtensions.<>c.<b__0_0>d.MoveNext() in E:\\\\Source Codes\\\\GitHub\\\\Jinget\\\\03-Infrastructure\\\\Jinget.ExceptionHandler\\\\Extensions\\\\ApplicationBuilderExtensions.cs:line 21\\r\\n--- End of stack trace from previous location ---\\r\\n at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.g__Awaited|10_0(ExceptionHandlerMiddlewareImpl middleware, HttpContext context, Task task)\",\"Data\":{}}","EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":58.9069,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000004","Method":"GET","Body":null,"Headers":null,"IP":"::1","Type":4,"TypeDescription":"error","PageUrl":null,"ContentLength":0,"AdditionalData":null,"Username":null,"Severity":"Error","CallerFilePath":null,"CallerLineNumber":null,"CallerMember":null,"Id":0} + + +2025-02-24 22:55:27.568 +03:30 [Information] {"TimeStamp":"2025-02-24T22:55:27.564843+03:30","Url":"http://localhost:5063/errorlog","Description":null,"EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":0.0,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000005","Method":"GET","Body":"","Headers":"[\r\n \"[Accept, text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7]\",\r\n \"[Connection, keep-alive]\",\r\n \"[Host, localhost:5063]\",\r\n \"[User-Agent, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0]\",\r\n \"[Accept-Encoding, gzip, deflate, br, zstd]\",\r\n \"[Accept-Language, en-US,en;q=0.9,fa;q=0.8,he;q=0.7]\",\r\n \"[Upgrade-Insecure-Requests, 1]\",\r\n \"[sec-ch-ua, \\\"Not(A:Brand\\\";v=\\\"99\\\", \\\"Microsoft Edge\\\";v=\\\"133\\\", \\\"Chromium\\\";v=\\\"133\\\"]\",\r\n \"[sec-ch-ua-mobile, ?0]\",\r\n \"[sec-ch-ua-platform, \\\"Windows\\\"]\",\r\n \"[Sec-Fetch-Site, none]\",\r\n \"[Sec-Fetch-Mode, navigate]\",\r\n \"[Sec-Fetch-User, ?1]\",\r\n \"[Sec-Fetch-Dest, document]\"\r\n]","IP":"::1","Type":1,"TypeDescription":"request","PageUrl":null,"ContentLength":650,"AdditionalData":null,"Username":null,"Severity":"Information","CallerFilePath":null,"CallerLineNumber":null,"CallerMember":null,"Id":0} + + +2025-02-24 22:55:27.576 +03:30 [Error] {"TimeStamp":"2025-02-24T22:55:27.5691714+03:30","Url":"http://localhost:5063/errorlog","Description":null,"EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":5.1301,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000005","Method":"GET","Body":null,"Headers":null,"IP":"::1","Type":4,"TypeDescription":"error","PageUrl":null,"ContentLength":0,"AdditionalData":"{\"description\":\"Sample Error message1!\",\"exception\":null}","Username":null,"Severity":"Error","CallerFilePath":null,"CallerLineNumber":null,"CallerMember":null,"Id":0} + + +2025-02-24 22:55:27.581 +03:30 [Error] {"TimeStamp":"2025-02-24T22:55:27.5766216+03:30","Url":"http://localhost:5063/errorlog","Description":null,"EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":12.3024,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000005","Method":"GET","Body":null,"Headers":null,"IP":"::1","Type":4,"TypeDescription":"error","PageUrl":null,"ContentLength":0,"AdditionalData":"{\"description\":\"Sample Error message2!\",\"exception\":null}","Username":null,"Severity":"Error","CallerFilePath":null,"CallerLineNumber":null,"CallerMember":null,"Id":0} + + +2025-02-24 22:55:27.585 +03:30 [Error] {"TimeStamp":"2025-02-24T22:55:27.58117+03:30","Url":"http://localhost:5063/errorlog","Description":null,"EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":16.7636,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000005","Method":"GET","Body":null,"Headers":null,"IP":"::1","Type":4,"TypeDescription":"error","PageUrl":null,"ContentLength":0,"AdditionalData":"{\"description\":\"Sample Error message3!\",\"exception\":null}","Username":null,"Severity":"Error","CallerFilePath":null,"CallerLineNumber":null,"CallerMember":null,"Id":0} + + +2025-02-24 22:55:27.716 +03:30 [Error] {"TimeStamp":"2025-02-24T22:55:27.7125061+03:30","Url":"http://localhost:5063/errorlog","Description":"{\"Message\":\"Sample exception\",\"StackTrace\":\" at Program.<>c.<
$>b__0_7(IHttpContextAccessor httpContextAccessor, ILogger`1 logger) in E:\\\\Source Codes\\\\GitHub\\\\Jinget\\\\Tests\\\\Jinget.WebAPI.Tests\\\\Program.cs:line 98\\r\\n at lambda_method10(Closure, Object, HttpContext)\\r\\n at Program.<>c.<<
$>b__0_4>d.MoveNext() in E:\\\\Source Codes\\\\GitHub\\\\Jinget\\\\Tests\\\\Jinget.WebAPI.Tests\\\\Program.cs:line 79\\r\\n--- End of stack trace from previous location ---\\r\\n at Jinget.Logger.Middlewares.RequestResponseLoggingMiddleware.InvokeAsync(HttpContext context) in E:\\\\Source Codes\\\\GitHub\\\\Jinget\\\\03-Infrastructure\\\\Jinget.Logger\\\\Middlewares\\\\RequestResponseLoggingMiddleware.cs:line 47\\r\\n at Jinget.ExceptionHandler.Extensions.ApplicationBuilderExtensions.<>c.<b__0_0>d.MoveNext() in E:\\\\Source Codes\\\\GitHub\\\\Jinget\\\\03-Infrastructure\\\\Jinget.ExceptionHandler\\\\Extensions\\\\ApplicationBuilderExtensions.cs:line 16\\r\\n--- End of stack trace from previous location ---\\r\\n at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.g__Awaited|10_0(ExceptionHandlerMiddlewareImpl middleware, HttpContext context, Task task)\",\"Data\":{}}","EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":147.986,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000005","Method":"GET","Body":null,"Headers":null,"IP":"::1","Type":4,"TypeDescription":"error","PageUrl":null,"ContentLength":0,"AdditionalData":null,"Username":null,"Severity":"Error","CallerFilePath":null,"CallerLineNumber":null,"CallerMember":null,"Id":0} + + +2025-02-24 22:55:33.512 +03:30 [Information] {"TimeStamp":"2025-02-24T22:55:33.5091017+03:30","Url":"http://localhost:5063/exception","Description":null,"EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":0.0,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000006","Method":"GET","Body":"","Headers":"[\r\n \"[Accept, text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7]\",\r\n \"[Connection, keep-alive]\",\r\n \"[Host, localhost:5063]\",\r\n \"[User-Agent, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0]\",\r\n \"[Accept-Encoding, gzip, deflate, br, zstd]\",\r\n \"[Accept-Language, en-US,en;q=0.9,fa;q=0.8,he;q=0.7]\",\r\n \"[Upgrade-Insecure-Requests, 1]\",\r\n \"[sec-ch-ua, \\\"Not(A:Brand\\\";v=\\\"99\\\", \\\"Microsoft Edge\\\";v=\\\"133\\\", \\\"Chromium\\\";v=\\\"133\\\"]\",\r\n \"[sec-ch-ua-mobile, ?0]\",\r\n \"[sec-ch-ua-platform, \\\"Windows\\\"]\",\r\n \"[Sec-Fetch-Site, none]\",\r\n \"[Sec-Fetch-Mode, navigate]\",\r\n \"[Sec-Fetch-User, ?1]\",\r\n \"[Sec-Fetch-Dest, document]\"\r\n]","IP":"::1","Type":1,"TypeDescription":"request","PageUrl":null,"ContentLength":650,"AdditionalData":null,"Username":null,"Severity":"Information","CallerFilePath":null,"CallerLineNumber":null,"CallerMember":null,"Id":0} + + +2025-02-24 22:55:33.631 +03:30 [Error] {"TimeStamp":"2025-02-24T22:55:33.6278583+03:30","Url":"http://localhost:5063/exception","Description":"{\"Message\":\"New HttpRequestException\",\"StackTrace\":\" at Program.<>c.<
$>b__0_5(IHttpContextAccessor httpContextAccessor, ILogger`1 logger) in E:\\\\Source Codes\\\\GitHub\\\\Jinget\\\\Tests\\\\Jinget.WebAPI.Tests\\\\Program.cs:line 84\\r\\n at lambda_method8(Closure, Object, HttpContext)\\r\\n at Program.<>c.<<
$>b__0_4>d.MoveNext() in E:\\\\Source Codes\\\\GitHub\\\\Jinget\\\\Tests\\\\Jinget.WebAPI.Tests\\\\Program.cs:line 79\\r\\n--- End of stack trace from previous location ---\\r\\n at Jinget.Logger.Middlewares.RequestResponseLoggingMiddleware.InvokeAsync(HttpContext context) in E:\\\\Source Codes\\\\GitHub\\\\Jinget\\\\03-Infrastructure\\\\Jinget.Logger\\\\Middlewares\\\\RequestResponseLoggingMiddleware.cs:line 47\\r\\n at Jinget.ExceptionHandler.Extensions.ApplicationBuilderExtensions.<>c.<b__0_0>d.MoveNext() in E:\\\\Source Codes\\\\GitHub\\\\Jinget\\\\03-Infrastructure\\\\Jinget.ExceptionHandler\\\\Extensions\\\\ApplicationBuilderExtensions.cs:line 16\\r\\n--- End of stack trace from previous location ---\\r\\n at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.g__Awaited|10_0(ExceptionHandlerMiddlewareImpl middleware, HttpContext context, Task task)\",\"Data\":{}}","EnvironmentInfo":"{\"MachineName\":\"DESKTOP-IPL5N29\",\"CLRVersion\":\"8.0.12\",\"OSVersion\":\"\\\"Microsoft Windows NT 10.0.26100.0\\\"\",\"ProcessId\":22432,\"WorkingThreadUsername\":\"farah\",\"WorkingThreadUserDomain\":\"DESKTOP-IPL5N29\"}","ElapsedMilliseconds":119.0777,"SubSystem":"Jinget.WebAPI.Tests","ParitionKey":null,"TraceIdentifier":"0HNAL13MF7M8A:00000006","Method":"GET","Body":null,"Headers":null,"IP":"::1","Type":4,"TypeDescription":"error","PageUrl":null,"ContentLength":0,"AdditionalData":null,"Username":null,"Severity":"Error","CallerFilePath":null,"CallerLineNumber":null,"CallerMember":null,"Id":0} + + diff --git a/Tests/Jinget.WebAPI.Tests/Program.cs b/Tests/Jinget.WebAPI.Tests/Program.cs index 048f893..f74344a 100644 --- a/Tests/Jinget.WebAPI.Tests/Program.cs +++ b/Tests/Jinget.WebAPI.Tests/Program.cs @@ -1,5 +1,5 @@ -using Jinget.ExceptionHandler.Entities; -using Jinget.ExceptionHandler.Extensions; +using Jinget.Core.Types; +using Jinget.Logger.Configuration.File; using Jinget.Logger.Extensions; var builder = WebApplication.CreateBuilder(args); @@ -8,59 +8,80 @@ string[] blacklist = ["/logs/"]; -//FileSettingModel fileSetting = new FileSettingModel +FileSettingModel fileSetting = new() +{ + FileNamePrefix = "Log", + LogDirectory = "Logs", + RetainFileCountLimit = 5, + FileSizeLimitMB = 10, + UseGlobalExceptionHandler = true, + Handle4xxResponses = true, + MaxRequestBodySize = 1024 * 1024 * 10, + MaxResponseBodySize = 1024 * 1024 * 10 +}; +builder.Host.LogToFile(blacklist, fileSetting, LogLevel.Information); +builder.Services.ConfigureFileLogger(fileSetting); + +//builder.Host.LogToElasticSearch(blacklist); +//var elasticSearchSetting = new ElasticSearchSettingModel //{ -// FileNamePrefix = "Log", -// LogDirectory = "Logs", -// RetainFileCountLimit = 5, -// FileSizeLimitMB = 10, +// CreateIndexPerPartition = false, +// UserName = "elastic", +// Password = "Aa@123456", +// Url = "localhost:9200", +// UseSsl = false, // UseGlobalExceptionHandler = true, // Handle4xxResponses = true, +// MaxRequestBodySize = 1024 * 1024 * 10, +// MaxResponseBodySize = 1024 * 1024 * 10 //}; -//builder.Host.LogToFile(blacklist, fileSetting); -//builder.Services.ConfigureFileLogger(fileSetting); -builder.Host.LogToElasticSearch(blacklist); -var elasticSearchSetting = new ElasticSearchSettingModel -{ - CreateIndexPerPartition = false, - UserName = "elastic", - Password = "Aa@123456", - Url = "localhost:9200", - UseSsl = false, - UseGlobalExceptionHandler = true, - Handle4xxResponses = true -}; -builder.Services.ConfigureElasticSearchLogger(elasticSearchSetting); +//builder.Services.ConfigureElasticSearchLogger(elasticSearchSetting); builder.Services.AddControllers(); var app = builder.Build(); -app.UseWhen(p => elasticSearchSetting.CreateIndexPerPartition, appBuilder => +//app.UseWhen(p => elasticSearchSetting.CreateIndexPerPartition, appBuilder => +//{ +// appBuilder.Use(async (context, next) => +// { +// context.SetLoggerPartitionKey($"{DateTime.Now:yyyyMMdd}"); +// await next.Invoke(); +// }); +//}); +app.UseWhen(p => p.Request.Path == "/detailedlog", appBuilder => { appBuilder.Use(async (context, next) => { - context.SetLoggerPartitionKey($"{DateTime.Now:yyyyMMdd}"); + context.Request.Headers.TryAdd("req-header", $"{DateTime.Now:yyyyMMdd}"); + context.Request.Headers.TryAdd("AdditionalData", $"{DateTime.Now:yyyyMMdd}"); await next.Invoke(); }); }); +app.UseJingetLogging(); app.UseWhen(p => p.Request.Path == "/detailedlog", appBuilder => { appBuilder.Use(async (context, next) => { - context.Request.Headers.Add("req-header", $"{DateTime.Now:yyyyMMdd}"); - context.Request.Headers.Add("AdditionalData", $"{DateTime.Now:yyyyMMdd}"); await next.Invoke(); + context.Response.Headers.TryAdd("res-header", $"{DateTime.Now:yyyyMMdd}"); + context.Items.Add("AdditionalData", $"{DateTime.Now:yyyyMMdd}"); }); }); -app.UseJingetLogging(); -app.UseWhen(p => p.Request.Path == "/detailedlog", appBuilder => + +app.Use(async (context, next) => { - appBuilder.Use(async (context, next) => + if (context.Request.Path == "/ratelimit") { + context.Response.StatusCode = StatusCodes.Status429TooManyRequests; + await context.Response.WriteAsJsonAsync(new ResponseResult("Rate Limit applied", 0)); + } + else await next.Invoke(); - context.Response.Headers.Add("res-header", $"{DateTime.Now:yyyyMMdd}"); - context.Items.Add("AdditionalData", $"{DateTime.Now:yyyyMMdd}"); - }); +}); + +app.MapGet("exception", (IHttpContextAccessor httpContextAccessor, ILogger logger) => +{ + throw new HttpRequestException("New HttpRequestException"); }); app.MapGet("customlog", (IHttpContextAccessor httpContextAccessor, ILogger logger) => { @@ -73,7 +94,7 @@ logger.LogError(httpContextAccessor.HttpContext, "Sample Error message1!"); logger.LogError(httpContextAccessor.HttpContext, "Sample Error message2!"); logger.LogError(httpContextAccessor.HttpContext, "Sample Error message3!"); - + throw new Exception("Sample exception"); }); app.MapGet("unauthorized", (IHttpContextAccessor httpContextAccessor, ILogger logger) => @@ -82,14 +103,10 @@ }); app.MapGet("successlog", () => "Hello vahid"); app.MapGet("detailedlog", () => "Sample Success"); -app.MapPost("save", (viewmodel vm) => vm); +app.MapPost("save", (object vm) => vm); -app.MapGet("/logs/{search}/{page}/{pagesize}", async ( - IElasticSearchLoggingDomainService domainService, string search, int page, int pagesize) => - await domainService.SearchAsync("20241026", search, page, pagesize, origin: "/logs/")); +//app.MapGet("/logs/{search}/{page}/{pagesize}", async ( +// IElasticSearchLoggingDomainService domainService, string search, int page, int pagesize) => +// await domainService.SearchAsync("20241026", search, page, pagesize, origin: "/logs/")); -app.Run(); -public class viewmodel -{ - public string name { get; set; } -} \ No newline at end of file +app.Run(); \ No newline at end of file