NSwag generated client still serializes string for text/plain body - asp.net-core

UPDATE: I went ahead and created an issue on NSwag after looking through its code. You can see it here: https://github.com/RicoSuter/NSwag/issues/3824
I have a functioning api endpoint that accepts "text/plain" as the body. The user of this endpoint is a third-party that controls the content type so I can't change that. I'm using NSwag to generate a c# client based on a swagger.json.
My main problem is that the generated client's function for calling this endpoint still serializes the string body, which causes the endpoint to fail to parse. I'm not 100% certain if this is a bug in my swagger.json or if it's something unintentional, because the json is also generated. This is why I'm not posting this on the NSwag github issues currently.
Here's what the swagger.json looks like:
{
"openapi": "3.0.1",
"info": {
"title": "PlainTextParser",
"version": "1.0"
},
"paths": {
"/api/v1/text": {
"post": {
"tags": [
"TextEvent"
],
"summary": "Parses a plain text body",
"operationId": "PostTextEvent",
"requestBody": {
"description": "Accepts a plain text body",
"content": {
"text/plain": {
"schema": {
"type": "string",
"nullable": true
}
}
}
},
"responses": {
"202": {
"description": "Success"
}
}
}
}
},
"components": { }
}
When I run nswag on it, I get this inside of the generated client:
public virtual async System.Threading.Tasks.Task PostTextEventAsync(string body, System.Threading.CancellationToken cancellationToken)
{
//...
var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value));
content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("text/plain");
//...
}
The SerializeObject method call is what is causing the issue. When I removed it everything worked fine. You can even see on the second line there that it understands the content type, which is why I'm confused why it's trying to serialize it.
So is there something I'm missing in the swagger.json or is there a bug in NSwag?
I do realize I can work around this by making my API endpoint reformat the string to its intended state, but that feels like a hack.

Related

Why doesn't GraphQL.NET honour the errors.extensions schema?

I recently rewrote some GraphQL services from Java to .NET Core.
In Java, I was able to provide custom error messages to the clients using the errors.extensions in the response, ie:
{
"data": {
"someMutation": null
},
"errors": [{
"cause": null,
"message": "Unauthorized",
"httpStatusCode": 0,
"extensions": {
"uiMessage": "Oh no, your session expired. You'll need to login again to continue.",
"httpStatusDescription": "Unauthorized",
"httpStatusCode": 401
},
"errorType": "ValidationError",
"path": null,
"localizedMessage": "Unauthorized",
"suppressed": []
}
]
}
However, in .NET, I don't seem to be able to replicate this format.
ErrorInfo.Extensions is added to the root of the response, not to the the Errors object itself, eg:
{
"data": {
"someMutation": null
},
"errors": [{
"message": "Auth token not provided"
}
],
"extensions": {
"httpStatusCode": 401,
"httpStatusDescription": null,
"uiMessage": "Oh no, your session expired. You'll need to login again to continue.",
}
}
The GraphQL spec reads (ref https://spec.graphql.org/October2021/#sec-Errors, https://spec.graphql.org/October2021/#example-8b658):
GraphQL services may provide an additional entry to errors with key
extensions. This entry, if set, must have a map as its value. This
entry is reserved for implementors to add additional information to
errors however they see fit, and there are no additional restrictions
on its contents.
eg:
{
"errors": [
{
"message": "Name for character with ID 1002 could not be fetched.",
"locations": [{ "line": 6, "column": 7 }],
"path": ["hero", "heroFriends", 1, "name"],
"extensions": {
"code": "CAN_NOT_FETCH_BY_ID",
"timestamp": "Fri Feb 9 14:33:09 UTC 2018"
}
}
]
}
I created a new test project (.NET Core 3.1) using the latest versions of the libraries (GraphQL 7.1.1 et al) but am still unable to add custom properties to errors.extensions.
This is the test mutation which intentionally throws an exception:
Field<StringGraphType>("greet")
.Argument<NonNullGraphType<StringGraphType>>("name")
.Resolve(context => {
try {
throw new Exception("Invalid input");
return "Hello " + context.GetArgument<String>("name");
} catch(Exception ex) {
// This doesn't seem to get returned anywhere in the response
Dictionary<String, object> extraData = new Dictionary<string, object>();
extraData.Add("error1", "message1");
// Add the error to the response using the overloaded constructor
context.Errors.Add(new ExecutionError("Oh dear, that went wrong", extraData));
// This gets added to the root of the response
context.OutputExtensions.Add("error2", "message2");
return null;
}
});
the mutation to invoke it:
mutation {greet(name:"Chewbacca")}
and the response (I don't know where errors.extensions.details comes from):
{
"errors": [
{
"message": "Oh dear, that went wrong",
"extensions": {
"details": "GraphQL.ExecutionError: Oh dear, that went wrong"
}
}
],
"data": {
"greet": null
},
"extensions": {
"error2": "message2"
}
}
I would imagine that the GraphQL.NET library would expose an Extensions dictionary on the ExecutionError object so one could add custom values in the usual manner, eg:
ExecutionError executionError = new ExecutionError("Oh dear, that went horribly wrong");
executionError.Extensions.Add("customError", "Your custom error here")
context.Errors.Add(executionError);
Which would result in a response similar to this:
{
"data": {
"someMutation": null
},
"errors": [{
"message": "Oh dear, that went horribly wrong",
"extensions": {
"customError": "Your custom error here"
}
}
]
}
I am hopeful that some bright individual in the community can (slap me upside the head and) point me in the right direction.

Can you set an Application's Client Secret using a kickstart file? FusionAuth

I am using a kickstart.json file to setup FusionAuth in developer environments. Everything is automated except I still need to manually go and get the client secret from the fusion auth instance.
Is there anyway I can predefine the client secret in the kickstart file so I can pre-configure it in my app?
you should absolutely be able to set the client secret from kickstart.json. Any API call should work from within Kickstart.
https://fusionauth.io/docs/v1/tech/apis/applications#create-an-application indicates you can POST an application including the client secret.
So a kickstart file like this should work:
{
"variables": {
"defaultTenantId": "30663132-6464-6665-3032-326466613934"
},
"apiKeys": [
{
"key": "mykey",
"description": "API key"
}
],
"requests": [
{
"method": "POST",
"url": "/api/application/85a03867-dccf-4882-adde-1a79aeec50df",
"body": {
"application": {
"name": "Pied Piper",
"roles": [
{
"name": "dev"
},
{
"name": "ceo"
},
{
"name": "intern"
}
],
"oauthConfiguration" : {
"clientSecret": "shhh-your-desired-secret"
}
}
}
}
]
}
I haven't tested that, but don't see any reason why it would not work. (Note that 1.37, the most recent version, has an issue with kickstart as documented here: https://github.com/FusionAuth/fusionauth-issues/issues/1816 but that should be fixed soon.)
If this doesn't work for you, please share the error message and a scrubbed kickstart file.

Changing $ref in Swashbuckle Swagger Generation for response object

I'm producing a swagger for a simple api that returns an object which correctly generates the swagger response similar to the below. My question is, is it possible to change the definition from myCustomerMadeUpResponse to customerResponse.
"responses": {
"200": {
"description": "Success",
"schema": {
"$ref": "#/definitions/myCustomerMadeUpResponse"

Return string from Web API .NET Core get operation

I have a get operation that I want to return a string from. An example would be
"000875"
When I return this string from a controller in my Web API Controller in full .NET, it formats it like this:
{
"Property": "000875"
}
When I return the string in my converted .NET Core Controller it returns this:
{
"$id": "1",
"$type": "System.Net.Http.HttpResponseMessage, System.Net.Http",
"Version": "1.1",
"Content": {
"$id": "2",
"$type": "System.Net.Http.StringContent, System.Net.Http",
"Headers": [
{
"Key": "Content-Type",
"Value": [
"application/json; charset=utf-8"
]
}
]
},
"StatusCode": "OK",
"ReasonPhrase": "OK",
"Headers": [],
"TrailingHeaders": [],
"RequestMessage": null,
"IsSuccessStatusCode": true
}
It is interesting to note that the value is not even in there!
I am running some interesting JSON Serialization to make BreezeJs work with .NET Core. It is possible that it is the cause of this weirdness:
.AddNewtonsoftJson(opt =>
{
// Let Breeze say how we serialize. This adds in the Type and Id options the way breeze expects
var jsonSerializerSettings = JsonSerializationFns.UpdateWithDefaults(opt.SerializerSettings);
......
I am hoping for a way to get strings through without all this mess. Can that be done?
I get the impression that the subject action definition returns HttpResponseMessage.
public HttpResponseMessage MyAction(....
HttpRequestMessage is no longer a first class citizen in asp.net-core framework and will be treated as a normal model and serialized.
That explains the JSON you are seeing with your controller
The syntax needs to be updated to return IActionResult derived responses
public IActionResult MyAction() {
//...
return Ok("000875");
}
ActionResult<T>
public ActionResult<string> MyAction() {
//...
if(somecondition)
return NotFound();
return "000875";
}
or the model itself.
public string MyAction() {
//...
return "000875";
}
Reference Controller action return types in ASP.NET Core Web API

oauth2orize redirect_uri issue

I'm attempting to customize the oauth2orize all-grants example for my use. I can run the all-grants as-is and everything works (as you would expect), but when I run my customized version, I always end up with this error:
Error: Unable to issue redirect for OAuth 2.0 transaction
at Object.response [as handle] (C:\Dev\Expy\api\node_modules\oauth2orize\lib\grant\code.js:122:41)
I've been digging into this a bit and it seems there is a property of the txn variable within that function that should be named redirectURI and should be populated with the redirect_uri from the query string of the initial request to the /dialog/authorize page. For some reason this doesn't happen on my example app. Is this caused by an express version difference? That is the biggest difference that I see between the example code and my customizations. The all-grants uses express 2.* and my app will use express 4.*.
If it isn't an express version issue, where should I start looking in my code for the issue?
For reference, this is what I see in my app for the txn object:
txn: {
"transactionID": "evlUd2q4",
"client": { ... },
"req": {
"type": "code",
"clientID": "5C3B4438-433F-11E5-A532-74653C701F13"
},
"user": { ... },
"res": {
"allow": true
}
}
and this is what I see in that same object with the example (note the presence of the redirectURI in req and in the txn itself):
txn: {
"transactionID": "EEcYp3Uj",
"client": { ... },
"redirectURI": "http://localhost:3000/api/userinfo",
"req": {
"type": "code",
"clientID": "abc123",
"redirectURI": "http://localhost:3000/api/userinfo"
},
"user": { ... },
"res": {
"allow": true
}
}