.net 核心 GraphQL、GraphQL.SystemTextJson:不支持“System.Type”实例的序列化和反序列化
Posted
技术标签:
【中文标题】.net 核心 GraphQL、GraphQL.SystemTextJson:不支持“System.Type”实例的序列化和反序列化【英文标题】:.net core GraphQL, GraphQL.SystemTextJson: Serialization and deserialization of 'System.Type' instances are not supported 【发布时间】:2021-06-29 08:18:35 【问题描述】:在 ASP.NET core 5 应用程序中,我将 GraphQL 与 GraphQL.SystemTextJson 一起使用。 当我尝试返回结果时,我收到 System.NotSupportedException 说“'System.Type' 实例的序列化和反序列化不受支持,应该避免,因为它们可能导致安全问题。”。
我怀疑 DocumentWriter 的配置中缺少某些内容。
在ConfigureServices中是这样配置的:
public void ConfigureServices(IServiceCollection services)
services.AddControllers();
...
services.AddScoped<IDocumentWriter, DocumentWriter>();
有什么建议吗?
更新:
为了完整起见,正如@AndrewSilver 所要求的,我报告了整个代码(改编自https://www.red-gate.com/simple-talk/dotnet/net-development/building-and-consuming-graphql-api-in-asp-net-core-3-1/ 并移植到.net core 5.0)。
public class Startup
public Startup(IConfiguration configuration)
Configuration = configuration;
public IConfiguration Configuration get;
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
services.AddControllers();
services.AddSwaggerGen(c =>
c.SwaggerDoc("v1", new OpenApiInfo Title = "GraphQlExperiments", Version = "v1" );
);
services.AddScoped<IDocumentExecuter, DocumentExecuter>();
services.AddScoped<IDocumentWriter, DocumentWriter>();
services.AddScoped<AuthorService>();
services.AddScoped<AuthorRepository>();
services.AddScoped<AuthorQuery>();
services.AddScoped<AuthorType>();
services.AddScoped<BlogPostType>();
services.AddScoped<ISchema, GraphQLDemoSchema>();
services.AddControllers();
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "GraphQlExperiments v1"));
// See: https://github.com/JosephWoodward/graphiql-dotnet
app.UseGraphiQl("/graphiql", "/api/v1/graphql");
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
endpoints.MapControllers();
);
public class Author
public int Id get; set;
public string FirstName get; set;
public string LastName get; set;
public class BlogPost
public int Id get; set;
public string Title get; set;
public string Content get; set;
public Author Author get; set;
public class AuthorType : ObjectGraphType<Author>
public AuthorType()
Name = "Author";
Field(_ => _.Id).Description("Author's Id.");
Field(_ => _.FirstName).Description("First name of the author");
Field(_ => _.LastName).Description("Last name of the author");
public class BlogPostType : ObjectGraphType<BlogPost>
public BlogPostType()
Name = "BlogPost";
Field(_ => _.Id, type:
typeof(IdGraphType)).Description("The Id of the Blog post.");
Field(_ => _.Title).Description("The title of the blog post.");
Field(_ => _.Content).Description("The content of the blog post.");
public class AuthorQuery : ObjectGraphType
public AuthorQuery(AuthorService authorService)
int id = 0;
Field<ListGraphType<AuthorType>>(
name: "authors",
resolve: context =>
return authorService.GetAllAuthors();
);
Field<AuthorType>(
name: "author",
arguments: new QueryArguments(new QueryArgument<IntGraphType> Name = "id" ),
resolve: context =>
id = context.GetArgument<int>("id");
return authorService.GetAuthorById(id);
);
Field<ListGraphType<BlogPostType>>(
name: "blogs",
arguments: new QueryArguments(new QueryArgument<IntGraphType> Name = "id" ),
resolve: context =>
return authorService.GetPostsByAuthor(id);
);
public class GraphQLQueryDTO
public string OperationName get; set;
public string NamedQuery get; set;
public string Query get; set;
public string Variables get; set;
public class GraphQLDemoSchema : Schema, ISchema
public GraphQLDemoSchema(IServiceProvider resolver) : base(resolver)
Query = resolver.GetService<AuthorQuery>();
public class AuthorService
private readonly AuthorRepository _authorRepository;
public AuthorService(AuthorRepository
authorRepository)
_authorRepository = authorRepository;
public List<Author> GetAllAuthors()
return _authorRepository.GetAllAuthors();
public Author GetAuthorById(int id)
return _authorRepository.GetAuthorById(id);
public List<BlogPost> GetPostsByAuthor(int id)
return _authorRepository.GetPostsByAuthor(id);
public class AuthorRepository
private readonly List<Author> authors = new List<Author>();
private readonly List<BlogPost> posts = new List<BlogPost>();
public AuthorRepository()
Author author1 = new Author
Id = 1,
FirstName = "Joydip",
LastName = "Kanjilal"
;
Author author2 = new Author
Id = 2,
FirstName = "Steve",
LastName = "Smith"
;
BlogPost csharp = new BlogPost
Id = 1,
Title = "Mastering C#",
Content = "This is a series of articles on C#.",
Author = author1
;
BlogPost java = new BlogPost
Id = 2,
Title = "Mastering Java",
Content = "This is a series of articles on Java",
Author = author1
;
posts.Add(csharp);
posts.Add(java);
authors.Add(author1);
authors.Add(author2);
public List<Author> GetAllAuthors()
return this.authors;
public Author GetAuthorById(int id)
return authors.Where(author => author.Id == id).FirstOrDefault<Author>();
public List<BlogPost> GetPostsByAuthor(int id)
return posts.Where(post => post.Author.Id == id).ToList<BlogPost>();
[Route("/api/v1/graphql")]
public class GraphQLController : Controller
private readonly ISchema _schema;
private readonly IDocumentExecuter _executer;
public GraphQLController(
ISchema schema,
IDocumentExecuter executer
)
_schema = schema;
_executer = executer;
[HttpPost]
public async Task<IActionResult> Post([FromBody] GraphQLQueryDTO query)
var result = await _executer.ExecuteAsync(_ =>
_.Schema = _schema;
_.Query = query.Query;
_.Inputs = query.Variables?.ToInputs();
);
if (result.Errors?.Count > 0)
return BadRequest();
return Ok(result.Data);
这是一个触发错误的示例请求:
query
author (id: 1)
id
firstName
lastName
blogs
id
title
content
【问题讨论】:
您的代码中有DataSet 或DataTable 吗?您可以查看此问题github.com/dotnet/runtime/issues/41920。也许这就是原因。无论如何,我认为没有更多细节很难说具体的事情 @AndrewSilver,我既不使用 DataSet,也不使用 DataTable。为了完整起见,我更新了问题,报告了整个代码。 【参考方案1】:我解决了创建自定义 JsonConverter 的问题:
public class CustomJsonConverterForType : JsonConverter<Type>
public override Type Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options
)
// Caution: Deserialization of type instances like this
// is not recommended and should be avoided
// since it can lead to potential security issues.
// If you really want this supported (for instance if the JSON input is trusted):
// string assemblyQualifiedName = reader.GetString();
// return Type.GetType(assemblyQualifiedName);
throw new NotSupportedException();
public override void Write(
Utf8JsonWriter writer,
Type value,
JsonSerializerOptions options
)
string assemblyQualifiedName = value.AssemblyQualifiedName;
// Use this with caution, since you are disclosing type information.
writer.WriteStringValue(assemblyQualifiedName);
然后,在 configureServices 中:
services.AddControllers()
.AddJsonOptions(options =>
options.JsonSerializerOptions.WriteIndented = true;
options.JsonSerializerOptions.Converters.Add(new CustomJsonConverterForType());
);
【讨论】:
为我工作谢谢。 在这个上获得System.Text.Json.JsonException: A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32
@PriteshAcharya 这是因为您的对象可能有循环(如自引用)。您需要确保您的对象没有任何循环,因为它们会导致反序列化中的无限循环【参考方案2】:
我使用文档中显示的 sn-p 解决了这个问题:https://graphql-dotnet.github.io/docs/migrations/migration3
[HttpPost]
public async Task<IActionResult> Post([FromBody] GraphQLQueryDTO query)
var result = await _executer.ExecuteAsync(_ =>
_.Schema = _schema;
_.Query = query.Query;
_.Inputs = query.Variables?.ToInputs();
);
/* ----------- Added this ---------------------------------*/
HttpContext.Response.ContentType = "application/json";
HttpContext.Response.StatusCode = 200; // OK
var writer = new GraphQL.SystemTextJson.DocumentWriter();
await writer.WriteAsync(HttpContext.Response.Body, result);*
/* ------------------------------------------------------*/
if (result.Errors?.Count > 0)
return BadRequest();
return Ok(result.Data);
【讨论】:
是的。这实际上解决了。小问题:方法应该返回任务; sn-p 应该是: HttpContext.Response.ContentType = "application/json"; HttpContext.Response.StatusCode = result.Errors?.Any() == true ? (int)HttpStatusCode.BadRequest : (int)HttpStatusCode.OK; var writer = new GraphQL.SystemTextJson.DocumentWriter();之后应该没有代码 不过,我希望 DocumentWriter 能够按照 ConfigureServices 中的定义进行注入。 好吧,我想你可以将它注入控制器。显然你必须显式调用 DocumentWriter.Write,而所有的文档都让人相信它隐含在对 DocumentExecutor 的调用中。【参考方案3】:在您的 startup.cs 中,在 ConfigureServices 中
在 AddControllers() 之后添加 AddNewtonsoftJson()
services.AddControllers().AddNewtonsoftJson();
【讨论】:
这不是一个实际的解决方案,因为它没有通过使用 System.Text.Json 解决 OP 的问题,它只是提供了一种替代方案。以上是关于.net 核心 GraphQL、GraphQL.SystemTextJson:不支持“System.Type”实例的序列化和反序列化的主要内容,如果未能解决你的问题,请参考以下文章
.Net Core 上的 GraphQL、MongoDB - 中间件?
.NET 遇上 GraphQL使用 Hot Chocolate 构建 GraphQL 服务