In a ASP.NET core 5 application, I use GraphQL with GraphQL.SystemTextJson. When I attempt to return a result, I get s System.NotSupportedException saying "Serialization and deserialization of 'System.Type' instances are not supported and should be avoided since they can lead to security issues.".
I suspect something to be missing in the configuration of DocumentWriter.
It is configured like this in ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
...
services.AddScoped<IDocumentWriter, DocumentWriter>();
Any suggestion?
Update:
for completeness, as asked by @AndrewSilver, I report the whole code (adapted from https://www.red-gate.com/simple-talk/dotnet/net-development/building-and-consuming-graphql-api-in-asp-net-core-3-1/ and ported to .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);
}
}
And this is a sample request that triggers the error:
query {
author (id: 1){
id
firstName
lastName
}
blogs
{
id
title
content
}
}
I solved creating a custom 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);
}
}
Then, in configureServices:
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.WriteIndented = true;
options.JsonSerializerOptions.Converters.Add(new CustomJsonConverterForType());
});
I fixed that problem by using the snippet shown in the docs: 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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With