GraphQL 模型苦苦挣扎 System.Text.Json.JsonException
GraphQL models struggling with System.Text.Json.JsonException
我创建了一个新的 .NET Core 项目并安装了包 GraphQL、GraphiQL 和 GraphQL.SystemTextJson。
当运行申请时,这就是我得到的
除了异常消息 GraphiQL 无法找到架构文档。
首先我有两个实体,用户和任务。
public sealed class User
{
public Guid Id { get; set; }
}
public sealed class Task
{
public Guid Id { get; set; }
}
而且他们都有自己的代表图类型
public sealed class UserType : ObjectGraphType<User>
{
public UserType()
{
Name = nameof(User);
Field(user => user.Id).Description("The user id.");
}
}
public sealed class TaskType : ObjectGraphType<Task>
{
public TaskType()
{
Name = nameof(Task);
Field(task => task.Id).Description("The task id.");
}
}
我创建了包含客户端可以执行的所有“操作”的查询
public sealed class GraphQLQuery : ObjectGraphType
{
private readonly List<User> _users = new List<User>();
private readonly List<Task> _tasks = new List<Task>();
public GraphQLQuery()
{
Field<ListGraphType<UserType>>(
"users",
"Returns a collection of users.",
resolve: context => _users);
Field<ListGraphType<TaskType>>(
"tasks",
"Returns a collection of tasks.",
resolve: context => _tasks);
}
}
并为架构注册该查询
public sealed class GraphQLSchema : GraphQL.Types.Schema
{
public GraphQLSchema(GraphQLQuery graphQLQuery, IServiceProvider serviceProvider) : base(serviceProvider)
{
Query = graphQLQuery;
}
}
在 ConfigureServices
的启动文件中,我在调用 services.AddControllers()
之前添加了这段代码来注册所有需要的服务
serviceCollection
.AddSingleton<IDocumentExecuter, DocumentExecuter>()
.AddSingleton<IDocumentWriter, DocumentWriter>()
.AddSingleton<ISchema, GraphQLSchema>()
.AddSingleton<GraphQLQuery>()
在 Configure
方法中,我首先调用 app.UseGraphiQl()
。
对应GraphQL请求DTO
public sealed class GraphQLRequest
{
public string OperationName { get; set; }
public string Query { get; set; }
[JsonConverter(typeof(ObjectDictionaryConverter))]
public Dictionary<string, object> Variables { get; set; }
}
最后我实现了 API 控制器
[ApiController]
[Route("[controller]")]
public sealed class GraphQLController : Controller
{
private readonly ISchema _schema;
private readonly IDocumentExecuter _documentExecuter;
public GraphQLController(ISchema schema, IDocumentExecuter documentExecuter)
{
_schema = schema;
_documentExecuter = documentExecuter;
}
public async Task<IActionResult> Post([FromBody] GraphQLRequest graphQlRequest)
{
ExecutionOptions executionOptions = new ExecutionOptions()
{
Schema = _schema,
Query = graphQlRequest.Query,
Inputs = graphQlRequest.Variables?.ToInputs()
};
ExecutionResult executionResult = await _documentExecuter.ExecuteAsync(executionOptions);
if (executionResult.Errors != null)
return BadRequest(executionResult);
return Ok(executionResult);
}
}
有人知道这里出了什么问题吗?我看不到循环依赖等问题
当运行应用程序graphQlRequest
包含以下值
- 操作名称:内省查询
- 查询:
.
query IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
subscriptionType { name }
types {
...FullType
}
directives {
name
description
locations
args {
...InputValue
}
}
}
}
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}
fragment InputValue on __InputValue {
name
description
type { ...TypeRef }
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}
我现在迁移到 .NET 5,却收到此错误
我添加了一个复制存储库
它是 Json 解析器的经典之作。如果您有导航属性或相互引用的属性。
通常可以通过返回映射结果或调整 Json 序列化程序设置来解决此问题。
我不确定这是否已在 .Net Core 3.1 中修复
但你可以在 startup.cs 添加
安装Newtonsoft Json
如果你还没有。
services
.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
希望这能解决问题。
这里有一些关于这个问题的文章。
https://dotnetcoretutorials.com/2020/03/15/fixing-json-self-referencing-loop-exceptions/
您在 .Net 5 中的错误与未注册的图形类型有关。如果您在调试设置中启用所有异常并禁用“Just My Code”,您将看到此错误
System.InvalidOperationException: 'Required service for type API.GraphTypes.UserType not found'
This exception was originally thrown at this call stack:
GraphQL.Utilities.ServiceProviderExtensions.GetRequiredService(System.IServiceProvider, System.Type) in ServiceProviderExtensions.cs
GraphQL.Types.Schema.CreateTypesLookup.AnonymousMethod__68_1(System.Type) in Schema.cs
GraphQL.Types.GraphTypesLookup.Create.AnonymousMethod__0(System.Type) in GraphTypesLookup.cs
GraphQL.Types.GraphTypesLookup.AddTypeIfNotRegistered(System.Type, GraphQL.Types.TypeCollectionContext) in GraphTypesLookup.cs
GraphQL.Types.GraphTypesLookup.HandleField(GraphQL.Types.IComplexGraphType, GraphQL.Types.FieldType, GraphQL.Types.TypeCollectionContext, bool) in GraphTypesLookup.cs
GraphQL.Types.GraphTypesLookup.AddType(GraphQL.Types.IGraphType, GraphQL.Types.TypeCollectionContext) in GraphTypesLookup.cs
GraphQL.Types.GraphTypesLookup.Create(System.Collections.Generic.IEnumerable<GraphQL.Types.IGraphType>, System.Collections.Generic.IEnumerable<GraphQL.Types.DirectiveGraphType>, System.Func<System.Type, GraphQL.Types.IGraphType>, GraphQL.Conversion.INameConverter, bool) in GraphTypesLookup.cs
GraphQL.Types.Schema.CreateTypesLookup() in Schema.cs
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
将 UserType
和 TaskType
添加到 DI 容器解决了这个错误。
现在,你原来的问题:你应该使用 IDocumentWriter
来写响应,你不能通过返回 Ok(executionResult)
来序列化 executionResult
。
使用此代码编写响应(从官方 graphql-dotnet/examples 存储库中窃取):
private async Task WriteResponseAsync(HttpContext context, ExecutionResult result)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = result.Errors?.Any() == true ? (int)HttpStatusCode.BadRequest : (int)HttpStatusCode.OK;
await _documentWriter.WriteAsync(context.Response.Body, result);
}
已更新GraphQLController.cs
[ApiController]
[Route("[controller]")]
public sealed class GraphQLController : Controller
{
private readonly ISchema _schema;
private readonly IDocumentExecuter _documentExecuter;
private readonly IDocumentWriter _documentWriter;
public GraphQLController(ISchema schema, IDocumentExecuter documentExecuter, IDocumentWriter documentWriter)
{
_schema = schema;
_documentExecuter = documentExecuter;
_documentWriter = documentWriter;
}
public async Task Post([FromBody] GraphQLRequest graphQlRequest)
{
ExecutionOptions executionOptions = new ExecutionOptions()
{
Schema = _schema,
Query = graphQlRequest.Query,
Inputs = graphQlRequest.Variables?.ToInputs()
};
ExecutionResult executionResult = await _documentExecuter.ExecuteAsync(executionOptions);
await WriteResponseAsync(HttpContext, executionResult);
}
private async Task WriteResponseAsync(HttpContext context, ExecutionResult result)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = result.Errors?.Any() == true ? (int)HttpStatusCode.BadRequest : (int)HttpStatusCode.OK;
await _documentWriter.WriteAsync(context.Response.Body, result);
}
}
我创建了一个新的 .NET Core 项目并安装了包 GraphQL、GraphiQL 和 GraphQL.SystemTextJson。
当运行申请时,这就是我得到的
除了异常消息 GraphiQL 无法找到架构文档。
首先我有两个实体,用户和任务。
public sealed class User
{
public Guid Id { get; set; }
}
public sealed class Task
{
public Guid Id { get; set; }
}
而且他们都有自己的代表图类型
public sealed class UserType : ObjectGraphType<User>
{
public UserType()
{
Name = nameof(User);
Field(user => user.Id).Description("The user id.");
}
}
public sealed class TaskType : ObjectGraphType<Task>
{
public TaskType()
{
Name = nameof(Task);
Field(task => task.Id).Description("The task id.");
}
}
我创建了包含客户端可以执行的所有“操作”的查询
public sealed class GraphQLQuery : ObjectGraphType
{
private readonly List<User> _users = new List<User>();
private readonly List<Task> _tasks = new List<Task>();
public GraphQLQuery()
{
Field<ListGraphType<UserType>>(
"users",
"Returns a collection of users.",
resolve: context => _users);
Field<ListGraphType<TaskType>>(
"tasks",
"Returns a collection of tasks.",
resolve: context => _tasks);
}
}
并为架构注册该查询
public sealed class GraphQLSchema : GraphQL.Types.Schema
{
public GraphQLSchema(GraphQLQuery graphQLQuery, IServiceProvider serviceProvider) : base(serviceProvider)
{
Query = graphQLQuery;
}
}
在 ConfigureServices
的启动文件中,我在调用 services.AddControllers()
serviceCollection
.AddSingleton<IDocumentExecuter, DocumentExecuter>()
.AddSingleton<IDocumentWriter, DocumentWriter>()
.AddSingleton<ISchema, GraphQLSchema>()
.AddSingleton<GraphQLQuery>()
在 Configure
方法中,我首先调用 app.UseGraphiQl()
。
对应GraphQL请求DTO
public sealed class GraphQLRequest
{
public string OperationName { get; set; }
public string Query { get; set; }
[JsonConverter(typeof(ObjectDictionaryConverter))]
public Dictionary<string, object> Variables { get; set; }
}
最后我实现了 API 控制器
[ApiController]
[Route("[controller]")]
public sealed class GraphQLController : Controller
{
private readonly ISchema _schema;
private readonly IDocumentExecuter _documentExecuter;
public GraphQLController(ISchema schema, IDocumentExecuter documentExecuter)
{
_schema = schema;
_documentExecuter = documentExecuter;
}
public async Task<IActionResult> Post([FromBody] GraphQLRequest graphQlRequest)
{
ExecutionOptions executionOptions = new ExecutionOptions()
{
Schema = _schema,
Query = graphQlRequest.Query,
Inputs = graphQlRequest.Variables?.ToInputs()
};
ExecutionResult executionResult = await _documentExecuter.ExecuteAsync(executionOptions);
if (executionResult.Errors != null)
return BadRequest(executionResult);
return Ok(executionResult);
}
}
有人知道这里出了什么问题吗?我看不到循环依赖等问题
当运行应用程序graphQlRequest
包含以下值
- 操作名称:内省查询
- 查询:
.
query IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
subscriptionType { name }
types {
...FullType
}
directives {
name
description
locations
args {
...InputValue
}
}
}
}
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}
fragment InputValue on __InputValue {
name
description
type { ...TypeRef }
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}
我现在迁移到 .NET 5,却收到此错误
我添加了一个复制存储库
它是 Json 解析器的经典之作。如果您有导航属性或相互引用的属性。
通常可以通过返回映射结果或调整 Json 序列化程序设置来解决此问题。
我不确定这是否已在 .Net Core 3.1 中修复 但你可以在 startup.cs 添加
安装Newtonsoft Json
如果你还没有。
services
.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
希望这能解决问题。
这里有一些关于这个问题的文章。
https://dotnetcoretutorials.com/2020/03/15/fixing-json-self-referencing-loop-exceptions/
您在 .Net 5 中的错误与未注册的图形类型有关。如果您在调试设置中启用所有异常并禁用“Just My Code”,您将看到此错误
System.InvalidOperationException: 'Required service for type API.GraphTypes.UserType not found'
This exception was originally thrown at this call stack:
GraphQL.Utilities.ServiceProviderExtensions.GetRequiredService(System.IServiceProvider, System.Type) in ServiceProviderExtensions.cs
GraphQL.Types.Schema.CreateTypesLookup.AnonymousMethod__68_1(System.Type) in Schema.cs
GraphQL.Types.GraphTypesLookup.Create.AnonymousMethod__0(System.Type) in GraphTypesLookup.cs
GraphQL.Types.GraphTypesLookup.AddTypeIfNotRegistered(System.Type, GraphQL.Types.TypeCollectionContext) in GraphTypesLookup.cs
GraphQL.Types.GraphTypesLookup.HandleField(GraphQL.Types.IComplexGraphType, GraphQL.Types.FieldType, GraphQL.Types.TypeCollectionContext, bool) in GraphTypesLookup.cs
GraphQL.Types.GraphTypesLookup.AddType(GraphQL.Types.IGraphType, GraphQL.Types.TypeCollectionContext) in GraphTypesLookup.cs
GraphQL.Types.GraphTypesLookup.Create(System.Collections.Generic.IEnumerable<GraphQL.Types.IGraphType>, System.Collections.Generic.IEnumerable<GraphQL.Types.DirectiveGraphType>, System.Func<System.Type, GraphQL.Types.IGraphType>, GraphQL.Conversion.INameConverter, bool) in GraphTypesLookup.cs
GraphQL.Types.Schema.CreateTypesLookup() in Schema.cs
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
将 UserType
和 TaskType
添加到 DI 容器解决了这个错误。
现在,你原来的问题:你应该使用 IDocumentWriter
来写响应,你不能通过返回 Ok(executionResult)
来序列化 executionResult
。
使用此代码编写响应(从官方 graphql-dotnet/examples 存储库中窃取):
private async Task WriteResponseAsync(HttpContext context, ExecutionResult result)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = result.Errors?.Any() == true ? (int)HttpStatusCode.BadRequest : (int)HttpStatusCode.OK;
await _documentWriter.WriteAsync(context.Response.Body, result);
}
已更新GraphQLController.cs
[ApiController]
[Route("[controller]")]
public sealed class GraphQLController : Controller
{
private readonly ISchema _schema;
private readonly IDocumentExecuter _documentExecuter;
private readonly IDocumentWriter _documentWriter;
public GraphQLController(ISchema schema, IDocumentExecuter documentExecuter, IDocumentWriter documentWriter)
{
_schema = schema;
_documentExecuter = documentExecuter;
_documentWriter = documentWriter;
}
public async Task Post([FromBody] GraphQLRequest graphQlRequest)
{
ExecutionOptions executionOptions = new ExecutionOptions()
{
Schema = _schema,
Query = graphQlRequest.Query,
Inputs = graphQlRequest.Variables?.ToInputs()
};
ExecutionResult executionResult = await _documentExecuter.ExecuteAsync(executionOptions);
await WriteResponseAsync(HttpContext, executionResult);
}
private async Task WriteResponseAsync(HttpContext context, ExecutionResult result)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = result.Errors?.Any() == true ? (int)HttpStatusCode.BadRequest : (int)HttpStatusCode.OK;
await _documentWriter.WriteAsync(context.Response.Body, result);
}
}