Swagger / Swashbuckle string property options - .net-core

We have an API in our .Net Core 3.1 project that has a controller which returns a model that is documented by Swagger (Generated by Swashbuckle).
The consumer of the endpoint would like to see the options that a given property may return. For several reasons this for now at least is not just an Enum which would make the process a lot easier. It is a string that can return two different values.
Is there any way to decorate the property so that the options are available in Swagger just as if it had been an Enum? For Enums we can use the MapType on startup, but this is just a string so it's not a type we can map perse.
This is how we have done it with Enums previously:
c.MapType<PaginationSessionType>(() => new OpenApiSchema
{
Type = "string",
Enum = typeof(PaginationSessionType).GetEnumNames()
.Select(name => new OpenApiString(name)).Cast<IOpenApiAny>().ToList()
});

http://swagger-net-test.azurewebsites.net/swagger/ui/index#/TestStringEnum/TestStringEnum_Post
I was able to do what you need with "swagger": "2.0" not 100% sure with the version you are using... At that time I got what I needed with the RegularExpression decorator.
here is the code that generates that:
https://github.com/heldersepu/Swagger-Net-Test/blob/master/Swagger_Test/Controllers/TestStringEnumController.cs
using System.Web.Http;
using System.ComponentModel.DataAnnotations;
namespace Swagger_Test.Controllers
{
public class MyEndpointRequestClass
{
[RegularExpression("^(dark-blue|dark-red|light-blue|light-red)")]
public string StringEnumColor { get; set; }
[RegularExpression("^(high|medium|low)")]
public string Transparency { get; set; }
public string Name { get; set; }
}
public class TestStringEnumController : ApiController
{
public string Post([FromUri] MyEndpointRequestClass r)
{
return r.StringEnumColor;
}
public string Get([RegularExpression("^(uno|due)")]string x)
{
return x;
}
}
}
the relevant swagger.json from that is:
...
"MyEndpointRequestClass": {
"properties": {
"StringEnumColor": {
"type": "string",
"pattern": "^(dark-blue|dark-red|light-blue|light-red)"
},
"Transparency": {
"type": "string",
"pattern": "^(high|medium|low)"
},
"Name": {
"type": "string"
}
},
"xml": {
"name": "MyEndpointRequestClass"
},
"type": "object"
},
...
This might not answer you question but hopefully it sends you on the right direction

Related

Swagger not recognizing JsonProperty("PropertyName") as request property

I have a web api in .net core3.1 and I am using swagger.
One of my request data class is as below.
public class SNMPv1ReqData{
[JsonProperty("snmpV1Info")]
[MinLength(1)]
[MaxLength(2000)]
[Required]
public List<SNMPv1Info> SNMPv1InfoLst { get; set; }
}
public class SNMPv1Info{
[JsonProperty("host")]
[Required]
public string HostName { get; set; }
[JsonProperty("snmpV1Setting")]
public SNMPv1Setting SNMPv1Setting { get; set; }
[JsonProperty("oid")]
[MinLength(1)]
[MaxLength(1000)]
[Required]
public string[] OID { get; set; }
}
In swagger request is shown as below as it is mentioned in JsonProperty("PropertyName")
{
"snmpV1Info": [
{
"host": "string",
"snmpV1Setting": {
"retryCount": 0,
"timeout": 0,
"port": 0,
"communityName": "string"
},
"oid": [
"string"
]
}
]
}
But when I send request it is showing the below error.
{
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"SNMPv1InfoLst": [
"The SNMPv1InfoLst field is required."
]
}
}
SNMPv1InfoLst is variable name but I want to use "snmpV1Info" in api request which is mentioned in JsonProperty("snmpV1Info") also showing "snmpV1Info" in swagger request.
Startup.cs
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1.0.0", new Microsoft.OpenApi.Models.OpenApiInfo
{
Title = "WebAPI",
Version = "v1.0",
Description = "Edge ASP.NET Core Web API",
});
// Set the comments path for the Swagger JSON and UI.
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
services.AddSwaggerGenNewtonsoftSupport();
Swashbuckle.AspNetCore : 5.6.3
Swashbuckle.AspNetCore.Newtonsoft : 5.6.3
.Net Core3.1
I am not able to find the cause of this problem. Can anyone help me ?

Working with restricted string field values in .NET Core WebAPI 3.1

I'm working on a webapi project using .netcore.
I have a model with the following properties:
public class Criterial {
[Required]
public string Field { get; set; }
[Required]
public Operator Operator { get; set; }
[Required]
public string Value { get; set; }
public bool Result { get; set; }
}
public enum Operator {
greater_than,
equal_to,
lower_than
}
I'm trying to use enum to restrict the values that the Operator propertie can receive, but when I make a POST request to the API I got the following scenario:
POST Request Body:
"criterials": [
{
"field": "amount",
"operator": "greater_than",
"value": "50"
}
]
Response from the API:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "|7e53377-444fa4a723ac655c.",
"errors": {
"$.criterials[0].operator": [
"The JSON value could not be converted to LeagueOfFateApi.Models.Operator. Path: $.criterials[0].operator | LineNumber: 5 | BytePositionInLine: 26."
]
}
}
Searching about the issue on the internet I found the [JsonConverter(typeof(JsonStringEnumConverter))] Data Annotation.
So I added it to my code and the issue was "solved":
[Required]
[JsonConverter(typeof(JsonStringEnumConverter))]
public Operator Operator { get; set; }
New response from the API:
"criterials": [
{
"field": "amount",
"operator": "greater_than",
"value": "50",
"result": false
}
]
The problem is: in my MongoDB collection a new document was saved with the int value 0 of the enums, and not the string value "greater_than":
"Criterials" : [
{
"Field" : "amount",
"Operator" : 0,
"Value" : "50",
"Result" : false
}
]
Besides, another problem is that the "criterial" field can receive any int value with no restrictions.
Is there any other practical way to restrict a string's options without using enums? Or is there anything I can add to this solution using enums?
Thank you very much for your attention and your time!
According to your description, I suggest you could write custom set and get method for the Operator property.
You could set the Operator's type is string and use Enum.IsDefined to check the Operator value is enum Operator or not.
More details, you could refer to below codes:
public class Criterial
{
[Required]
public string Field { get; set; }
private string _Operator;
[Required]
public string Operator {
get {
return this._Operator;
}
set {
if (Enum.IsDefined(typeof(Operator), value))
{
this._Operator = value;
}
else
{
this._Operator = "Error you used wrong string";
}
}
}
[Required]
public string Value { get; set; }
public bool Result { get; set; }
}
public enum Operator
{
greater_than,
equal_to,
lower_than
}
Result:

.NET Core get array in array from config.json

I have an issue where an array object in my config.json is coming back as empty. In the following code, gridFields will come back as empty.
{"grids": [{
"name": "Grid1"
"gridFields": [
{
"Name": "Something",
"Label": "Something"
},
{
"Name": "SomethingElse",
"Label": "SomethingElse"
}]
},
{"name": "Grid2"
"gridFields": [
{
"Name": "Something",
"Label": "Something"
}]
}]
}
I have matching POCOs and made sure the names match up as follows.
public class Grid
{
public string name { get; set; }
public gridFields gridFields {get; set;}
}
public class gridFields
{
public List<gridField> GridFields { get; set; } = new List<gridField>();
public int Count => GridFields.Count();
public IEnumerator GetEnumerator()
{
return GridFields.GetEnumerator();
}
}
public class gridField
{
public string Name { get; set; }
public string Label { get; set; }
}
In my startup I have the following
public void ConfigureServices(IServiceCollection services)
{ services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
var config = new List<Grid>();
Configuration.Bind("grids", config);
services.AddSingleton(config);
}
The config.gridFields ends up holding no values. I have values for name, but not gridFields. Even if I make gridFields List it comes back null.
My question is if there is someway following this current code that I can get the data out of this array in array, or do I need to do something completely different. Why isn't .net core able to bind every object it comes across underneath the parent?
The example json misses a , after the name of each grid.
{
"name": "Grid2",
"gridFields":
[{
"Name": "Something",
"Label": "Something"
}]
}
You have a list of gridField directly under the Grid in the Json.
In the Code however you use another object, GridFields.
You should remove the gridfiels class and use a list of gridField in the Grid class:
public class Grid
{
public string name { get; set; }
public List<gridField> gridFields {get; set;}
}

Parsing device listing from Urban Airship with JSON.Net

For the life of me, I can't figure out how to parse the collection of device_tokens out of this using JSON.Net. I can parse out the top level collection fine, but am bombing on parsing out the device tokens in any way shape or form. Anyone have any ideas?
{
"next_page": "https://go.urbanairship.com/api/device_tokens/?start=<MY_TOKEN>&limit=2",
"device_tokens_count": 87,
"device_tokens": [
{
"device_token": "<MY_TOKEN>",
"active": false,
"alias": null,
"tags": []
},
{
"device_token": "<MY_TOKEN>",
"active": true,
"alias": null,
"tags": ["tag1", "tag2"]
}
],
"active_device_tokens_count": 37
}
Heres how you can do it using Json.NET
First create a class to represent a single device_token:
public class DeviceToken
{
public string device_token { get; set; }
public bool active { get; set; }
public object alias { get; set; }
public List<object> tags { get; set; }
}
Then using the JsonConvert class you can deserialize the json device_token array to a list of DeviceToken objects.
string json = "{\"next_page\": \"https://go.urbanairship.com/api/device_tokens/?start=07AAFE44CD82C2F4E3FBAB8962A95B95F90A54857FB8532A155DE3510B481C13&limit=2\",\"device_tokens_count\": 87,\"device_tokens\": [{\"device_token\": \"0101F9929660BAD9FFF31A0B5FA32620FA988507DFFA52BD6C1C1F4783EDA2DB\",\"active\": false,\"alias\": null,\"tags\": []},{\"device_token\": \"07AAFE44CD82C2F4E3FBAB8962A95B95F90A54857FB8532A155DE3510B481C13\",\"active\": true,\"alias\": null,\"tags\": [\"tag1\", \"tag2\"] }],\"active_device_tokens_count\": 37}";
JObject obj = JObject.Parse(json);
var deviceTokens = JsonConvert.DeserializeObject<List<DeviceToken>>(obj["device_tokens"].ToString());

JSON in C# (preferably without third-party tools)

I've seen quite a few ways to output JSON in C#. None of them, however, seem easy. This may very well be due to my lack of knowledge of C#, though.
Question: If you were to write JSON (for use in JavaScript later on), how would you do it? I would prefer to not use third-party tools; however this is not an ultimatum of any kind. Is JSON doable in C# ASP.NET out of the box?
Example of JSON I wish to write:
{
"Trips": [
{
"from": "here",
"to": "there",
"stops": [
{
"place": "middle1",
"KMs": 37
},
{
"place": "middle2",
"KMs": 54
}
]
},
{
"from": "there",
"to": "here",
"stops": [
{
"place": "middle2",
"KMs": 37
},
{
"place": "middle1",
"KMs": 54
}
]
}
]
}
How about JavaScriptSerializer? Nice integrated solution, allowed to be expanded upon with JavaScriptConverters.
Demo of what you were after:
using System;
using System.Web.Script.Serialization;
using System.Text;
public class Stop
{
public String place { get; set; }
public Int32 KMs { get; set; }
}
public class Trip
{
public String from { get; set; }
public String to { get; set; }
public Stop[] stops { get; set; }
}
public class MyJSONObject
{
public Trip[] Trips { get; set; }
public override String ToString()
{
StringBuilder sb = new StringBuilder();
foreach (Trip trip in this.Trips)
{
sb.AppendFormat("Trip:\r\n");
sb.AppendFormat("\t To: {0}\r\n", trip.to);
sb.AppendFormat("\t From: {0}\r\n", trip.from);
sb.AppendFormat("\tStops:\r\n");
foreach (Stop stop in trip.stops)
{
sb.AppendFormat("\t\tPlace: {0}\r\n", stop.place);
sb.AppendFormat("\t\t KMs: {0}\r\n", stop.KMs);
}
sb.AppendLine();
}
return sb.ToString();
}
}
public class Test
{
public static void Main()
{
String example = "{\"Trips\":[{\"from\":\"here\",\"to\":\"there\",\"stops\":[{\"place\":\"middle1\",\"KMs\":37},{\"place\":\"middle2\",\"KMs\":54}]},{\"from\":\"there\",\"to\":\"here\",\"stops\":[{\"place\":\"middle2\",\"KMs\":37},{\"place\":\"middle1\",\"KMs\":54}]}]}";
JavaScriptSerializer serializer = new JavaScriptSerializer();
// Parse the string to our custom object
MyJSONObject result = serializer.Deserialize<MyJSONObject>(example);
Console.WriteLine("Object deserialized:\r\n\r\n{0}\r\n\r\n", result);
// take our custom object and dump it in to a string
StringBuilder sb = new StringBuilder();
serializer.Serialize(result, sb);
Console.WriteLine("Object re-serialized:\r\n\r\n{0}\r\n\r\n", sb);
// check step
Console.WriteLine(example.Equals(sb.ToString()) ? "MATCH" : "NO match");
}
}
And the output:
Object deserialized:
Trip:
To: there
From: here
Stops:
Place: middle1
KMs: 37
Place: middle2
KMs: 54
Trip:
To: here
From: there
Stops:
Place: middle2
KMs: 37
Place: middle1
KMs: 54
Object re-serialized:
{"Trips":[{"from":"here","to":"there","stops":[{"place":"middle1","KMs":37},{"pl
ace":"middle2","KMs":54}]},{"from":"there","to":"here","stops":[{"place":"middle
2","KMs":37},{"place":"middle1","KMs":54}]}]}
MATCH
Press any key to continue . . .
There is JavaScriptSerializer from System.Web.Extensions.dll (.NET 3.5 SP1)
http://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer.aspx
You should really take a look at Json.NET
Not only does this library perform superbly it can parse JSON as well. Which means you can easily round trip stuff and communicate over JSON without involving a WCF service layer. It also does other neat things such as providing a queryable dynamic object model.

Resources