RicoSuter/NSwag

FileResponse is incorrectly deserialized for Error Responses

Opened this issue · 0 comments

When using a swagger definition that includes error responses of the file type, the generated client doesn't really work correctly.

See the swagger doc and client snippets at the bottom, but here's the relevant part, for a 400 response the client does this:

var objectResponse_ = await ReadObjectResponseAsync<FileResponse>(response_, headers_, cancellationToken).ConfigureAwait(false);

ReadObjectResponseAsync tries to deserialize the response as a FileResponse

Which obviously doesn't work, the FileResponse is just meant to hold the stream, headers, etc...

For error responses that have type File NSwag should either correctly produce a FileResponse, or if that's not acceptable, could we just use the default unknown response handling, ie:

else
{
     var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
     throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
    ...
    "/MY/PATH": {
      "post": {
        "tags": [
          "MYTAG"
        ],
        "summary": "summary",
        "operationId": "MYOPERATIONID",
        "consumes": [
          "application/json",
          "text/json",
          "application/*+json"
        ],
        "produces": [
          "application/octet-stream",
          "application/json"
        ],
        "parameters": [
          {
            "in": "body",
            "name": "request",
            "description": "description",
            "schema": {
              "$ref": "#/definitions/MYREQUESTOBJECT"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "schema": {
              "format": "byte",
              "description": "description",
              "type": "file"
            }
          },
          "400": {
            "description": "description",
            "schema": {
              "format": "byte",
              "description": "description",
              "type": "file"
            }
          },
          "401": {
            "description": "description",
            "schema": {
              "format": "byte",
              "description": "description",
              "type": "file"
            }
          },
          "403": {
            "description": "description",
            "schema": {
              "format": "byte",
              "description": "description",
              "type": "file"
            }
          },
          "500": {
            "description": "description",
            "schema": {
              "format": "byte",
              "description": "description",
              "type": "file"
            }
          },
          "503": {
            "description": "description",
            "schema": {
              "format": "byte",
              "description": "description",
              "type": "file"
            }
          }
        }
      }
    },
    ...

Generated using the NSwag toolchain v14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org)

public virtual async System.Threading.Tasks.Task<FileResponse> MYMETHODAsync(MYREQUESTTYPE request = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
        {
            var client_ = _httpClient;
            var disposeClient_ = false;
            try
            {
                using (var request_ = new System.Net.Http.HttpRequestMessage())
                {
                    var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(request, _settings.Value);
                    var content_ = new System.Net.Http.ByteArrayContent(json_);
                    content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");
                    request_.Content = content_;
                    request_.Method = new System.Net.Http.HttpMethod("POST");
                    request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json"));

                    var urlBuilder_ = new System.Text.StringBuilder();
                    if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
                    // Operation Path: "api/v1/VehicleCatalog/CurrentStreamed"
                    urlBuilder_.Append("api/v1/VehicleCatalog/CurrentStreamed");

                    PrepareRequest(client_, request_, urlBuilder_);

                    var url_ = urlBuilder_.ToString();
                    request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);

                    PrepareRequest(client_, request_, url_);

                    var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
                    var disposeResponse_ = true;
                    try
                    {
                        var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
                        foreach (var item_ in response_.Headers)
                            headers_[item_.Key] = item_.Value;
                        if (response_.Content != null && response_.Content.Headers != null)
                        {
                            foreach (var item_ in response_.Content.Headers)
                                headers_[item_.Key] = item_.Value;
                        }

                        ProcessResponse(client_, response_);

                        var status_ = (int)response_.StatusCode;
                        if (status_ == 200 || status_ == 206)
                        {
                            var responseStream_ = response_.Content == null ? System.IO.Stream.Null : await response_.Content.ReadAsStreamAsync().ConfigureAwait(false);
                            var fileResponse_ = new FileResponse(status_, headers_, responseStream_, null, response_);
                            disposeClient_ = false; disposeResponse_ = false; // response and client are disposed by FileResponse
                            return fileResponse_;
                        }
                        else
                        if (status_ == 400)
                        {
                            var objectResponse_ = await ReadObjectResponseAsync<FileResponse>(response_, headers_, cancellationToken).ConfigureAwait(false);
                            if (objectResponse_.Object == null)
                            {
                                throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
                            }
                            throw new ApiException<FileResponse>("The input format was incorrect, could not be parsed, or was missing data.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
                        }
                        else
                        if (status_ == 401)
                        {
                            var objectResponse_ = await ReadObjectResponseAsync<FileResponse>(response_, headers_, cancellationToken).ConfigureAwait(false);
                            if (objectResponse_.Object == null)
                            {
                                throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
                            }
                            throw new ApiException<FileResponse>("The user is not authorized for the endpoint.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
                        }
                        else
                        if (status_ == 403)
                        {
                            var objectResponse_ = await ReadObjectResponseAsync<FileResponse>(response_, headers_, cancellationToken).ConfigureAwait(false);
                            if (objectResponse_.Object == null)
                            {
                                throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
                            }
                            throw new ApiException<FileResponse>("The user is not authorized for the Dataset.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
                        }
                        else
                        if (status_ == 500)
                        {
                            var objectResponse_ = await ReadObjectResponseAsync<FileResponse>(response_, headers_, cancellationToken).ConfigureAwait(false);
                            if (objectResponse_.Object == null)
                            {
                                throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
                            }
                            throw new ApiException<FileResponse>("The process threw an error.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
                        }
                        else
                        if (status_ == 503)
                        {
                            var objectResponse_ = await ReadObjectResponseAsync<FileResponse>(response_, headers_, cancellationToken).ConfigureAwait(false);
                            if (objectResponse_.Object == null)
                            {
                                throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
                            }
                            throw new ApiException<FileResponse>("The service is unavailable.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
                        }
                        else
                        {
                            var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
                            throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
                        }
                    }
                    finally
                    {
                        if (disposeResponse_)
                            response_.Dispose();
                    }
                }
            }
            finally
            {
                if (disposeClient_)
                    client_.Dispose();
            }
        }

Happy to try and fix this.