diff --git a/src/Transports.AspNetCore/AuthorizationVisitorBase.Validation.cs b/src/Transports.AspNetCore/AuthorizationVisitorBase.Validation.cs
index 43a834a2..451c16cb 100644
--- a/src/Transports.AspNetCore/AuthorizationVisitorBase.Validation.cs
+++ b/src/Transports.AspNetCore/AuthorizationVisitorBase.Validation.cs
@@ -141,6 +141,7 @@ protected virtual void HandleNodeNotAuthorized(ValidationInfo info)
{
var resource = GenerateResourceDescription(info);
var err = info.Node == null ? new AccessDeniedError(resource) : new AccessDeniedError(resource, info.Context.Document.Source, info.Node);
+ err.PreferredStatusCode = HttpStatusCode.Unauthorized;
info.Context.ReportError(err);
}
diff --git a/src/Transports.AspNetCore/Errors/AccessDeniedError.cs b/src/Transports.AspNetCore/Errors/AccessDeniedError.cs
index 7ad2c685..b678a8ea 100644
--- a/src/Transports.AspNetCore/Errors/AccessDeniedError.cs
+++ b/src/Transports.AspNetCore/Errors/AccessDeniedError.cs
@@ -3,7 +3,7 @@ namespace GraphQL.Server.Transports.AspNetCore.Errors;
///
/// Represents an error indicating that the user is not allowed access to the specified resource.
///
-public class AccessDeniedError : ValidationError
+public class AccessDeniedError : ValidationError, IHasPreferredStatusCode
{
///
public AccessDeniedError(string resource)
@@ -29,4 +29,7 @@ public AccessDeniedError(string resource, GraphQLParser.ROM originalQuery, param
/// Returns the list of role memberships that would allow access to these node(s).
///
public List? RolesRequired { get; set; }
+
+ ///
+ public HttpStatusCode PreferredStatusCode { get; set; } = HttpStatusCode.Forbidden;
}
diff --git a/src/Transports.AspNetCore/Errors/FileCountExceededError.cs b/src/Transports.AspNetCore/Errors/FileCountExceededError.cs
index 0d5733cc..79f6a4b9 100644
--- a/src/Transports.AspNetCore/Errors/FileCountExceededError.cs
+++ b/src/Transports.AspNetCore/Errors/FileCountExceededError.cs
@@ -14,5 +14,5 @@ public FileCountExceededError()
}
///
- public HttpStatusCode PreferredStatusCode => HttpStatusCode.RequestEntityTooLarge;
+ public HttpStatusCode PreferredStatusCode { get; set; } = HttpStatusCode.RequestEntityTooLarge;
}
diff --git a/src/Transports.AspNetCore/Errors/FileSizeExceededError.cs b/src/Transports.AspNetCore/Errors/FileSizeExceededError.cs
index 67b998c5..9f897d53 100644
--- a/src/Transports.AspNetCore/Errors/FileSizeExceededError.cs
+++ b/src/Transports.AspNetCore/Errors/FileSizeExceededError.cs
@@ -14,5 +14,5 @@ public FileSizeExceededError()
}
///
- public HttpStatusCode PreferredStatusCode => HttpStatusCode.RequestEntityTooLarge;
+ public HttpStatusCode PreferredStatusCode { get; set; } = HttpStatusCode.RequestEntityTooLarge;
}
diff --git a/src/Transports.AspNetCore/Errors/InvalidContentTypeError.cs b/src/Transports.AspNetCore/Errors/InvalidContentTypeError.cs
index 83194aa2..8fcc9872 100644
--- a/src/Transports.AspNetCore/Errors/InvalidContentTypeError.cs
+++ b/src/Transports.AspNetCore/Errors/InvalidContentTypeError.cs
@@ -3,11 +3,14 @@ namespace GraphQL.Server.Transports.AspNetCore.Errors;
///
/// Represents an error indicating that the content-type is invalid, for example, could not be parsed or is not supported.
///
-public class InvalidContentTypeError : RequestError
+public class InvalidContentTypeError : RequestError, IHasPreferredStatusCode
{
///
public InvalidContentTypeError() : base("Invalid 'Content-Type' header.") { }
///
public InvalidContentTypeError(string message) : base("Invalid 'Content-Type' header: " + message) { }
+
+ ///
+ public HttpStatusCode PreferredStatusCode { get; set; } = HttpStatusCode.UnsupportedMediaType;
}
diff --git a/src/Transports.AspNetCore/GraphQLHttpMiddleware.cs b/src/Transports.AspNetCore/GraphQLHttpMiddleware.cs
index a8ed6d37..5d493710 100644
--- a/src/Transports.AspNetCore/GraphQLHttpMiddleware.cs
+++ b/src/Transports.AspNetCore/GraphQLHttpMiddleware.cs
@@ -273,7 +273,7 @@ protected virtual async Task InvokeAsync(HttpContext context, RequestDelegate ne
}
catch (ExecutionError ex) // catches FileCountExceededError, FileSizeExceededError, InvalidMapError
{
- await WriteErrorResponseAsync(context, ex is IHasPreferredStatusCode sc ? sc.PreferredStatusCode : HttpStatusCode.BadRequest, ex);
+ await WriteErrorResponseAsync(context, ex);
return null;
}
catch (Exception ex) // catches JSON deserialization exceptions
@@ -1046,19 +1046,19 @@ await webSocket.CloseAsync(
/// Writes an access denied message to the output with status code 401 Unauthorized when the user is not authenticated.
///
protected virtual Task HandleNotAuthenticatedAsync(HttpContext context, RequestDelegate next)
- => WriteErrorResponseAsync(context, HttpStatusCode.Unauthorized, new AccessDeniedError("schema"));
+ => WriteErrorResponseAsync(context, new AccessDeniedError("schema") { PreferredStatusCode = HttpStatusCode.Unauthorized });
///
/// Writes an access denied message to the output with status code 403 Forbidden when the user fails the role checks.
///
protected virtual Task HandleNotAuthorizedRoleAsync(HttpContext context, RequestDelegate next)
- => WriteErrorResponseAsync(context, HttpStatusCode.Forbidden, new AccessDeniedError("schema") { RolesRequired = _options.AuthorizedRoles });
+ => WriteErrorResponseAsync(context, new AccessDeniedError("schema") { RolesRequired = _options.AuthorizedRoles });
///
/// Writes an access denied message to the output with status code 403 Forbidden when the user fails the policy check.
///
protected virtual Task HandleNotAuthorizedPolicyAsync(HttpContext context, RequestDelegate next, AuthorizationResult authorizationResult)
- => WriteErrorResponseAsync(context, HttpStatusCode.Forbidden, new AccessDeniedError("schema") { PolicyRequired = _options.AuthorizedPolicy, PolicyAuthorizationResult = authorizationResult });
+ => WriteErrorResponseAsync(context, new AccessDeniedError("schema") { PolicyRequired = _options.AuthorizedPolicy, PolicyAuthorizationResult = authorizationResult });
///
/// Writes a '400 JSON body text could not be parsed.' message to the output.
@@ -1095,7 +1095,7 @@ protected virtual Task HandleWebSocketSubProtocolNotSupportedAsync(HttpContext c
/// Writes a '415 Invalid Content-Type header: could not be parsed.' message to the output.
///
protected virtual Task HandleContentTypeCouldNotBeParsedErrorAsync(HttpContext context, RequestDelegate next)
- => WriteErrorResponseAsync(context, HttpStatusCode.UnsupportedMediaType, new InvalidContentTypeError($"value '{context.Request.ContentType}' could not be parsed."));
+ => WriteErrorResponseAsync(context, new InvalidContentTypeError($"value '{context.Request.ContentType}' could not be parsed."));
///
/// Writes a '415 Invalid Content-Type header: non-supported media type.' message to the output.
@@ -1103,7 +1103,6 @@ protected virtual Task HandleContentTypeCouldNotBeParsedErrorAsync(HttpContext c
protected virtual Task HandleInvalidContentTypeErrorAsync(HttpContext context, RequestDelegate next)
=> WriteErrorResponseAsync(
context,
- HttpStatusCode.UnsupportedMediaType,
_options.ReadFormOnPost
? new InvalidContentTypeError($"non-supported media type '{context.Request.ContentType}'. Must be '{MEDIATYPE_JSON}', '{MEDIATYPE_GRAPHQL}' or a form body.")
: new InvalidContentTypeError($"non-supported media type '{context.Request.ContentType}'. Must be '{MEDIATYPE_JSON}' or '{MEDIATYPE_GRAPHQL}'.")
diff --git a/tests/ApiApprovalTests/net50+net60+net80/GraphQL.Server.Transports.AspNetCore.approved.txt b/tests/ApiApprovalTests/net50+net60+net80/GraphQL.Server.Transports.AspNetCore.approved.txt
index d72fac9e..279e2717 100644
--- a/tests/ApiApprovalTests/net50+net60+net80/GraphQL.Server.Transports.AspNetCore.approved.txt
+++ b/tests/ApiApprovalTests/net50+net60+net80/GraphQL.Server.Transports.AspNetCore.approved.txt
@@ -192,12 +192,13 @@ namespace GraphQL.Server.Transports.AspNetCore
}
namespace GraphQL.Server.Transports.AspNetCore.Errors
{
- public class AccessDeniedError : GraphQL.Validation.ValidationError
+ public class AccessDeniedError : GraphQL.Validation.ValidationError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public AccessDeniedError(string resource) { }
public AccessDeniedError(string resource, GraphQLParser.ROM originalQuery, params GraphQLParser.AST.ASTNode[] nodes) { }
public Microsoft.AspNetCore.Authorization.AuthorizationResult? PolicyAuthorizationResult { get; set; }
public string? PolicyRequired { get; set; }
+ public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
public System.Collections.Generic.List? RolesRequired { get; set; }
}
public class BatchedRequestsNotSupportedError : GraphQL.Execution.RequestError
@@ -212,12 +213,12 @@ namespace GraphQL.Server.Transports.AspNetCore.Errors
public class FileCountExceededError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public FileCountExceededError() { }
- public System.Net.HttpStatusCode PreferredStatusCode { get; }
+ public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class FileSizeExceededError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public FileSizeExceededError() { }
- public System.Net.HttpStatusCode PreferredStatusCode { get; }
+ public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class HttpMethodValidationError : GraphQL.Validation.ValidationError
{
@@ -227,10 +228,11 @@ namespace GraphQL.Server.Transports.AspNetCore.Errors
{
System.Net.HttpStatusCode PreferredStatusCode { get; }
}
- public class InvalidContentTypeError : GraphQL.Execution.RequestError
+ public class InvalidContentTypeError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public InvalidContentTypeError() { }
public InvalidContentTypeError(string message) { }
+ public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class InvalidMapError : GraphQL.Execution.RequestError
{
diff --git a/tests/ApiApprovalTests/netcoreapp21+netstandard20/GraphQL.Server.Transports.AspNetCore.approved.txt b/tests/ApiApprovalTests/netcoreapp21+netstandard20/GraphQL.Server.Transports.AspNetCore.approved.txt
index 3de02297..e1a2d4c7 100644
--- a/tests/ApiApprovalTests/netcoreapp21+netstandard20/GraphQL.Server.Transports.AspNetCore.approved.txt
+++ b/tests/ApiApprovalTests/netcoreapp21+netstandard20/GraphQL.Server.Transports.AspNetCore.approved.txt
@@ -210,12 +210,13 @@ namespace GraphQL.Server.Transports.AspNetCore
}
namespace GraphQL.Server.Transports.AspNetCore.Errors
{
- public class AccessDeniedError : GraphQL.Validation.ValidationError
+ public class AccessDeniedError : GraphQL.Validation.ValidationError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public AccessDeniedError(string resource) { }
public AccessDeniedError(string resource, GraphQLParser.ROM originalQuery, params GraphQLParser.AST.ASTNode[] nodes) { }
public Microsoft.AspNetCore.Authorization.AuthorizationResult? PolicyAuthorizationResult { get; set; }
public string? PolicyRequired { get; set; }
+ public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
public System.Collections.Generic.List? RolesRequired { get; set; }
}
public class BatchedRequestsNotSupportedError : GraphQL.Execution.RequestError
@@ -230,12 +231,12 @@ namespace GraphQL.Server.Transports.AspNetCore.Errors
public class FileCountExceededError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public FileCountExceededError() { }
- public System.Net.HttpStatusCode PreferredStatusCode { get; }
+ public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class FileSizeExceededError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public FileSizeExceededError() { }
- public System.Net.HttpStatusCode PreferredStatusCode { get; }
+ public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class HttpMethodValidationError : GraphQL.Validation.ValidationError
{
@@ -245,10 +246,11 @@ namespace GraphQL.Server.Transports.AspNetCore.Errors
{
System.Net.HttpStatusCode PreferredStatusCode { get; }
}
- public class InvalidContentTypeError : GraphQL.Execution.RequestError
+ public class InvalidContentTypeError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public InvalidContentTypeError() { }
public InvalidContentTypeError(string message) { }
+ public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class InvalidMapError : GraphQL.Execution.RequestError
{
diff --git a/tests/ApiApprovalTests/netcoreapp31/GraphQL.Server.Transports.AspNetCore.approved.txt b/tests/ApiApprovalTests/netcoreapp31/GraphQL.Server.Transports.AspNetCore.approved.txt
index 2122e599..6ed0eab4 100644
--- a/tests/ApiApprovalTests/netcoreapp31/GraphQL.Server.Transports.AspNetCore.approved.txt
+++ b/tests/ApiApprovalTests/netcoreapp31/GraphQL.Server.Transports.AspNetCore.approved.txt
@@ -192,12 +192,13 @@ namespace GraphQL.Server.Transports.AspNetCore
}
namespace GraphQL.Server.Transports.AspNetCore.Errors
{
- public class AccessDeniedError : GraphQL.Validation.ValidationError
+ public class AccessDeniedError : GraphQL.Validation.ValidationError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public AccessDeniedError(string resource) { }
public AccessDeniedError(string resource, GraphQLParser.ROM originalQuery, params GraphQLParser.AST.ASTNode[] nodes) { }
public Microsoft.AspNetCore.Authorization.AuthorizationResult? PolicyAuthorizationResult { get; set; }
public string? PolicyRequired { get; set; }
+ public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
public System.Collections.Generic.List? RolesRequired { get; set; }
}
public class BatchedRequestsNotSupportedError : GraphQL.Execution.RequestError
@@ -212,12 +213,12 @@ namespace GraphQL.Server.Transports.AspNetCore.Errors
public class FileCountExceededError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public FileCountExceededError() { }
- public System.Net.HttpStatusCode PreferredStatusCode { get; }
+ public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class FileSizeExceededError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public FileSizeExceededError() { }
- public System.Net.HttpStatusCode PreferredStatusCode { get; }
+ public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class HttpMethodValidationError : GraphQL.Validation.ValidationError
{
@@ -227,10 +228,11 @@ namespace GraphQL.Server.Transports.AspNetCore.Errors
{
System.Net.HttpStatusCode PreferredStatusCode { get; }
}
- public class InvalidContentTypeError : GraphQL.Execution.RequestError
+ public class InvalidContentTypeError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public InvalidContentTypeError() { }
public InvalidContentTypeError(string message) { }
+ public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class InvalidMapError : GraphQL.Execution.RequestError
{