.NET Core 3.1使用GraphQL Client(v3.2.1)调用API时响应Data属性为空问题
我之前也碰到过类似的情况,结合你的代码和响应数据来看,大概率是序列化配置不匹配或者忽略了GraphQL的错误响应导致的,下面一步步帮你排查解决:
第一步:先检查GraphQL响应是否包含错误
即使HTTP状态码是200 OK,GraphQL接口也可能返回errors字段(比如权限问题、查询语法错误等),这时候SendQueryAsync返回的结果里Data会是null,而Errors属性会有具体错误信息。你可以先修改代码打印错误:
var result = await graphQLClient.SendQueryAsync<Data>(request); // 新增错误检查逻辑 if (result.Errors != null && result.Errors.Any()) { foreach (var error in result.Errors) { Console.WriteLine($"GraphQL Error: {error.Message}"); // 如果有错误详情,也可以打印出来 if (error.Extensions != null) { Console.WriteLine($"Error Details: {Newtonsoft.Json.JsonConvert.SerializeObject(error.Extensions)}"); } } }
如果这里输出了错误信息,直接根据提示修复即可(比如查询语句拼写错误、权限不足等)。
第二步:修复序列化的大小写映射问题
如果没有错误,那核心问题就是Newtonsoft.Json的序列化配置和你的模型类不匹配:
问题根源
GraphQL接口返回的是小驼峰命名的JSON字段,而你通过“粘贴JSON为类”生成的模型类也是小驼峰属性,但GraphQL Client默认的NewtonsoftJsonSerializer使用的是CamelCasePropertyNamesContractResolver——这个解析器的逻辑是:将C#的PascalCase属性映射到JSON的小驼峰字段。你的模型属性是小驼峰,就会导致映射失败,最终反序列化后Data为null。
解决方法(推荐遵循C#编码规范)
修改模型类为C#标准的PascalCase命名,然后显式配置序列化器使用驼峰解析:
1. 调整模型类为PascalCase
// 注意:不需要Rootobject类,因为SendQueryAsync会自动提取外层的data字段 public class Data { public CurrentUser CurrentUser { get; set; } } public class CurrentUser { public int CardsCount { get; set; } public Card[] Cards { get; set; } } public class Card { public string Name { get; set; } public string PictureUrl { get; set; } public string Position { get; set; } public Player Player { get; set; } } public class Player { public string DisplayName { get; set; } }
2. 配置GraphQL Client的序列化器
创建GraphQLHttpClient时,传入自定义的Newtonsoft.Json配置:
var serializerSettings = new Newtonsoft.Json.JsonSerializerSettings { // 让序列化器将JSON小驼峰映射到C# PascalCase属性 ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver(), // 忽略null值,避免不必要的序列化问题 NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore }; var graphQLClient = new GraphQLHttpClient( new GraphQLHttpClientOptions { EndPoint = new Uri(_graphQLEndPoint) }, new NewtonsoftJsonSerializer(serializerSettings), httpclient);
3. 调用时使用正确的泛型参数
因为SendQueryAsync<T>会自动解析响应中最外层的data字段,所以直接传入Data类作为泛型即可:
var request = new GraphQLRequest { Query = @"query CurrentUserCards { currentUser { cardsCount cards { name pictureUrl position player { displayName } } } }" }; var result = await graphQLClient.SendQueryAsync<Data>(request); // 现在result.Data应该就是你要的CurrentUser数据了 var currentUser = result.Data.CurrentUser;
备选方案(保持现有小驼峰模型)
如果你不想修改模型类,可以改用默认的DefaultContractResolver,它会按名称(不区分大小写)匹配字段和属性:
var serializerSettings = new Newtonsoft.Json.JsonSerializerSettings { ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver(), MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore // 忽略JSON中存在但模型里没有的字段 }; var graphQLClient = new GraphQLHttpClient( new GraphQLHttpClientOptions { EndPoint = new Uri(_graphQLEndPoint) }, new NewtonsoftJsonSerializer(serializerSettings), httpclient);
调用时同样使用Data类作为泛型参数即可。
额外注意点
- 不要用
Rootobject作为泛型参数:SendQueryAsync<T>已经帮你处理了最外层的data字段,传入Rootobject会导致它尝试将data字段反序列化为Rootobject类型,而实际data字段的结构是Data类,所以会出现嵌套错误。 - 确认查询语句的字段名和模型属性完全对应:比如你的查询里是
cardsCount,模型里要对应CardsCount(PascalCase)或者cardsCount(小驼峰),不能有拼写错误。
内容的提问来源于stack exchange,提问作者Thomas

