You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core:自定义JsonConverter如何抛出多错误或访问ModelState?

解决方案:通过自定义模型绑定器实现多错误收集并写入ModelState

由于JsonConverter属于System.Text.Json序列化组件,本身无法直接访问ASP.NET Core的ModelState(两者上下文隔离),所以要实现无需包装类、直接将多个反序列化错误写入ModelState的需求,最直接的方式是用自定义模型绑定器替代原有转换器的逻辑,在绑定流程中完成反序列化重试、错误收集,并将错误注入ModelState。

具体实现步骤

1. 实现自定义模型绑定器

这个绑定器会执行你的反序列化重试逻辑,收集所有错误后直接添加到ModelState:

using Microsoft.AspNetCore.Mvc.ModelBinding;
using System.Text.Json;

public class ErrorCollectingModelBinder<T> : IModelBinder where T : class, new()
{
    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
            throw new ArgumentNullException(nameof(bindingContext));

        // 读取请求JSON内容
        var request = bindingContext.HttpContext.Request;
        using var streamReader = new StreamReader(request.Body);
        var rawJson = await streamReader.ReadToEndAsync();

        // 先验证JSON格式合法性
        JsonDocument jsonDoc;
        try
        {
            jsonDoc = JsonDocument.Parse(rawJson);
        }
        catch (JsonException ex)
        {
            bindingContext.ModelState.TryAddModelError(string.Empty, $"JSON格式无效: {ex.Message}");
            bindingContext.Result = ModelBindingResult.Failed();
            return;
        }

        // 适配你原逻辑中的"model"属性提取(如果不需要可直接用root)
        var rootElement = jsonDoc.RootElement;
        var targetElement = rootElement.TryGetProperty("model", out var modelProp) ? modelProp : rootElement;

        var jsonObject = JsonObject.Create(targetElement);
        var errors = new List<(string PropertyPath, string Message)>();
        var targetModel = new T();

        // 循环重试反序列化,直到成功或无法继续修复
        while (true)
        {
            try
            {
                targetModel = JsonSerializer.Deserialize<T>(jsonObject, new JsonSerializerOptions())!;
                break;
            }
            catch (JsonException ex)
            {
                var propertyPath = ExtractPropertyPathFromError(ex.Message);
                if (string.IsNullOrEmpty(propertyPath))
                {
                    errors.Add((string.Empty, ex.Message));
                    break;
                }

                // 收集错误(属性路径+错误消息)
                errors.Add((propertyPath, ex.Message));
                
                // 移除问题属性,重试反序列化
                if (!jsonObject.Remove(propertyPath))
                {
                    // 无法移除属性,避免死循环,退出
                    break;
                }
            }
        }

        // 将错误写入ModelState
        foreach (var error in errors)
        {
            // 转换路径格式(比如将$.[Age]转为Age,根据你的需求调整)
            var modelStateKey = error.PropertyPath.Replace("$.", "", StringComparison.OrdinalIgnoreCase);
            bindingContext.ModelState.TryAddModelError(modelStateKey, error.Message);
        }

        // 设置绑定成功的模型
        bindingContext.Result = ModelBindingResult.Success(targetModel);
    }

    // 从JsonException消息中提取属性路径(适配你的错误格式)
    private string ExtractPropertyPathFromError(string errorMessage)
    {
        var pathMarkerIndex = errorMessage.IndexOf("Path: ", StringComparison.OrdinalIgnoreCase);
        if (pathMarkerIndex == -1) return string.Empty;

        var endIndex = errorMessage.IndexOf(" | ", pathMarkerIndex, StringComparison.OrdinalIgnoreCase);
        endIndex = endIndex == -1 ? errorMessage.Length : endIndex;

        return errorMessage.Substring(pathMarkerIndex + 6, endIndex - pathMarkerIndex - 6).Trim();
    }
}

2. 给目标模型绑定自定义绑定器

在你需要反序列化的模型类上添加[ModelBinder]属性,指定使用上面的绑定器:

using Microsoft.AspNetCore.Mvc;

[ModelBinder(typeof(ErrorCollectingModelBinder<YourTargetModel>))]
public class YourTargetModel
{
    public int Id { get; set; }
    public string Username { get; set; }
    public int Age { get; set; }
    // 其他业务属性
}

3. 控制器中直接使用模型

在API接口中直接将模型作为参数,ModelState会自动包含所有反序列化错误:

[ApiController]
[Route("api/[controller]")]
public class DemoController : ControllerBase
{
    [HttpPost]
    public IActionResult Create(YourTargetModel model)
    {
        if (!ModelState.IsValid)
        {
            // 直接返回包含所有错误的ModelState
            return BadRequest(ModelState);
        }

        // 正常处理业务逻辑
        return Ok(model);
    }
}

为什么不推荐在JsonConverter中实现?

JsonConverter的设计目标是处理序列化/反序列化的格式转换,它属于通用组件,不依赖ASP.NET Core的上下文,因此无法直接访问ModelState。强行通过静态变量、依赖注入传递上下文会导致代码耦合度高、线程安全问题,不符合组件设计原则。

内容的提问来源于stack exchange,提问作者SventoryMang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 03:00:07