.net core GraphQL、GraphQL.SystemTextJson:不支持“System.Type”实例的序列化和反序列化

问题描述 投票:0回答:5

在 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
    }
}
asp.net-core graphql jsonserializer
5个回答
13
投票

我解决了创建自定义 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());
            });

3
投票

不要使用 System.Text.Json.JsonSearializer 使用 NewtonSoft.JsonConvert.SearializeObject


2
投票

我通过使用文档中显示的代码片段解决了这个问题: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);
  }
}

0
投票

尝试安装Microsoft.AspNetCore.Mvc.NewtonsoftJson和ConfigureServices,如下所示: builder.Services.AddControllers().AddNewtonsoftJson();

希望对你有帮助


-3
投票

在你的startup.cs中,在ConfigureServices中

在 AddControllers() 之后添加 AddNewtonsoftJson()

services.AddControllers().AddNewtonsoftJson();

© www.soinside.com 2019 - 2024. All rights reserved.