For - <TargetFramework>net7.0</TargetFramework>
or newer.
// https://stackoverflow.com/a/79718639
using System.Linq.Expressions;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters;
namespace XXX.XXX;
public class JsonKeys<T> where T : class
{
private string[] _jsonKeys = Array.Empty<string>();
public bool HasKey<TReturn>(Expression<Func<T, TReturn>> expression) =>
expression.Body is MemberExpression { Member.MemberType: MemberTypes.Property } memberExpression &&
_jsonKeys.Contains(memberExpression.Member.Name, StringComparer.InvariantCultureIgnoreCase);
public void SetJsonKeys(string[] jsonKeys)
{
if (_jsonKeys.Length > 0) throw new InvalidOperationException($"{nameof(jsonKeys)} is already set");
_jsonKeys = jsonKeys;
}
}
public class JsonKeysInputFormatter : SystemTextJsonInputFormatter
{
public JsonKeysInputFormatter(
JsonOptions options,
ILogger<SystemTextJsonInputFormatter> logger
) : base(options, logger)
{
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
var httpContext = context.HttpContext;
var memoryStream = new MemoryStream();
await httpContext.Request.Body.CopyToAsync(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
httpContext.Request.Body = memoryStream;
var inputFormatterResult = await base.ReadRequestBodyAsync(context);
memoryStream.Seek(0, SeekOrigin.Begin);
var jsonNode = await JsonSerializer.DeserializeAsync<JsonNode>(memoryStream, SerializerOptions);
AddKeyExistence(inputFormatterResult.Model, jsonNode);
await memoryStream.DisposeAsync();
return inputFormatterResult;
}
private static void AddKeyExistence(object? model, JsonNode? jsonNode)
{
if (model == null || jsonNode == null || model.GetType().IsArray || !model.GetType().IsClass) return;
// https://learn.microsoft.com/dotnet/fundamentals/reflection/how-to-examine-and-instantiate-generic-types-with-reflection
var modelsGenericType = model.GetType().BaseType?.GetGenericArguments().SingleOrDefault();
if (modelsGenericType == null) return;
if (!model.GetType().IsAssignableTo(typeof(JsonKeys<>).MakeGenericType(modelsGenericType))) return;
var setJsonKeysMethod = typeof(JsonKeys<>).GetMethod(nameof(JsonKeys<object>.SetJsonKeys));
if (setJsonKeysMethod == null) return;
var modelSetJsonKeysMethod = model.GetType().GetMethod(
setJsonKeysMethod.Name,
BindingFlags.Public | BindingFlags.Instance,
setJsonKeysMethod.GetParameters().Select(a => a.ParameterType).ToArray()
);
if (modelSetJsonKeysMethod == null) return;
JsonObject jsonObject;
try
{
jsonObject = jsonNode.AsObject();
}
catch (Exception e)
{
Console.WriteLine(e);
return;
}
var modelType = model.GetType();
foreach (var propertyInfo in modelType.GetProperties())
{
if (propertyInfo.GetMethod == null) continue;
var getValue = propertyInfo.GetMethod.Invoke(model, null);
if (!jsonObject.TryGetPropertyValue(propertyInfo.Name, out var value)) continue;
AddKeyExistence(getValue, value);
}
modelSetJsonKeysMethod.Invoke(model, new object[] { jsonObject.Select(a => a.Key).ToArray() });
}
}
Step 2 of 3
// https://stackoverflow.com/a/79718639
using dotenv.net;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Options;
WebApplication? app = null;
var builder = WebApplication.CreateBuilder(args);
// ...
builder.Services.AddControllers(mvcOptions =>
{
ArgumentNullException.ThrowIfNull(app);
var loggerFactory = app.Services.GetRequiredService<ILoggerFactory>();
var jsonOptions = app.Services.GetRequiredService<IOptions<JsonOptions>>();
// https://learn.microsoft.com/aspnet/core/web-api/advanced/custom-formatters?view=aspnetcore-7.0
mvcOptions.InputFormatters.Insert(0,
new JsonKeysInputFormatter(jsonOptions.Value, loggerFactory.CreateLogger<SystemTextJsonInputFormatter>()));
});
// ...
app = builder.Build();
Step 3 of 3
// https://stackoverflow.com/a/79718639
using XXX.XXX;
using Microsoft.AspNetCore.Mvc;
namespace YYY.YYY.YYY;
public class Nested : JsonKeys<Nested>
{
public string? NestedOne { get; set; }
}
public class MyRequest : JsonKeys<MyRequest>
{
public string? One { get; set; }
public string? Two { get; set; }
public string? Three { get; set; }
public Nested? Nested { get; set; }
public int[]? Four { get; set; }
}
[ApiController]
[Route("api/dev")]
public class DevTestJsonKeysController : ControllerBase
{
[HttpPost(nameof(TestJsonKeys))]
public async Task<IActionResult> TestJsonKeys([FromBody] MyRequest request)
{
request.HasKey(x => x.Nested);
request.Nested?.HasKey(x => x.NestedOne);
return Ok();
}
}
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4