Client From NSwag CSharpClientGenerator Cannot Deserialize String
Asked Answered
R

3

6

We have a Swagger.json output by using AddOpenApiDocument (see below).

A snippet from the swagger as below shows it returns a 200 response type of application/json. The schema part (which I am not too familiar with) shows "type": "string".

When we generate and use a client from NSwag.CodeGeneration.CSharp.CSharpClientGenerator we get an error of:

SwaggerException: Could not deserialize the response body stream as System.String.
Status: 200
Response: 

 ---> Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: P. 
Path '', line 1, position 1.

swagger snippet

"/api/infrastructure": {
  "get": {
    "tags": [
      "Infrastructure"
    ],
    "operationId": "Infrastructure_Ping",
    "responses": {
      "200": {
        "description": "",
        "content": {
          "application/json": {
            "schema": {
              "type": "string"
            }
          }
        }
      }
    }
  }
}

The generate client code looks like this:

                    var status_ = (int)response_.StatusCode;
                    if (status_ == 200)
                    {
                        var objectResponse_ = await ReadObjectResponseAsync<string>(response_, headers_, cancellationToken).ConfigureAwait(false);
                        if (objectResponse_.Object == null)
                        {
                            throw new YadaYada.Library.Client.SwaggerException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
                        }
                        return objectResponse_.Object;
                    }
                    else
                    {
                        var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
                        throw new YadaYada.Library.Client.SwaggerException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
                    }

The capture from fiddler is:

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 41
Connection: keep-alive
Date: Mon, 26 Apr 2021 12:29:10 GMT
Cache-Control: no-store
Apigw-Requestid: eZDDBie8oAMEM7A=
X-Cache: Miss from cloudfront
Via: 1.1 ec8b1bfbf511818c606f196b49f871e2.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: IAD50-C2
X-Amz-Cf-Id: G90aS8nFabyc4j8jdQ8jHlWdD8GhSqwk-vx8G6gHWnyg-9BIfvYE1Q==

Pong3

What might we be doing wrong?

Resistant answered 25/4, 2021 at 14:48 Comment(2)
So when you use var objectResponse_ = await ReadObjectResponseAsync<string>(response_, headers_, cancellationToken).ConfigureAwait(false);,you get the error?What is your response_?Septivalent
The capture from fiddler is: HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8 Content-Length: 41 Connection: keep-alive Date: Mon, 26 Apr 2021 12:29:10 GMT Cache-Control: no-store Apigw-Requestid: eZDDBie8oAMEM7A= X-Cache: Miss from cloudfront Via: 1.1 ec8b1bfbf511818c606f196b49f871e2.cloudfront.net (CloudFront) X-Amz-Cf-Pop: IAD50-C2 X-Amz-Cf-Id: G90aS8nFabyc4j8jdQ8jHlWdD8GhSqwk-vx8G6gHWnyg-9BIfvYE1Q== PongResistant
O
3

There are a few parts needed for this, and most likely updated versions of code generation that weren't available when you first posted:

  1. Put these attributes on the route method, and use ActionResult<string>, not IActionResult. (I tried with various combinations of ProducesResponseType but couldn't get it to work):
    [HttpGet, Route("/infrastructure")]
    [Produces("text/plain")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    public async Task<ActionResult<string>> Ping() { ... }
  1. Ensure you don't generate contents other than text/plain, as this will confuse the code generator and make it prefer JSON. Your generated spec should look something like:
        "responses": {
        ...
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
  1. I had success with Microsoft.Extensions.ApiDescription.Client 6.0.10, not 3.x, and NSwag.ApiDescription.Client version 13.17.0 not 13.05. The older versions were automatically included by Visual Studio Connected Services.

~thanks to all the discussion here.

Ohl answered 24/10, 2022 at 12:6 Comment(0)
N
1

I believe the issue is that in the Swagger file you have a content of "application/json", but your response is not JSON.

You need to either tell the Swagger file to expect "text/plain" or have your endpoint return a JSON string.

Nathanielnathanil answered 28/9, 2021 at 15:2 Comment(2)
How? I have tried [Produces("text/plain") and [ProducesResponseType(typeof(string), (int)HttpStatusCode.OK, "text/plain")]. Neither seem to affect the produces Swagger output.Resistant
I would have to +1 @Dan's answer because, according to json.org/json-en.html, JSON always begins with { and ends with a }.Lohner
M
0

The issue is that your returning string is not in quotes, that is the reason why you're getting deserialize error. To fix it just remove .NET HttpNoContentOutputFormatter:

services.AddControllers(options =>
    {
        options.OutputFormatters.RemoveType<StringOutputFormatter>();
    });

As a result the service will produce "Pong3" content (with quotes), which is valid string type now.

Mature answered 22/7 at 20:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.