New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
5.0.0 - How to Write ISchemaFilter for ProblemDetails? #985
Comments
@domaindrivendev Is this possible? |
# Boxed.AspNetCore - Upgrade to .NET Core 3 SDK and target `netcoreapp3.0`. - Switch from NuGet `PackageIconUrl` to `PackageIcon`, so the icon is now embedded in the package. - Upgrade `Micorosoft.Extensions.*` NuGet packages to `3.0.0`. - Switch from `Newtonsoft.Json` to `System.Text.Json`. - `DistributedCacheExtensions` now uses `System.Text.Json` and serializes directly to UTF8 for performance reasons. - Use Ordinal string comparisons. - Switch from `IHostingEnvironment` to `IWebHostEnvironment`. - `RedirectToCanonicalUrlRule` implements `Microsoft.AspNetCore.Rewrite.IRule` and can be used to redirect to a single canonical URL. This used to be an MVC filter. - `HttpExceptionMiddleware` now implements `IMiddleware`. - Remove site map code. Look at the source code for the The ASP.NET Core standup site for how to do this in a better way. - Remove `UrlHelperExtensions`. Use `LinkGenerator` instead. - Remove `ConfigurationExtensions.GetSection<T>`. Use `IConfiguration.Get` instead. - Remove `InternalServerErrorOnExceptionMiddleware` since it was unused. - Remove `NoCacheAttribute`. # Boxed.AspNetCore.Swagger - Upgrade to .NET Core 3 SDK and target `netcoreapp3.0`. - Upgrade to `Swashbuckle.AspNetCore` version `5.0.0-rc4` for Open API 3 support. - Switch from NuGet `PackageIconUrl` to `PackageIcon`, so the icon is now embedded in the package. - Added `CorrelationIdOperationFilter` operation filter. - Added `ProblemDetails` schema filter but this is not yet working so it's internal for now. See domaindrivendev/Swashbuckle.AspNetCore#985. - Removed `ModelStateDictionary`'s schema filter. # Boxed.AspNetCore.TagHelpers - Upgrade to .NET Core 3 SDK and target `netcoreapp3.0`. - Switch from NuGet `PackageIconUrl` to `PackageIcon`, so the icon is now embedded in the package. # Boxed.DotnetNewTest - Upgrade to .NET Core 3 SDK and target `netstandard2.1` and `netcoreapp3.0`. - Switch from NuGet `PackageIconUrl` to `PackageIcon`, so the icon is now embedded in the package. - Use Ordinal file path string comparisons. - Move experimental `DotnetRunInMemoryAsync` to be `netcoreapp3.0` only due to API limitations. # Boxed.Mapping - Upgrade to .NET Core 3 SDK and target `netstandard1.3`, `netstandard2.0` and `netstandard2.1`. - Switch from NuGet `PackageIconUrl` to `PackageIcon`, so the icon is now embedded in the package. - Add extension methods adding support for `IAsyncEnumerable<T>`.
@RehanSaeed did you find a solution for this? |
Not yet unfortunately. |
@RehanSaeed using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace Platform.Client.Gateway.Utilities
{
public class AuthorizeCheckOperationFilter : IOperationFilter
{
private static readonly OpenApiObject _status401Object = new OpenApiObject()
{
["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7235#section-3.1"),
["title"] = new OpenApiString("Unauthorized"),
["status"] = new OpenApiInteger(StatusCodes.Status401Unauthorized),
["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
};
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var hasAuthorize =
context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any()
|| context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any();
if (hasAuthorize)
{
var openApi401Response = new OpenApiResponse
{
Description = "Unauthorized",
Content = new Dictionary<string, OpenApiMediaType>()
{
{
$"application/problem+json", new OpenApiMediaType()
{
Schema = new OpenApiSchema {
Default = _status401Object,
Example = _status401Object
}
}
}
}
};
operation.Responses.Add($"{StatusCodes.Status401Unauthorized}", openApi401Response);
// operation.Responses.Add($"{StatusCodes.Status403Forbidden}", _openApi403Response);
var oAuthScheme = new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
};
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
[ oAuthScheme ] = new List<string>()
}
};
}
}
}
} But I am not sure if this is the way to go in Swagger, and it only handles 401s and 403 |
In Swagger / OpenAPI, Schemas are a lower level concept that don't have knowledge of status codes etc. So, this isn't something that can be done at that level. As noted above, you need to use an |
Is it possible to get hold of the status code in an |
@RehanSaeed as you can see in my example code you can use StatusCodes.Status401Unauthorized. You do these operation filters when generating the documentation spec. This is not done at runtime that the response changes. What it does is document it as an allowed response with a specific scheme, and it is not responsible for sending the response :-) |
This works: using System.Collections.Generic;
using System.Net.Mime;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
public class ProblemDetailsOperationFilter : IOperationFilter
{
private static readonly OpenApiObject _status400ProblemDetails = new()
{
["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.1"),
["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status400BadRequest)),
["status"] = new OpenApiInteger(StatusCodes.Status400BadRequest),
["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
["errors"] = new OpenApiObject
{
["property1"] = new OpenApiArray
{
new OpenApiString("The property field is required"),
},
},
};
private static readonly OpenApiObject _status401ProblemDetails = new()
{
["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7235#section-3.1"),
["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status401Unauthorized)),
["status"] = new OpenApiInteger(StatusCodes.Status401Unauthorized),
["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
};
private static readonly OpenApiObject _status403ProblemDetails = new()
{
["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.3"),
["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status403Forbidden)),
["status"] = new OpenApiInteger(StatusCodes.Status403Forbidden),
["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
};
private static readonly OpenApiObject _status404ProblemDetails = new()
{
["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.4"),
["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status404NotFound)),
["status"] = new OpenApiInteger(StatusCodes.Status404NotFound),
["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
};
private static readonly OpenApiObject _status406ProblemDetails = new()
{
["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.6"),
["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status406NotAcceptable)),
["status"] = new OpenApiInteger(StatusCodes.Status406NotAcceptable),
["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
};
private static readonly OpenApiObject _status409ProblemDetails = new()
{
["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.8"),
["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status409Conflict)),
["status"] = new OpenApiInteger(StatusCodes.Status409Conflict),
["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
};
private static readonly OpenApiObject _status415ProblemDetails = new()
{
["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.13"),
["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status415UnsupportedMediaType)),
["status"] = new OpenApiInteger(StatusCodes.Status415UnsupportedMediaType),
["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
};
private static readonly OpenApiObject _status422ProblemDetails = new()
{
["type"] = new OpenApiString("https://tools.ietf.org/html/rfc4918#section-11.2"),
["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status422UnprocessableEntity)),
["status"] = new OpenApiInteger(StatusCodes.Status422UnprocessableEntity),
["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
};
private static readonly OpenApiObject _status500ProblemDetails = new()
{
["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.6.1"),
["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status500InternalServerError)),
["status"] = new OpenApiInteger(StatusCodes.Status500InternalServerError),
["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
};
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var problemDetails = new Dictionary<string, IOpenApiAny>
{
{ StatusCodes.Status400BadRequest.ToString(), _status400ProblemDetails },
{ StatusCodes.Status401Unauthorized.ToString(), _status401ProblemDetails },
{ StatusCodes.Status403Forbidden.ToString(), _status403ProblemDetails },
{ StatusCodes.Status404NotFound.ToString(), _status404ProblemDetails },
{ StatusCodes.Status406NotAcceptable.ToString(), _status406ProblemDetails },
{ StatusCodes.Status409Conflict.ToString(), _status409ProblemDetails },
{ StatusCodes.Status415UnsupportedMediaType.ToString(), _status415ProblemDetails },
{ StatusCodes.Status422UnprocessableEntity.ToString(), _status422ProblemDetails },
{ StatusCodes.Status500InternalServerError.ToString(), _status500ProblemDetails },
};
foreach (var operationResponse in operation.Responses)
{
if (problemDetails.TryGetValue(operationResponse.Key, out var problemDetail))
operationResponse.Value.Content[MediaTypeNames.Application.Json].Example = problemDetail;
}
}
} |
I'm trying to write a
ISchemaFilter
forProblemDetails
in ASP.NET Core 3.0.ProblemDetails
is returned for a lot of different status codes e.g. 400, 500 etc. How can I provide a different example ofProblemDetails
depending on the status code?The text was updated successfully, but these errors were encountered: