I also see issues with online validators not liking the OPenAPI document generated by ServiceStack. One I can work around, the other I’m not seeing a way around it:
TL;DR : I get the following error reported by validators on the POST operations:
Operations cannot have both a “body” parameter and “formData” parameter
To illustrate this in a simple, clean test, I made a new SS 4.5.8 service with the following:
Model:
public class Pet
{
virtual public int? PetId { get; set; }
virtual public string Name { get; set; }
}
Request DTO:
[Route("/Pets", "POST")]
public class PetsPOSTRequest : Pet, IReturn<Pet>
{
[System.Runtime.Serialization.IgnoreDataMember]
public override int? PetId { get; set; }
}
Service:
public class PetServices : Service
{
public Pet POST(PetsPOSTRequest request)
{
return request;
}
}
The AppHost:
public override void Configure(Container container)
{
this.Plugins.Add(new ServiceStack.Api.OpenApi.OpenApiFeature();
}
This results in an openapi document like so:
{
"swagger" : "2.0",
"info" : {
"title" : "ssTestServer2",
"version" : "1.0"
},
"host" : "localhost:32999",
"basePath" : "/",
"schemes" : ["http"],
"consumes" : ["application/json"],
"produces" : ["application/json"],
"paths" : {
"/Pets" : {
"post" : {
"tags" : ["/Pets"],
"operationId" : "PetsPOSTRequest_Post",
"consumes" : ["application/json"],
"produces" : ["application/json"],
"parameters" : [{
"name" : "Name",
"in" : "formData",
"type" : "string"
}, {
"name" : "body",
"in" : "body",
"schema" : {
"$ref" : "#/definitions/PetsPOSTRequest"
}
}
],
"responses" : {
"default" : {
"description" : "",
"schema" : {
"$ref" : "#/definitions/Pet"
}
}
},
"deprecated" : false
},
"parameters" : [{
"$ref" : "#/parameters/Accept"
}
]
}
},
"definitions" : {
"Object" : {
"properties" : {},
"description" : "Object",
"type" : "object"
},
"PetsPOSTRequest" : {
"properties" : {
"Name" : {
"type" : "string"
}
},
"description" : "PetsPOSTRequest",
"type" : "object"
},
"Pet" : {
"properties" : {
"PetId" : {
"type" : "integer",
"format" : "int32"
},
"Name" : {
"type" : "string"
}
},
"description" : "Pet",
"type" : "object"
}
},
"parameters" : {
"Accept" : {
"name" : "Accept",
"in" : "header",
"description" : "Accept Header",
"type" : "string",
"required" : true,
"enum" : ["application/json"]
}
},
"securityDefinitions" : {
"basic" : {
"type" : "basic"
}
},
"tags" : [{
"name" : "/Pets"
}
]
}
This will produce 2 errors when validated by online validators such as the ones ticky74 mentioned (e.g. http://editor.swagger.io):
Semantic error at paths./Pets.post
Operations with Parameters of “in: formData” must include “application/x-www-form-urlencoded” or “multipart/form-data” in their “consumes” property
And
Semantic error at paths./Pets.post.parameters
Operations cannot have both a “body” parameter and “formData” parameter
The first error I can get around by adding an Operation and ApiDeclaration filter to add FormUrlEncoded to the consumes:
public override void Configure(Container container)
{
this.Plugins.Add(new ServiceStack.Api.OpenApi.OpenApiFeature()
{
OperationFilter = (verb, operation) =>
{
operation.Consumes = new[] { MimeTypes.FormUrlEncoded, MimeTypes.Json, MimeTypes.Xml }.ToList();
operation.Produces = new[] { MimeTypes.Json, MimeTypes.Xml }.ToList();
},
ApiDeclarationFilter = api =>
{
api.Consumes = new[] { MimeTypes.FormUrlEncoded, MimeTypes.Json, MimeTypes.Xml }.ToList();
api.Produces = new[] { MimeTypes.Json, MimeTypes.Xml }.ToList();
}
});
}
Now - as for the second error - according to the OpenAPI 2.0 specification:
Since form parameters are sent in the payload, they cannot be declared together with a body parameter for the same operation
I’m not sure yet if this “error” has any material consequences or not - I’m not quite at the stage where I’m going to be testing the OpenAPI document with services such as Azure API management, Zapier, et cetera - but given the spec clearly states that you can’t have body parameters and form parameters in the same operation, at best will lead to unexpected or inconsistent behaviors by those services parsing / interpreting the document.
Is there a way we can work around this via the Operation or APIDeclaration filters?