RicoSuter/NSwag
View on GitHubSchema definition is wrong: Custom exception's properties are not camel case
Open
#3072 opened on Sep 24, 2020
help wanted
Description
When I create a very simple Swagger API:
public void ConfigureServices(IServiceCollection services)
{
services
.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.Converters.Add(new StringEnumConverter());
// We only allow undefined in TS. So ignore null properties.
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
})
;
// Register the Swagger services.
services.AddSwaggerDocument();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
// Register the Swagger generator and the Swagger UI middleware.
app.UseOpenApi();
app.UseSwaggerUi3();
...
}
with this Controller Action:
[HttpGet("city/{city}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(OtherException), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(CityNotFoundException), StatusCodes.Status404NotFound)]
public async Task<ActionResult<WeatherForecast[]>> Get(string city)
{
try
{
return Ok(await _Get(city));
}
catch (CityNotFoundException e)
{
return NotFound(e);
}
catch (OtherException e)
{
return BadRequest(e);
}
}
the "definitions" schema looks like:
{
"WeatherForecast": {
"type": "object",
"required": [
"date",
"temperatureC",
"temperatureF"
],
"properties": {
"city": {
"type": "string"
},
"date": {
"type": "string",
"format": "date-time"
},
"temperatureC": {
"type": "integer",
"format": "int32"
},
"temperatureF": {
"type": "integer",
"format": "int32"
},
"summary": {
"type": "string"
}
}
},
"OtherException": {
"allOf": [
{
"$ref": "#/definitions/Exception"
},
{
"type": "object",
"required": [
"timestamp"
],
"properties": {
"timestamp": {
"type": "string",
"format": "date-time"
}
}
}
]
},
"Exception": {
"type": "object",
"required": [
"Message"
],
"properties": {
"StackTrace": {
"type": "string"
},
"Message": {
"type": "string"
},
"InnerException": {
"$ref": "#/definitions/Exception"
},
"Source": {
"type": "string"
}
}
},
"CityNotFoundException": {
"allOf": [
{
"$ref": "#/definitions/Exception"
},
{
"type": "object"
}
]
}
}
You'll notice the properties of base Exception are all pascal case. That's wrong because the response will be camel case:
{
"timestamp": "2020-09-24T15:50:55.1297797+02:00",
"stackTrace": " at WebApplication1.Controllers.WeatherForecastController._Get(String city) in C:\\Users\\xxx\\Projects\\WebApplication1\\WebApplication1\\Controllers\\WeatherForecastController.cs:line 50\r\n at WebApplication1.Controllers.WeatherForecastController.Get(String city) in C:\\Users\\xxx\\Projects\\WebApplication1\\WebApplication1\\Controllers\\WeatherForecastController.cs:line 34",
"message": "City required: -",
"data": {},
"source": "WebApplication1",
"hResult": -2146233088
}
This would result in buggy generated clients, e.g. Typescript:
export class Exception implements IException {
...
init(_data?: any) {
if (_data) {
this.stackTrace = _data["StackTrace"]; // FAIL!
this.message = _data["Message"]; // FAIL!
this.innerException = _data["InnerException"] ? Exception.fromJS(_data["InnerException"]) : <any>undefined; // FAIL!
this.source = _data["Source"]; // FAIL!
}
}
...
}