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

GraphQL-dotnet字段解析前验证问题:无法获取输入值

问题分析与解决方案

我来帮你拆解下遇到的两个核心问题:context.Inputs始终为null,以及控制器中query.Variables.ToInputs()返回null,这其实分别和变量绑定逻辑、GraphQL.NET 2.4.0的验证阶段生命周期有关,下面逐个解决:


一、解决query.Variables.ToInputs()返回null的问题

这个问题大概率是你的GraphQLQuery类定义或者前端请求格式不正确导致的:

1. 检查GraphQLQuery类定义

确保它能正确绑定前端传入的variables字段,示例定义如下:

public class GraphQLQuery
{
    public string Query { get; set; }
    public string OperationName { get; set; }
    // 用JObject或Dictionary<string, object>都可以,要匹配JSON格式的变量
    public JObject Variables { get; set; }
}

2. 确认前端请求格式

前端发送的JSON请求体必须包含variables字段(即使无变量也要传{}),比如:

{
  "query": "mutation($input: UserInput!) { createUser(input: $input) { id } }",
  "operationName": "createUser",
  "variables": { "input": { "phone": "1234567890" } }
}

如果前端没传variables或者格式错误,query.Variables会为null,进而导致ToInputs()返回null。


二、解决自定义IValidationRule中无法获取变量值的问题

在GraphQL.NET 2.4.0中,验证阶段是在变量解析之前执行的,框架默认不会把解析后的变量值传入ValidationContext,所以context.Inputs自然是null。

这里有两种解决方案,优先推荐第一种:

方案1:优先选择——在InputObjectGraphType字段上直接添加验证

这种方式更简洁,符合GraphQL.NET的设计逻辑,不需要处理复杂的AST遍历,直接在字段定义时嵌入验证:

public class UserInputType : InputObjectGraphType
{
    public UserInputType()
    {
        Name = "UserInput";
        
        Field<StringGraphType>("phone")
            // 保留你的元数据标识(如果需要其他逻辑识别)
            .Metadata(nameof(PhoneNumberValidationRule), true)
            // 添加字段级验证逻辑
            .Validate(context =>
            {
                var phoneValue = context.Value as string;
                // 这里可以直接拿到字段的实际传入值
                if (!string.IsNullOrEmpty(phoneValue) && !phoneValue.IsValidPhoneNumber())
                {
                    return new ValidationError(
                        "InvalidPhoneNumber", 
                        "The supplied phone number is not valid.",
                        context.FieldName);
                }
                // 验证通过返回null
                return null;
            });
            
        // 其他字段定义...
    }
}

这种方式会在字段解析时自动触发验证,错误会被收集到查询结果的errors列表中,完全符合你的需求。

方案2:手动传递解析后的变量到验证规则

如果你必须在IValidationRule中统一处理验证,需要先在控制器中解析变量,再把解析后的Inputs传递给验证规则:

1. 修改PhoneNumberValidationRule,添加接收变量的属性

public class PhoneNumberValidationRule : IValidationRule
{
    // 添加这个属性,接收控制器传递的解析后变量
    public Inputs Inputs { get; set; }

    public INodeVisitor Validate(ValidationContext context)
    {
        return new EnterLeaveListener(_ =>
        {
            _.Match<Argument>(argAst =>
            {
                var argDef = context.TypeInfo.GetArgument();
                if (argDef == null) return;
                
                var inputType = argDef.ResolvedType.GetNamedType() as InputObjectGraphType;
                if (inputType == null) return;
                
                // 递归收集所有嵌套的输入字段(修正你之前的字段收集逻辑)
                var allFields = new List<FieldType>();
                CollectInputFields(inputType, allFields);
                
                // 筛选带指定元数据的字段
                foreach (var field in allFields.Where(f => f.HasMetadata(nameof(PhoneNumberValidationRule))))
                {
                    // 从传递的Inputs中获取对应值
                    var argValue = Inputs?.GetValue(argAst.Name.Value);
                    var fieldValue = argValue?[field.Name];
                    
                    if (fieldValue != null)
                    {
                        var phoneNumber = fieldValue.ToString();
                        if (!phoneNumber.IsValidPhoneNumber())
                        {
                            context.ReportError(new ValidationError(
                                context.OriginalQuery,
                                "InvalidPhoneNumber",
                                "The supplied phone number is not valid.",
                                argAst));
                        }
                    }
                }
            });
        });
    }

    // 递归收集所有输入字段,处理NonNull和List嵌套
    private void CollectInputFields(IComplexGraphType inputType, List<FieldType> fields)
    {
        foreach (var field in inputType.Fields)
        {
            fields.Add(field);
            
            var resolvedType = field.ResolvedType.GetNamedType();
            if (resolvedType is IComplexGraphType nestedInputType)
            {
                CollectInputFields(nestedInputType, fields);
            }
        }
    }
}

2. 在控制器中传递解析后的变量到验证规则

[Route("graphql")]
[HttpPost]
public async Task<IActionResult> PostAsync([FromBody]GraphQLQuery query) {
    if (!ModelState.IsValid || query == null) {
        return Json("Проблем в формата на изпратената на заявка!");
    }
    
    // 解析变量(现在应该能正常返回值,前提是GraphQLQuery定义正确)
    var inputs = query.Variables?.ToInputs() ?? new Inputs();
    
    // 给自定义验证规则传递解析后的Inputs
    var validationRules = DocumentValidator.CoreRules()
        .Concat(_validationRules.Select(rule => {
            if (rule is PhoneNumberValidationRule phoneRule)
            {
                phoneRule.Inputs = inputs;
            }
            return rule;
        }))
        .ToList();
        
    var result = await _executer.ExecuteAsync(o => {
        o.Schema = _schema;
        o.Query = query.Query;
        o.OperationName = query.OperationName;
        o.Inputs = inputs;
        o.ExposeExceptions = true;
        o.EnableMetrics = true;
        o.ComplexityConfiguration = new GraphQL.Validation.Complexity.ComplexityConfiguration { MaxDepth = 15 };
        o.UserContext = new GraphQLUserContext { User = User };
        o.ValidationRules = validationRules;
        o.Listeners.Add(_dataLoaderDocumentListener);
    }).ConfigureAwait(false);

    if (result.Errors?.Count > 0) {
        _logger.LogWarning($"Errors: {JsonConvert.SerializeObject(result.Errors)}");
        return BadRequest(result);
    }

    return Ok(result);
}

总结

优先选择方案1,它更简洁、符合框架设计,不需要处理AST遍历和变量传递的复杂逻辑。如果必须在统一的验证规则中处理,再考虑方案2。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 03:58:10