From 5d7652a77e0f05dc573c7656c62a439d132b4e8e Mon Sep 17 00:00:00 2001 From: Andrii Voznesenskyi Date: Mon, 30 Sep 2024 17:26:02 +0200 Subject: [PATCH 01/11] (#115) webApi: udpate formatters and extensiosn [pack-all-force] --- .../src/Paralax.WebApi/Extensions.cs | 151 +++++++++++------- .../Formatters/JsonInputFormatter.cs | 34 ++-- .../Formatters/JsonOutputFormatter.cs | 14 +- .../src/Paralax.WebApi/Paralax.WebApi.csproj | 2 + 4 files changed, 119 insertions(+), 82 deletions(-) diff --git a/src/Paralax.WebApi/src/Paralax.WebApi/Extensions.cs b/src/Paralax.WebApi/src/Paralax.WebApi/Extensions.cs index d944726..85f110c 100644 --- a/src/Paralax.WebApi/src/Paralax.WebApi/Extensions.cs +++ b/src/Paralax.WebApi/src/Paralax.WebApi/Extensions.cs @@ -19,6 +19,11 @@ using Paralax.WebApi.Exceptions; using Paralax.WebApi.Formatters; using Paralax.WebApi.Requests; +using Open.Serialization.Json; +using System.Text.Json.Serialization; +using System.Text.Json; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using Paralax.Core; namespace Paralax.WebApi { @@ -57,7 +62,8 @@ public static Task DispatchAsync(this HttpContext co return handler.HandleAsync(request); } - public static IParalaxBuilder AddWebApi(this IParalaxBuilder builder, Action configureMvc = null, string sectionName = SectionName) + public static IParalaxBuilder AddWebApi(this IParalaxBuilder builder, Action configureMvc = null, + IJsonSerializer jsonSerializer = null, string sectionName = SectionName) { if (string.IsNullOrWhiteSpace(sectionName)) { @@ -69,46 +75,70 @@ public static IParalaxBuilder AddWebApi(this IParalaxBuilder builder, Action(); - builder.Services.AddSingleton(new WebApiEndpointDefinitions()); + if (jsonSerializer is null) + { + var jsonSerializerOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true, + NumberHandling = JsonNumberHandling.AllowReadingFromString, + Converters = {new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)} + }; + + jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)); + + var factory = new Open.Serialization.Json.System.JsonSerializerFactory(jsonSerializerOptions); + + jsonSerializer = factory.GetSerializer(); + } - var options = builder.GetOptions(sectionName); - builder.Services.AddSingleton(options); + if (jsonSerializer.GetType().Namespace?.Contains("Newtonsoft") == true) + { + builder.Services.Configure(o => o.AllowSynchronousIO = true); + builder.Services.Configure(o => o.AllowSynchronousIO = true); + } - var mvcCoreBuilder = builder.Services - .AddLogging() - .AddMvcCore(); + builder.Services.AddSingleton(jsonSerializer); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(new WebApiEndpointDefinitions()); + var options = builder.GetOptions(sectionName); + builder.Services.AddSingleton(options); + _bindRequestFromRoute = options.BindRequestFromRoute; - mvcCoreBuilder.AddMvcOptions(o => + var mvcCoreBuilder = builder.Services + .AddLogging() + .AddMvcCore(); + + mvcCoreBuilder.AddMvcOptions(o => { o.OutputFormatters.Clear(); - o.OutputFormatters.Add(new JsonOutputFormatter()); + o.OutputFormatters.Add(new JsonOutputFormatter(jsonSerializer)); o.InputFormatters.Clear(); - o.InputFormatters.Add(new JsonInputFormatter()); + o.InputFormatters.Add(new JsonInputFormatter(jsonSerializer)); }) .AddDataAnnotations() .AddApiExplorer() .AddAuthorization(); - configureMvc?.Invoke(mvcCoreBuilder); - - builder.Services.Scan(scan => - scan.FromAssemblies(AppDomain.CurrentDomain.GetAssemblies()) - .AddClasses(classes => classes.AssignableTo(typeof(IRequestHandler<,>))) - .AsImplementedInterfaces() - .WithTransientLifetime()); + configureMvc?.Invoke(mvcCoreBuilder); - builder.Services.AddTransient(); + builder.Services.Scan(s => + s.FromAssemblies(AppDomain.CurrentDomain.GetAssemblies()) + .AddClasses(c => c.AssignableTo(typeof(IRequestHandler<,>)) + .WithoutAttribute(typeof(DecoratorAttribute))) + .AsImplementedInterfaces() + .WithTransientLifetime()); - if (builder.Services.All(s => s.ServiceType != typeof(IExceptionToResponseMapper))) - { - builder.Services.AddTransient(); - } + builder.Services.AddTransient(); - return builder; + if (builder.Services.All(s => s.ServiceType != typeof(IExceptionToResponseMapper))) + { + builder.Services.AddTransient(); } + return builder; + } + public static async Task ReadJsonAsync(this HttpContext context) { var logger = context.RequestServices.GetService(); @@ -160,47 +190,54 @@ public static IParalaxBuilder AddErrorHandler(this IParalaxBuilder builder) } - public static T ReadQuery(this HttpContext context) where T : class - { - var request = context.Request; - var values = new Dictionary(); + public static T ReadQuery(this HttpContext context) where T : class + { + var request = context.Request; + RouteValueDictionary values = null; - // Parse route data if it exists - if (HasRouteData(request)) - { - var routeValues = request.HttpContext.GetRouteData().Values; - foreach (var (key, value) in routeValues) - { - values[key] = value; - } - } + // Parse route data if available + if (HasRouteData(request)) + { + values = request.HttpContext.GetRouteData().Values; + } - // Parse query string if it exists - if (HasQueryString(request)) - { - var queryString = HttpUtility.ParseQueryString(request.HttpContext.Request.QueryString.Value); - foreach (var key in queryString.AllKeys) - { - if (!string.IsNullOrEmpty(key)) - { - values[key] = queryString[key]; - } - } - } + // Parse query string if available + if (HasQueryString(request)) + { + var queryString = HttpUtility.ParseQueryString(request.HttpContext.Request.QueryString.Value); + values ??= new RouteValueDictionary(); - // If there are no values, return a new instance of the object - if (!values.Any()) + // Add query string parameters to values + foreach (var key in queryString.AllKeys) { - return null; + values.TryAdd(key, queryString[key]); } + } - // Serialize the dictionary of values into JSON - var serialized = NetJSON.NetJSON.Serialize(values); + // Get JSON serializer from the request services + var serializer = context.RequestServices.GetRequiredService(); - // Deserialize the JSON back into the expected object type - return NetJSON.NetJSON.Deserialize(serialized); + // If no values were found, return an empty object + if (values == null || !values.Any()) + { + return serializer.Deserialize(EmptyJsonObject); } + // Serialize the dictionary of values to JSON + var serializedValues = serializer.Serialize(values.ToDictionary(k => k.Key, k => k.Value)) + ?.Replace("\\\"", "\"") // Unescape quotes + .Replace("\"{", "{") // Fix escaped curly brackets + .Replace("}\"", "}") + .Replace("\"[", "[") // Fix escaped square brackets + .Replace("]\"", "]"); + + // Deserialize and return the resulting object + return serializer.Deserialize(serializedValues); + } + + + + public static Task Ok(this HttpResponse response, object data = null) { diff --git a/src/Paralax.WebApi/src/Paralax.WebApi/Formatters/JsonInputFormatter.cs b/src/Paralax.WebApi/src/Paralax.WebApi/Formatters/JsonInputFormatter.cs index c3d4530..72f8488 100644 --- a/src/Paralax.WebApi/src/Paralax.WebApi/Formatters/JsonInputFormatter.cs +++ b/src/Paralax.WebApi/src/Paralax.WebApi/Formatters/JsonInputFormatter.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Formatters; using NetJSON; +using Open.Serialization.Json; namespace Paralax.WebApi.Formatters { @@ -13,15 +14,14 @@ internal class JsonInputFormatter : IInputFormatter { private const string EmptyJson = "{}"; private readonly ConcurrentDictionary _methods = new(); + private readonly IJsonSerializer _serializer; private readonly MethodInfo _deserializeMethod; - public JsonInputFormatter() + public JsonInputFormatter(IJsonSerializer serializer) { - // Ensure that the correct Deserialize method is selected - _deserializeMethod = typeof(NetJSON.NetJSON).GetMethods() - .Single(m => m.IsGenericMethod && m.Name == nameof(NetJSON.NetJSON.Deserialize) - && m.GetParameters().Length == 1 - && m.GetParameters()[0].ParameterType == typeof(string)); // Ensure it's the correct overload + _serializer = serializer; + _deserializeMethod = _serializer.GetType().GetMethods() + .Single(m => m.IsGenericMethod && m.Name == nameof(_serializer.Deserialize)); } public bool CanRead(InputFormatterContext context) @@ -38,10 +38,10 @@ public async Task ReadAsync(InputFormatterContext context) } var request = context.HttpContext.Request; - string json; - - using (var streamReader = new StreamReader(request.Body)) + var json = string.Empty; + if (request.Body is not null) { + using var streamReader = new StreamReader(request.Body); json = await streamReader.ReadToEndAsync(); } @@ -50,18 +50,10 @@ public async Task ReadAsync(InputFormatterContext context) json = EmptyJson; } - try - { - // Use NetJSON to deserialize the JSON string - var result = method.Invoke(null, new object[] { json }); + var result = method.Invoke(_serializer, new object[] {json}); - return await InputFormatterResult.SuccessAsync(result); - } - catch (Exception ex) - { - // Handle deserialization errors - throw new InvalidOperationException("Invalid JSON format", ex); - } - } + return await InputFormatterResult.SuccessAsync(result); + } } } + diff --git a/src/Paralax.WebApi/src/Paralax.WebApi/Formatters/JsonOutputFormatter.cs b/src/Paralax.WebApi/src/Paralax.WebApi/Formatters/JsonOutputFormatter.cs index cdb6ca3..b264f4f 100644 --- a/src/Paralax.WebApi/src/Paralax.WebApi/Formatters/JsonOutputFormatter.cs +++ b/src/Paralax.WebApi/src/Paralax.WebApi/Formatters/JsonOutputFormatter.cs @@ -2,11 +2,19 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Formatters; using NetJSON; +using Open.Serialization.Json; namespace Paralax.WebApi.Formatters { internal class JsonOutputFormatter : IOutputFormatter { + private readonly IJsonSerializer _serializer; + + public JsonOutputFormatter(IJsonSerializer serializer) + { + _serializer = serializer; + } + public bool CanWriteResult(OutputFormatterCanWriteContext context) { return true; @@ -20,15 +28,13 @@ public async Task WriteAsync(OutputFormatterWriteContext context) } context.HttpContext.Response.ContentType = "application/json"; - if (context.Object is string json) { await context.HttpContext.Response.WriteAsync(json); return; } - - var serializedJson = NetJSON.NetJSON.Serialize(context.Object); - await context.HttpContext.Response.WriteAsync(serializedJson); + + await _serializer.SerializeAsync(context.HttpContext.Response.Body, context.Object); } } } diff --git a/src/Paralax.WebApi/src/Paralax.WebApi/Paralax.WebApi.csproj b/src/Paralax.WebApi/src/Paralax.WebApi/Paralax.WebApi.csproj index 2490545..4f81186 100644 --- a/src/Paralax.WebApi/src/Paralax.WebApi/Paralax.WebApi.csproj +++ b/src/Paralax.WebApi/src/Paralax.WebApi/Paralax.WebApi.csproj @@ -38,6 +38,8 @@ + + From e516265ae331e9a1c6d3eaa297206187ce8e4c21 Mon Sep 17 00:00:00 2001 From: Andrii Voznesenskyi Date: Tue, 1 Oct 2024 10:13:59 +0200 Subject: [PATCH 02/11] (#115) webApi: udpate date time handler [pack-all-force] --- .../src/Paralax.WebApi/Utils/Extensions.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/Paralax.WebApi/src/Paralax.WebApi/Utils/Extensions.cs b/src/Paralax.WebApi/src/Paralax.WebApi/Utils/Extensions.cs index d8a7e75..fa3c3cb 100644 --- a/src/Paralax.WebApi/src/Paralax.WebApi/Utils/Extensions.cs +++ b/src/Paralax.WebApi/src/Paralax.WebApi/Utils/Extensions.cs @@ -4,11 +4,13 @@ using System.Linq; using System.Reflection; using System.Runtime.Serialization; +using System.Text.RegularExpressions; namespace Paralax.WebApi.Utils { public static class Extensions { + private static readonly Regex JsDateRegex = new Regex(@"\/Date\((\d+)(?:[+-]\d+)?\)\/", RegexOptions.Compiled); // Returns the default instance of the specified type public static object GetDefaultInstance(this Type type) { @@ -279,6 +281,13 @@ public static object ConvertToExpectedType(Type targetType, object value) } } + // Date handling + if (targetType == typeof(DateTime)) + { + return ParseDateTime(value); + } + + // Handle nullable types if (Nullable.GetUnderlyingType(targetType) != null) { @@ -325,5 +334,33 @@ public static T ConvertTo(this object value) return instance; } + + public static DateTime? ParseDateTime(object value) + { + if (value == null) return null; + + // Check for JS-style date \/Date(...) + var stringValue = value.ToString(); + var match = JsDateRegex.Match(stringValue); + if (match.Success && long.TryParse(match.Groups[1].Value, out var unixTimeMs)) + { + // Convert milliseconds since Unix epoch + return DateTimeOffset.FromUnixTimeMilliseconds(unixTimeMs).UtcDateTime; + } + + // Try ISO 8601 date parsing + if (DateTime.TryParse(stringValue, null, System.Globalization.DateTimeStyles.RoundtripKind, out var parsedDate)) + { + return parsedDate; + } + + // Try parsing as Unix timestamp + if (long.TryParse(stringValue, out var unixTimeSeconds)) + { + return DateTimeOffset.FromUnixTimeSeconds(unixTimeSeconds).UtcDateTime; + } + + return null; + } } } From 9d439c7d2ff11b22dc5785179e1ac2e243ce4e61 Mon Sep 17 00:00:00 2001 From: Andrii Voznesenskyi Date: Thu, 3 Oct 2024 01:55:28 +0200 Subject: [PATCH 03/11] (#118) webApi: update web api formatters --- .../Exceptions/ErrorHandlerMiddleware.cs | 13 +- .../src/Paralax.WebApi/Extensions.cs | 205 +++++++++--------- .../Formatters/JsonInputFormatter.cs | 24 +- .../Formatters/JsonOutputFormatter.cs | 4 +- .../ParalaxFormatterResolver.cs | 1 - .../src/Paralax.WebApi/Parsers/JsonParser.cs | 126 +++++++---- .../src/Paralax.WebApi/Utils/Extensions.cs | 37 ---- 7 files changed, 205 insertions(+), 205 deletions(-) diff --git a/src/Paralax.WebApi/src/Paralax.WebApi/Exceptions/ErrorHandlerMiddleware.cs b/src/Paralax.WebApi/src/Paralax.WebApi/Exceptions/ErrorHandlerMiddleware.cs index 16ee9b2..7e3603a 100644 --- a/src/Paralax.WebApi/src/Paralax.WebApi/Exceptions/ErrorHandlerMiddleware.cs +++ b/src/Paralax.WebApi/src/Paralax.WebApi/Exceptions/ErrorHandlerMiddleware.cs @@ -3,19 +3,21 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -using NetJSON; +using Open.Serialization.Json; namespace Paralax.WebApi.Exceptions { internal sealed class ErrorHandlerMiddleware : IMiddleware { private readonly IExceptionToResponseMapper _exceptionToResponseMapper; + private readonly IJsonSerializer _jsonSerializer; private readonly ILogger _logger; public ErrorHandlerMiddleware(IExceptionToResponseMapper exceptionToResponseMapper, - ILogger logger) + IJsonSerializer jsonSerializer, ILogger logger) { _exceptionToResponseMapper = exceptionToResponseMapper; + _jsonSerializer = jsonSerializer; _logger = logger; } @@ -28,7 +30,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) catch (Exception exception) { _logger.LogError(exception, "An error occurred while processing the request."); - await HandleErrorAsync(context, exception); // Handle the error when caught + await HandleErrorAsync(context, exception); } } @@ -48,10 +50,7 @@ private async Task HandleErrorAsync(HttpContext context, Exception exception) context.Response.ContentType = "application/json"; - // Use NetJSON to serialize the response - var jsonResponse = NetJSON.NetJSON.Serialize(response); - - await context.Response.WriteAsync(jsonResponse); + await _jsonSerializer.SerializeAsync(context.Response.Body, response); } } } diff --git a/src/Paralax.WebApi/src/Paralax.WebApi/Extensions.cs b/src/Paralax.WebApi/src/Paralax.WebApi/Extensions.cs index 85f110c..395a8e8 100644 --- a/src/Paralax.WebApi/src/Paralax.WebApi/Extensions.cs +++ b/src/Paralax.WebApi/src/Paralax.WebApi/Extensions.cs @@ -7,6 +7,8 @@ using System.Net; using System.Reflection; using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; using System.Web; using Microsoft.AspNetCore.Builder; @@ -15,15 +17,10 @@ using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using NetJSON; +using Open.Serialization.Json; using Paralax.WebApi.Exceptions; using Paralax.WebApi.Formatters; using Paralax.WebApi.Requests; -using Open.Serialization.Json; -using System.Text.Json.Serialization; -using System.Text.Json; -using Microsoft.AspNetCore.Server.Kestrel.Core; -using Paralax.Core; namespace Paralax.WebApi { @@ -62,8 +59,7 @@ public static Task DispatchAsync(this HttpContext co return handler.HandleAsync(request); } - public static IParalaxBuilder AddWebApi(this IParalaxBuilder builder, Action configureMvc = null, - IJsonSerializer jsonSerializer = null, string sectionName = SectionName) + public static IParalaxBuilder AddWebApi(this IParalaxBuilder builder, Action configureMvc = null, IJsonSerializer jsonSerializer = null, string sectionName = SectionName) { if (string.IsNullOrWhiteSpace(sectionName)) { @@ -75,69 +71,63 @@ public static IParalaxBuilder AddWebApi(this IParalaxBuilder builder, Action(o => o.AllowSynchronousIO = true); - builder.Services.Configure(o => o.AllowSynchronousIO = true); - } + builder.Services.AddSingleton(jsonSerializer); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(new WebApiEndpointDefinitions()); - builder.Services.AddSingleton(jsonSerializer); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(new WebApiEndpointDefinitions()); - var options = builder.GetOptions(sectionName); - builder.Services.AddSingleton(options); - _bindRequestFromRoute = options.BindRequestFromRoute; + var options = builder.GetOptions(sectionName); + builder.Services.AddSingleton(options); + _bindRequestFromRoute = options.BindRequestFromRoute; - var mvcCoreBuilder = builder.Services - .AddLogging() - .AddMvcCore(); + var mvcCoreBuilder = builder.Services + .AddLogging() + .AddMvcCore(); - mvcCoreBuilder.AddMvcOptions(o => + // Configure formatters using the injected IJsonSerializer + mvcCoreBuilder.AddMvcOptions(o => { o.OutputFormatters.Clear(); - o.OutputFormatters.Add(new JsonOutputFormatter(jsonSerializer)); + o.OutputFormatters.Add(new JsonOutputFormatter(jsonSerializer)); // Use the new serializer o.InputFormatters.Clear(); - o.InputFormatters.Add(new JsonInputFormatter(jsonSerializer)); + o.InputFormatters.Add(new JsonInputFormatter(jsonSerializer)); // Use the new serializer }) .AddDataAnnotations() .AddApiExplorer() .AddAuthorization(); - configureMvc?.Invoke(mvcCoreBuilder); + configureMvc?.Invoke(mvcCoreBuilder); - builder.Services.Scan(s => - s.FromAssemblies(AppDomain.CurrentDomain.GetAssemblies()) - .AddClasses(c => c.AssignableTo(typeof(IRequestHandler<,>)) - .WithoutAttribute(typeof(DecoratorAttribute))) - .AsImplementedInterfaces() - .WithTransientLifetime()); + builder.Services.Scan(scan => + scan.FromAssemblies(AppDomain.CurrentDomain.GetAssemblies()) + .AddClasses(classes => classes.AssignableTo(typeof(IRequestHandler<,>))) + .AsImplementedInterfaces() + .WithTransientLifetime()); - builder.Services.AddTransient(); + builder.Services.AddTransient(); - if (builder.Services.All(s => s.ServiceType != typeof(IExceptionToResponseMapper))) - { - builder.Services.AddTransient(); - } + if (builder.Services.All(s => s.ServiceType != typeof(IExceptionToResponseMapper))) + { + builder.Services.AddTransient(); + } - return builder; - } + return builder; + } public static async Task ReadJsonAsync(this HttpContext context) { @@ -152,9 +142,8 @@ public static async Task ReadJsonAsync(this HttpContext context) try { - using var reader = new StreamReader(context.Request.Body); - var json = await reader.ReadToEndAsync(); - var payload = NetJSON.NetJSON.Deserialize(json); + var serializer = context.RequestServices.GetRequiredService(); + var payload = await serializer.DeserializeAsync(context.Request.Body); if (_bindRequestFromRoute && HasRouteData(context.Request)) { @@ -189,55 +178,49 @@ public static IParalaxBuilder AddErrorHandler(this IParalaxBuilder builder) return builder; } - - public static T ReadQuery(this HttpContext context) where T : class - { - var request = context.Request; - RouteValueDictionary values = null; - - // Parse route data if available - if (HasRouteData(request)) + public static T ReadQuery(this HttpContext context) where T : class { - values = request.HttpContext.GetRouteData().Values; - } + var request = context.Request; + var values = new Dictionary(); - // Parse query string if available - if (HasQueryString(request)) - { - var queryString = HttpUtility.ParseQueryString(request.HttpContext.Request.QueryString.Value); - values ??= new RouteValueDictionary(); - - // Add query string parameters to values - foreach (var key in queryString.AllKeys) + // Parse route data if it exists + if (HasRouteData(request)) { - values.TryAdd(key, queryString[key]); + var routeValues = request.HttpContext.GetRouteData().Values; + foreach (var (key, value) in routeValues) + { + values[key] = value; + } } - } - - // Get JSON serializer from the request services - var serializer = context.RequestServices.GetRequiredService(); - - // If no values were found, return an empty object - if (values == null || !values.Any()) - { - return serializer.Deserialize(EmptyJsonObject); - } - - // Serialize the dictionary of values to JSON - var serializedValues = serializer.Serialize(values.ToDictionary(k => k.Key, k => k.Value)) - ?.Replace("\\\"", "\"") // Unescape quotes - .Replace("\"{", "{") // Fix escaped curly brackets - .Replace("}\"", "}") - .Replace("\"[", "[") // Fix escaped square brackets - .Replace("]\"", "]"); - - // Deserialize and return the resulting object - return serializer.Deserialize(serializedValues); - } + // Parse query string if it exists + if (HasQueryString(request)) + { + var queryString = HttpUtility.ParseQueryString(request.HttpContext.Request.QueryString.Value); + foreach (var key in queryString.AllKeys) + { + if (!string.IsNullOrEmpty(key)) + { + values.TryAdd(key, queryString[key]); + } + } + } + var serializer = context.RequestServices.GetRequiredService(); + if (!values.Any()) + { + return serializer.Deserialize(EmptyJsonObject); + } + var serialized = serializer.Serialize(values.ToDictionary(k => k.Key, k => k.Value)) + ?.Replace("\\\"", "\"") + .Replace("\"{", "{") + .Replace("}\"", "}") + .Replace("\"[", "[") + .Replace("]\"", "]"); + return serializer.Deserialize(serialized); + } public static Task Ok(this HttpResponse response, object data = null) { @@ -248,11 +231,16 @@ public static Task Ok(this HttpResponse response, object data = null) public static Task Created(this HttpResponse response, string location = null, object data = null) { response.StatusCode = (int)HttpStatusCode.Created; - if (!string.IsNullOrWhiteSpace(location) && !response.Headers.ContainsKey(LocationHeader)) + if (string.IsNullOrWhiteSpace(location)) + { + return Task.CompletedTask; + } + + if (!response.Headers.ContainsKey(LocationHeader)) { response.Headers.Add(LocationHeader, location); } - return data != null ? response.WriteJsonAsync(data) : Task.CompletedTask; + return data is null ? Task.CompletedTask : response.WriteJsonAsync(data); } public static Task Accepted(this HttpResponse response) @@ -298,11 +286,20 @@ public static Task InternalServerError(this HttpResponse response) } public static async Task WriteJsonAsync(this HttpResponse response, T value) - { - response.ContentType = JsonContentType; - var json = NetJSON.NetJSON.Serialize(value); - await response.WriteAsync(json); - } +{ + response.ContentType = JsonContentType; + var serializer = response.HttpContext.RequestServices.GetRequiredService(); + + // Serialize the object to a string + var serializedJson = serializer.Serialize(value); + + // Log the serialized JSON to the console + Console.WriteLine($"Serialized JSON Response: {serializedJson}"); + + // Write the serialized JSON to the response body + await response.WriteAsync(serializedJson); +} + public static T Bind(this T model, Expression> expression, object value) => model.Bind(expression, value); diff --git a/src/Paralax.WebApi/src/Paralax.WebApi/Formatters/JsonInputFormatter.cs b/src/Paralax.WebApi/src/Paralax.WebApi/Formatters/JsonInputFormatter.cs index 72f8488..291ee0b 100644 --- a/src/Paralax.WebApi/src/Paralax.WebApi/Formatters/JsonInputFormatter.cs +++ b/src/Paralax.WebApi/src/Paralax.WebApi/Formatters/JsonInputFormatter.cs @@ -5,7 +5,6 @@ using System.Reflection; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Formatters; -using NetJSON; using Open.Serialization.Json; namespace Paralax.WebApi.Formatters @@ -19,7 +18,8 @@ internal class JsonInputFormatter : IInputFormatter public JsonInputFormatter(IJsonSerializer serializer) { - _serializer = serializer; + _serializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); + _deserializeMethod = _serializer.GetType().GetMethods() .Single(m => m.IsGenericMethod && m.Name == nameof(_serializer.Deserialize)); } @@ -38,10 +38,10 @@ public async Task ReadAsync(InputFormatterContext context) } var request = context.HttpContext.Request; - var json = string.Empty; - if (request.Body is not null) + string json; + + using (var streamReader = new StreamReader(request.Body)) { - using var streamReader = new StreamReader(request.Body); json = await streamReader.ReadToEndAsync(); } @@ -50,10 +50,16 @@ public async Task ReadAsync(InputFormatterContext context) json = EmptyJson; } - var result = method.Invoke(_serializer, new object[] {json}); + try + { + var result = method.Invoke(_serializer, new object[] { json }); - return await InputFormatterResult.SuccessAsync(result); - } + return await InputFormatterResult.SuccessAsync(result); + } + catch (Exception ex) + { + throw new InvalidOperationException("Invalid JSON format", ex); + } + } } } - diff --git a/src/Paralax.WebApi/src/Paralax.WebApi/Formatters/JsonOutputFormatter.cs b/src/Paralax.WebApi/src/Paralax.WebApi/Formatters/JsonOutputFormatter.cs index b264f4f..b932434 100644 --- a/src/Paralax.WebApi/src/Paralax.WebApi/Formatters/JsonOutputFormatter.cs +++ b/src/Paralax.WebApi/src/Paralax.WebApi/Formatters/JsonOutputFormatter.cs @@ -1,7 +1,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Formatters; -using NetJSON; using Open.Serialization.Json; namespace Paralax.WebApi.Formatters @@ -28,12 +27,13 @@ public async Task WriteAsync(OutputFormatterWriteContext context) } context.HttpContext.Response.ContentType = "application/json"; + if (context.Object is string json) { await context.HttpContext.Response.WriteAsync(json); return; } - + await _serializer.SerializeAsync(context.HttpContext.Response.Body, context.Object); } } diff --git a/src/Paralax.WebApi/src/Paralax.WebApi/ParalaxFormatterResolver.cs b/src/Paralax.WebApi/src/Paralax.WebApi/ParalaxFormatterResolver.cs index c5dbb7b..0b085ae 100644 --- a/src/Paralax.WebApi/src/Paralax.WebApi/ParalaxFormatterResolver.cs +++ b/src/Paralax.WebApi/src/Paralax.WebApi/ParalaxFormatterResolver.cs @@ -10,7 +10,6 @@ internal sealed class ParalaxFormatterResolver : IJsonFormatterResolver { public static readonly IJsonFormatterResolver Instance = new ParalaxFormatterResolver(); - // Resolvers that will be used if no custom formatter is available. private static readonly IJsonFormatterResolver[] Resolvers = { StandardResolver.CamelCase, // Use CamelCase as a standard fallback resolver diff --git a/src/Paralax.WebApi/src/Paralax.WebApi/Parsers/JsonParser.cs b/src/Paralax.WebApi/src/Paralax.WebApi/Parsers/JsonParser.cs index 59c124e..7e0a7c0 100644 --- a/src/Paralax.WebApi/src/Paralax.WebApi/Parsers/JsonParser.cs +++ b/src/Paralax.WebApi/src/Paralax.WebApi/Parsers/JsonParser.cs @@ -1,79 +1,115 @@ using System; using System.Collections.Generic; -using System.IO; -using NetJSON; +using System.Text.Json; namespace Paralax.WebApi.Parsers { - // This JSON parser uses the NetJSON library for serialization/deserialization. + // JSON parser using System.Text.Json for serialization/deserialization. // It parses a JSON string into key-value pairs suitable for configuration or other dictionary-based representations. public class JsonParser { private readonly Dictionary _data = new(StringComparer.OrdinalIgnoreCase); + private readonly Stack _stack = new(); public IDictionary Parse(string json) { - try + var jsonDocumentOptions = new JsonDocumentOptions { - // Deserialize the JSON into a Dictionary - var deserializedData = NetJSON.NetJSON.Deserialize>(json); - ProcessDictionary(deserializedData, string.Empty); - } - catch (Exception ex) + CommentHandling = JsonCommentHandling.Skip, // Skip comments in JSON + AllowTrailingCommas = true // Allow trailing commas + }; + + // Parse the JSON string using System.Text.Json + using (JsonDocument doc = JsonDocument.Parse(json, jsonDocumentOptions)) { - throw new FormatException($"Failed to parse JSON: {ex.Message}", ex); + if (doc.RootElement.ValueKind != JsonValueKind.Object) + { + throw new FormatException($"Invalid top-level JSON element: {doc.RootElement.ValueKind}"); + } + + // Traverse the JSON structure + VisitElement(doc.RootElement); } return _data; } - private void ProcessDictionary(Dictionary dict, string parentKey) + // Recursively visit each JSON element + private void VisitElement(JsonElement element) { - foreach (var kvp in dict) + var isEmpty = true; + + // Process each property in the JSON object + foreach (JsonProperty property in element.EnumerateObject()) { - var currentKey = string.IsNullOrEmpty(parentKey) ? kvp.Key : $"{parentKey}.{kvp.Key}"; + isEmpty = false; + EnterContext(property.Name); + VisitValue(property.Value); + ExitContext(); + } - if (kvp.Value is Dictionary nestedDict) - { - // Recursively process nested dictionaries - ProcessDictionary(nestedDict, currentKey); - } - else if (kvp.Value is IList list) - { - // Process lists - ProcessList(list, currentKey); - } - else - { - // Add to the data dictionary - if (_data.ContainsKey(currentKey)) - { - throw new FormatException($"Duplicated key: {currentKey}"); - } - _data[currentKey] = kvp.Value?.ToString(); - } + // Handle empty objects + if (isEmpty && _stack.Count > 0) + { + _data[_stack.Peek()] = null; } } - private void ProcessList(IList list, string parentKey) + // Process each value in the JSON object or array + private void VisitValue(JsonElement value) { - for (int i = 0; i < list.Count; i++) + switch (value.ValueKind) { - var currentKey = $"{parentKey}[{i}]"; + case JsonValueKind.Object: + // If it's an object, visit its properties + VisitElement(value); + break; - if (list[i] is Dictionary nestedDict) - { - ProcessDictionary(nestedDict, currentKey); - } - else - { - if (_data.ContainsKey(currentKey)) + case JsonValueKind.Array: + // If it's an array, process each element + int index = 0; + foreach (JsonElement arrayElement in value.EnumerateArray()) { - throw new FormatException($"Duplicated key: {currentKey}"); + EnterContext(index.ToString()); + VisitValue(arrayElement); + ExitContext(); + index++; } - _data[currentKey] = list[i]?.ToString(); - } + break; + + case JsonValueKind.Number: + case JsonValueKind.String: + case JsonValueKind.True: + case JsonValueKind.False: + case JsonValueKind.Null: + var key = _stack.Peek(); + + if (_data.ContainsKey(key)) + { + throw new FormatException($"Duplicated key: {key}"); + } + + // Store the value as a string + _data[key] = value.ToString(); + break; + + default: + throw new FormatException($"Unsupported JSON token: {value.ValueKind}"); } } + + // Enter a new context in the JSON structure (i.e., navigate deeper) + private void EnterContext(string context) + { + _stack.Push(_stack.Count > 0 + ? _stack.Peek() + "." + context // Use '.' to concatenate nested keys + : context); + } + + // Exit the current context (i.e., navigate back up) + private void ExitContext() + { + _stack.Pop(); + } } } diff --git a/src/Paralax.WebApi/src/Paralax.WebApi/Utils/Extensions.cs b/src/Paralax.WebApi/src/Paralax.WebApi/Utils/Extensions.cs index fa3c3cb..d8a7e75 100644 --- a/src/Paralax.WebApi/src/Paralax.WebApi/Utils/Extensions.cs +++ b/src/Paralax.WebApi/src/Paralax.WebApi/Utils/Extensions.cs @@ -4,13 +4,11 @@ using System.Linq; using System.Reflection; using System.Runtime.Serialization; -using System.Text.RegularExpressions; namespace Paralax.WebApi.Utils { public static class Extensions { - private static readonly Regex JsDateRegex = new Regex(@"\/Date\((\d+)(?:[+-]\d+)?\)\/", RegexOptions.Compiled); // Returns the default instance of the specified type public static object GetDefaultInstance(this Type type) { @@ -281,13 +279,6 @@ public static object ConvertToExpectedType(Type targetType, object value) } } - // Date handling - if (targetType == typeof(DateTime)) - { - return ParseDateTime(value); - } - - // Handle nullable types if (Nullable.GetUnderlyingType(targetType) != null) { @@ -334,33 +325,5 @@ public static T ConvertTo(this object value) return instance; } - - public static DateTime? ParseDateTime(object value) - { - if (value == null) return null; - - // Check for JS-style date \/Date(...) - var stringValue = value.ToString(); - var match = JsDateRegex.Match(stringValue); - if (match.Success && long.TryParse(match.Groups[1].Value, out var unixTimeMs)) - { - // Convert milliseconds since Unix epoch - return DateTimeOffset.FromUnixTimeMilliseconds(unixTimeMs).UtcDateTime; - } - - // Try ISO 8601 date parsing - if (DateTime.TryParse(stringValue, null, System.Globalization.DateTimeStyles.RoundtripKind, out var parsedDate)) - { - return parsedDate; - } - - // Try parsing as Unix timestamp - if (long.TryParse(stringValue, out var unixTimeSeconds)) - { - return DateTimeOffset.FromUnixTimeSeconds(unixTimeSeconds).UtcDateTime; - } - - return null; - } } } From 6707703a9057cf815c74e735a1257f85eb1355e9 Mon Sep 17 00:00:00 2001 From: Andrii Voznesenskyi Date: Thu, 3 Oct 2024 01:58:43 +0200 Subject: [PATCH 04/11] (#119) cqrs web api: update dispather endpoints --- .../Builders/DispatcherEndpointsBuilder.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/Builders/DispatcherEndpointsBuilder.cs b/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/Builders/DispatcherEndpointsBuilder.cs index b2fbde1..98faee1 100644 --- a/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/Builders/DispatcherEndpointsBuilder.cs +++ b/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/Builders/DispatcherEndpointsBuilder.cs @@ -137,11 +137,17 @@ private static async Task BuildCommandContext(T command, HttpContext context, private static async Task WriteJsonAsync(HttpResponse response, object result) { + NetJSON.NetJSON.DateFormat = NetJSON.NetJSONDateFormat.ISO; + NetJSON.NetJSON.SkipDefaultValue = false; + response.ContentType = "application/json"; + var json = NetJSON.NetJSON.Serialize(result); + await response.WriteAsync(json); } + private static async Task ReadJsonAsync(HttpContext context) where T : class { var body = await new StreamReader(context.Request.Body).ReadToEndAsync(); From 5a78d3f07b920484b1450546a9010c75adaacb12 Mon Sep 17 00:00:00 2001 From: Andrii Voznesenskyi Date: Thu, 3 Oct 2024 02:06:36 +0200 Subject: [PATCH 05/11] (#119) cqrs web api: update http clinet serialization [pack-all-force] --- .../src/Paralax.HTTP/NetJsonHttpClientSerializer.cs | 8 +++++--- src/Paralax.HTTP/src/Paralax.HTTP/ParalaxHttpClient.cs | 10 ++-------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Paralax.HTTP/src/Paralax.HTTP/NetJsonHttpClientSerializer.cs b/src/Paralax.HTTP/src/Paralax.HTTP/NetJsonHttpClientSerializer.cs index 60c7c89..981ffc6 100644 --- a/src/Paralax.HTTP/src/Paralax.HTTP/NetJsonHttpClientSerializer.cs +++ b/src/Paralax.HTTP/src/Paralax.HTTP/NetJsonHttpClientSerializer.cs @@ -12,9 +12,11 @@ public NetJsonHttpClientSerializer(NetJSONSettings settings = null) { _settings = settings ?? new NetJSONSettings { - UseEnumString = true, - CaseSensitive = false, - SkipDefaultValue = true + UseEnumString = true, + CaseSensitive = false, + SkipDefaultValue = false, + DateFormat = NetJSONDateFormat.ISO, + TimeZoneFormat = NetJSONTimeZoneFormat.Utc }; } diff --git a/src/Paralax.HTTP/src/Paralax.HTTP/ParalaxHttpClient.cs b/src/Paralax.HTTP/src/Paralax.HTTP/ParalaxHttpClient.cs index ad3e309..22baec7 100644 --- a/src/Paralax.HTTP/src/Paralax.HTTP/ParalaxHttpClient.cs +++ b/src/Paralax.HTTP/src/Paralax.HTTP/ParalaxHttpClient.cs @@ -180,6 +180,7 @@ private async Task> CreateHttpResult(HttpResponseMessage respon var stream = await response.Content.ReadAsStreamAsync(); serializer ??= _serializer; var result = await serializer.DeserializeAsync(stream); + Console.WriteLine($"Deserialized Result: {result}"); return new HttpResult(result, response); } @@ -198,7 +199,6 @@ private StringContent GetJsonPayload(object data, IHttpClientSerializer serializ return content; } - // Retry policy using Polly private async Task RetryPolicy(Func> action) { return await Policy @@ -209,22 +209,18 @@ private async Task RetryPolicy(Func> action) protected virtual async Task> SendResultAsync(string uri, Method method, HttpContent content = null, IHttpClientSerializer serializer = null) { - // Sending the request using the method and content var response = await SendAsync(uri, method, content); - // If the response status code is not successful, return a default result if (!response.IsSuccessStatusCode) { return new HttpResult(default, response); } - // Read the response content stream var stream = await response.Content.ReadAsStreamAsync(); - // Deserialize the stream using the provided serializer (or default to _serializer) var result = await DeserializeJsonFromStream(stream, serializer); + Console.WriteLine($"Deserialized Result: {result}"); - // Return the deserialized result along with the response return new HttpResult(result, response); } @@ -235,7 +231,6 @@ private async Task DeserializeJsonFromStream(Stream stream, IHttpClientSer return default; } - // Use the provided serializer or default to _serializer serializer ??= _serializer; return await serializer.DeserializeAsync(stream); @@ -253,7 +248,6 @@ public enum Method } } - // Extension method for converting Method enum to HttpMethod internal static class MethodExtensions { public static HttpMethod ToHttpMethod(this ParalaxHttpClient.Method method) From 374d0afefa4fd98042e869657c9744d4fd45ad64 Mon Sep 17 00:00:00 2001 From: Andrii Voznesenskyi Date: Thu, 3 Oct 2024 12:54:52 +0200 Subject: [PATCH 06/11] (#121) http: add MessagePacksrializer [pack-all-force] --- .../src/Paralax.HTTP/Extensions.cs | 2 +- .../MessagePackHttpClientSerializer.cs | 42 +++++++++++++++++++ .../src/Paralax.HTTP/Paralax.HTTP.csproj | 2 + 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/Paralax.HTTP/src/Paralax.HTTP/MessagePackHttpClientSerializer.cs diff --git a/src/Paralax.HTTP/src/Paralax.HTTP/Extensions.cs b/src/Paralax.HTTP/src/Paralax.HTTP/Extensions.cs index a93c06a..8f9ebbd 100644 --- a/src/Paralax.HTTP/src/Paralax.HTTP/Extensions.cs +++ b/src/Paralax.HTTP/src/Paralax.HTTP/Extensions.cs @@ -58,7 +58,7 @@ public static IParalaxBuilder AddHttpClient(this IParalaxBuilder builder, string } builder.Services.AddSingleton(options); - builder.Services.AddSingleton(); + builder.Services.AddSingleton(); var clientBuilder = builder.Services.AddHttpClient(clientName); httpClientBuilder?.Invoke(clientBuilder); diff --git a/src/Paralax.HTTP/src/Paralax.HTTP/MessagePackHttpClientSerializer.cs b/src/Paralax.HTTP/src/Paralax.HTTP/MessagePackHttpClientSerializer.cs new file mode 100644 index 0000000..d075ca1 --- /dev/null +++ b/src/Paralax.HTTP/src/Paralax.HTTP/MessagePackHttpClientSerializer.cs @@ -0,0 +1,42 @@ +using System.IO; +using System.Threading.Tasks; +using MessagePack; +using MessagePack.Resolvers; + +namespace Paralax.HTTP +{ + public class MessagePackHttpClientSerializer : IHttpClientSerializer + { + private readonly MessagePackSerializerOptions _options; + + public MessagePackHttpClientSerializer(MessagePackSerializerOptions options = null) + { + _options = options ?? MessagePackSerializerOptions.Standard + .WithResolver(ContractlessStandardResolver.Instance) + .WithCompression(MessagePackCompression.Lz4BlockArray) + .WithSecurity(MessagePackSecurity.UntrustedData); + } + + public string Serialize(T value) + { + var bytes = MessagePackSerializer.Serialize(value, _options); + return System.Convert.ToBase64String(bytes); + } + + public async Task SerializeAsync(Stream stream, T value) + { + await MessagePackSerializer.SerializeAsync(stream, value, _options); + } + + public async ValueTask DeserializeAsync(Stream stream) + { + return await MessagePackSerializer.DeserializeAsync(stream, _options); + } + + public T Deserialize(string base64) + { + var bytes = System.Convert.FromBase64String(base64); + return MessagePackSerializer.Deserialize(bytes, _options); + } + } +} diff --git a/src/Paralax.HTTP/src/Paralax.HTTP/Paralax.HTTP.csproj b/src/Paralax.HTTP/src/Paralax.HTTP/Paralax.HTTP.csproj index 48c6e02..cc85ffb 100644 --- a/src/Paralax.HTTP/src/Paralax.HTTP/Paralax.HTTP.csproj +++ b/src/Paralax.HTTP/src/Paralax.HTTP/Paralax.HTTP.csproj @@ -35,6 +35,8 @@ \ + + From 29cfc6f24aa0d95057c2da6d639e3ed8453b1633 Mon Sep 17 00:00:00 2001 From: Andrii Voznesenskyi Date: Thu, 3 Oct 2024 18:51:09 +0200 Subject: [PATCH 07/11] (#121) http: udpate deserialization netJson type [pack-all-force] --- src/Paralax.HTTP/src/Paralax.HTTP/Extensions.cs | 2 +- .../src/Paralax.HTTP/NetJsonHttpClientSerializer.cs | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Paralax.HTTP/src/Paralax.HTTP/Extensions.cs b/src/Paralax.HTTP/src/Paralax.HTTP/Extensions.cs index 8f9ebbd..a93c06a 100644 --- a/src/Paralax.HTTP/src/Paralax.HTTP/Extensions.cs +++ b/src/Paralax.HTTP/src/Paralax.HTTP/Extensions.cs @@ -58,7 +58,7 @@ public static IParalaxBuilder AddHttpClient(this IParalaxBuilder builder, string } builder.Services.AddSingleton(options); - builder.Services.AddSingleton(); + builder.Services.AddSingleton(); var clientBuilder = builder.Services.AddHttpClient(clientName); httpClientBuilder?.Invoke(clientBuilder); diff --git a/src/Paralax.HTTP/src/Paralax.HTTP/NetJsonHttpClientSerializer.cs b/src/Paralax.HTTP/src/Paralax.HTTP/NetJsonHttpClientSerializer.cs index 981ffc6..0b1b719 100644 --- a/src/Paralax.HTTP/src/Paralax.HTTP/NetJsonHttpClientSerializer.cs +++ b/src/Paralax.HTTP/src/Paralax.HTTP/NetJsonHttpClientSerializer.cs @@ -36,7 +36,12 @@ public async ValueTask DeserializeAsync(Stream stream) using (var reader = new StreamReader(stream)) { var content = await reader.ReadToEndAsync(); - return NetJSON.NetJSON.Deserialize(content, _settings); + var result = typeof(NetJSON.NetJSON) + .GetMethod("Deserialize") + .MakeGenericMethod(typeof(T)) + .Invoke(null, new object[] { content, _settings }); + + return (T)result; } } From 7fbd02b64dae183fea18c6d210279d245f9dc595 Mon Sep 17 00:00:00 2001 From: Andrii Voznesenskyi Date: Thu, 3 Oct 2024 19:09:00 +0200 Subject: [PATCH 08/11] (#121) http: udpate deserialization netJson type [pack-all-force] --- .../NetJsonHttpClientSerializer.cs | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/Paralax.HTTP/src/Paralax.HTTP/NetJsonHttpClientSerializer.cs b/src/Paralax.HTTP/src/Paralax.HTTP/NetJsonHttpClientSerializer.cs index 0b1b719..85d2f49 100644 --- a/src/Paralax.HTTP/src/Paralax.HTTP/NetJsonHttpClientSerializer.cs +++ b/src/Paralax.HTTP/src/Paralax.HTTP/NetJsonHttpClientSerializer.cs @@ -12,21 +12,26 @@ public NetJsonHttpClientSerializer(NetJSONSettings settings = null) { _settings = settings ?? new NetJSONSettings { - UseEnumString = true, - CaseSensitive = false, - SkipDefaultValue = false, + UseEnumString = true, + CaseSensitive = false, + SkipDefaultValue = false, DateFormat = NetJSONDateFormat.ISO, TimeZoneFormat = NetJSONTimeZoneFormat.Utc }; } - public string Serialize(T value) => NetJSON.NetJSON.Serialize(value, _settings); + public string Serialize(T value) + { + return NetJSON.NetJSON.Serialize(value, _settings); + } public async Task SerializeAsync(Stream stream, T value) { - using (var writer = new StreamWriter(stream)) + var json = NetJSON.NetJSON.Serialize(value, _settings); + + using (var writer = new StreamWriter(stream)) { - await writer.WriteAsync(NetJSON.NetJSON.Serialize(value, _settings)); + await writer.WriteAsync(json); await writer.FlushAsync(); } } @@ -36,15 +41,16 @@ public async ValueTask DeserializeAsync(Stream stream) using (var reader = new StreamReader(stream)) { var content = await reader.ReadToEndAsync(); - var result = typeof(NetJSON.NetJSON) - .GetMethod("Deserialize") - .MakeGenericMethod(typeof(T)) - .Invoke(null, new object[] { content, _settings }); - return (T)result; + var result = NetJSON.NetJSON.Deserialize(content, _settings); + + return result; } } - public T Deserialize(string json) => NetJSON.NetJSON.Deserialize(json, _settings); + public T Deserialize(string json) + { + return NetJSON.NetJSON.Deserialize(json, _settings); + } } } From cf67a8ec9f6bf308459c2e54fa672a7efd3f029a Mon Sep 17 00:00:00 2001 From: Andrii Voznesenskyi Date: Thu, 3 Oct 2024 21:05:53 +0200 Subject: [PATCH 09/11] (#121) cqrs: queries udpate query dispatcher [pack-all-force] --- .../Dispatchers/QueryDispatcher.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Paralax.CQRS.Queries/src/Paralax.CQRS.Queries/Dispatchers/QueryDispatcher.cs b/src/Paralax.CQRS.Queries/src/Paralax.CQRS.Queries/Dispatchers/QueryDispatcher.cs index 890d1f4..bb84eb2 100644 --- a/src/Paralax.CQRS.Queries/src/Paralax.CQRS.Queries/Dispatchers/QueryDispatcher.cs +++ b/src/Paralax.CQRS.Queries/src/Paralax.CQRS.Queries/Dispatchers/QueryDispatcher.cs @@ -19,22 +19,22 @@ public async Task QueryAsync(IQuery query, Cancellati using var scope = _serviceProvider.CreateScope(); var handlerType = typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult)); var handler = scope.ServiceProvider.GetRequiredService(handlerType); - - var handleAsyncMethod = handlerType.GetMethod(nameof(IQueryHandler, TResult>.HandleAsync)); + // We get the 'HandleAsync' method directly from the handler + var handleAsyncMethod = handlerType.GetMethod(nameof(IQueryHandler, TResult>.HandleAsync)); if (handleAsyncMethod == null) { throw new InvalidOperationException($"Handler for query '{query.GetType().Name}' does not contain a valid 'HandleAsync' method."); } - var resultTask = (Task?)handleAsyncMethod.Invoke(handler, new object[] { query, cancellationToken }); + var taskResult = handleAsyncMethod.Invoke(handler, new object[] { query, cancellationToken }); - if (resultTask == null) + if (taskResult is Task resultTask) { - throw new InvalidOperationException($"HandleAsync method for '{query.GetType().Name}' returned null."); + return await resultTask; } - return await resultTask; + throw new InvalidOperationException($"HandleAsync method for '{query.GetType().Name}' returned an invalid result."); } public async Task QueryAsync(TQuery query, CancellationToken cancellationToken = default) From 7ea3e4a906cfdd82e01bd6ebc7cce11a2a18fa91 Mon Sep 17 00:00:00 2001 From: Andrii Voznesenskyi Date: Thu, 3 Oct 2024 21:26:50 +0200 Subject: [PATCH 10/11] (#121) cqrs: queries udpate query dispatcher [pack-all-force] --- .../Dispatchers/QueryDispatcher.cs | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/Paralax.CQRS.Queries/src/Paralax.CQRS.Queries/Dispatchers/QueryDispatcher.cs b/src/Paralax.CQRS.Queries/src/Paralax.CQRS.Queries/Dispatchers/QueryDispatcher.cs index bb84eb2..74c7cc6 100644 --- a/src/Paralax.CQRS.Queries/src/Paralax.CQRS.Queries/Dispatchers/QueryDispatcher.cs +++ b/src/Paralax.CQRS.Queries/src/Paralax.CQRS.Queries/Dispatchers/QueryDispatcher.cs @@ -22,19 +22,22 @@ public async Task QueryAsync(IQuery query, Cancellati // We get the 'HandleAsync' method directly from the handler var handleAsyncMethod = handlerType.GetMethod(nameof(IQueryHandler, TResult>.HandleAsync)); - if (handleAsyncMethod == null) - { - throw new InvalidOperationException($"Handler for query '{query.GetType().Name}' does not contain a valid 'HandleAsync' method."); - } - - var taskResult = handleAsyncMethod.Invoke(handler, new object[] { query, cancellationToken }); - - if (taskResult is Task resultTask) - { - return await resultTask; - } - - throw new InvalidOperationException($"HandleAsync method for '{query.GetType().Name}' returned an invalid result."); + // if (handleAsyncMethod == null) + // { + // throw new InvalidOperationException($"Handler for query '{query.GetType().Name}' does not contain a valid 'HandleAsync' method."); + // } + + // var taskResult = handleAsyncMethod.Invoke(handler, new object[] { query, cancellationToken }); + + // if (taskResult is Task resultTask) + // { + // return await resultTask; + // } + + // throw new InvalidOperationException($"HandleAsync method for '{query.GetType().Name}' returned an invalid result."); + return await (Task) handlerType + .GetMethod(nameof(IQueryHandler, TResult>.HandleAsync))? + .Invoke(handler, new object[] {query, cancellationToken}); } public async Task QueryAsync(TQuery query, CancellationToken cancellationToken = default) From 3d1ee48e397011cb7257173dc5cb9bf7512f9ef4 Mon Sep 17 00:00:00 2001 From: Andrii Voznesenskyi Date: Thu, 3 Oct 2024 21:47:21 +0200 Subject: [PATCH 11/11] (#121) webapi cqrs: update namespaces [pack-all-force] --- .../Builders/DispatcherEndpointsBuilder.cs | 7 +++++-- .../src/Paralax.CQRS.WebApi/Extensions.cs | 6 +++--- .../src/Paralax.CQRS.WebApi/IDispatcherEndpointsBuilder.cs | 2 +- .../Middlewares/PublicContractsMiddleware.cs | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/Builders/DispatcherEndpointsBuilder.cs b/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/Builders/DispatcherEndpointsBuilder.cs index 98faee1..8c6a014 100644 --- a/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/Builders/DispatcherEndpointsBuilder.cs +++ b/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/Builders/DispatcherEndpointsBuilder.cs @@ -6,8 +6,10 @@ using NetJSON; using Paralax.CQRS.Commands; using Paralax.CQRS.Queries; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; -namespace Paralax.WebApi.CQRS.Builders +namespace Paralax.CQRS.WebApi.Builders { public class DispatcherEndpointsBuilder : IDispatcherEndpointsBuilder { @@ -49,7 +51,8 @@ public IDispatcherEndpointsBuilder Get(string path, return; } - await WriteJsonAsync(ctx.Response, result); + // await WriteJsonAsync(ctx.Response, result); + await ctx.Response.WriteJsonAsync(result); return; } diff --git a/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/Extensions.cs b/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/Extensions.cs index 7e513b5..bd17d54 100644 --- a/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/Extensions.cs +++ b/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/Extensions.cs @@ -5,10 +5,10 @@ using Microsoft.Extensions.DependencyInjection; using Paralax.CQRS.Commands; using Paralax.CQRS.Queries; +using Paralax.CQRS.WebApi.Builders; +using Paralax.CQRS.WebApi.Middlewares; using Paralax.WebApi; -using Paralax.WebApi.CQRS; -using Paralax.WebApi.CQRS.Builders; -using Paralax.WebApi.CQRS.Middlewares; + namespace Paralax.CQRS.WebApi { diff --git a/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/IDispatcherEndpointsBuilder.cs b/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/IDispatcherEndpointsBuilder.cs index 2f8a64e..d4d25cd 100644 --- a/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/IDispatcherEndpointsBuilder.cs +++ b/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/IDispatcherEndpointsBuilder.cs @@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; -namespace Paralax.WebApi.CQRS +namespace Paralax.CQRS.WebApi { public interface IDispatcherEndpointsBuilder { diff --git a/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/Middlewares/PublicContractsMiddleware.cs b/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/Middlewares/PublicContractsMiddleware.cs index 0f54aa9..14efd5d 100644 --- a/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/Middlewares/PublicContractsMiddleware.cs +++ b/src/Paralax.CQRS.WebApi/src/Paralax.CQRS.WebApi/Middlewares/PublicContractsMiddleware.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; using System.Reflection; -namespace Paralax.WebApi.CQRS.Middlewares +namespace Paralax.CQRS.WebApi.Middlewares { public class PublicContractsMiddleware {