Documenting Post DTO for OpenAPI Spec

I am having difficulty successfully annotating a dto for open api. Below is the annotations I have addeded to the class.
The offending section is:

    "parameters": [
      {
        "name": "WorkWeek",
        "in": "body",
        "type": "string",
        "required": false,
        "enum": [
          "Empty",
          "NineToFive",
          "EightToFiveWithLunchHour",
          "TwentyFourByFive",
          "TwentyForBySeven"
        ],
        "x-nullable": false
      }
    ]

With error message:
Schema error at paths./schedules.post.parameters[0]
is not exactly one from <#/definitions/parameter>,<#/definitions/jsonReference>

Code for DTO.

[Api(ClientConstants.ServiceName)]
    [Route("/schedules", Verbs = "POST", 
        Summary = ClientConstants.CreateSchedule.Summary)]
    public class CreateSchedule
    {
        [ApiMember(Name= "WorkWeek", DataType = "string", Description = "The type of workweek to initialize the schedule with.",
            ParameterType = "body")]
        [ApiAllowableValues("WorkWeek", typeof(WorkWeekType))]
        public WorkWeekType WorkWeek { get; set; }
    }

Any help would be appreciated.
ian

What tool shows you this error? If you manually remove "required":false from json does this tool show the error?

Hi, I’m trying to import the spec into aws api gateway. The tool that gave me the error is:
http://editor.swagger.io/#/
as well as
http://bigstickcarpet.com/swagger-parser/www/index.html

So the error only occurs on (http://bigstickcarpet.com/swagger-parser/www/index.html) if I validate against the swagger schema, however passes validation if I validate against the spec.

My final goal is to import this spec into AWS API Gateway. It sounds like I may be missing something between what OpenAPI and Swagger 2.0 actually are. I’m not knowledgeable in these areas really at all. If there is a conversion available (assuming they are different things) that you know about that would be great. Again though, my goal is to import the swagger 2.0 (which I thought was analogous to OpenAPI but perhaps not) into the AWS API Gateway.

Thanks for your help,
ian

if you import openapi json to AWS API do you have any errors? Various validation tools may show different messages on the same json. I see only one issue with your json, that type is defined as "x-nullable": false so it cannot be null and must have value, but it does not have "required": true set. So setting IsRequired=true in ApiMember attribute should resolve this validation error in swagger UI, but anyway if you target goal to import to AWS API you show test your json on Amazon API Gateway, because their validation can differ from swagger editor validation.

[ApiMember(Name= "WorkWeek", DataType = "string", Description = "The type of workweek to initialize the schedule with.", ParameterType = "body", IsRequired = true)]

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?

@mikesheen this is another issue. The parameters must not have both body and formData in the same request. From this DTO property Name should be transfered to query parameter type, possible the type was incorrectly set because the parameter is inherited. I am going to fix this.

As a workaround you can set DisableAutoDtoInBodyParam to true, but this will disable generating body for all parameters.

Plugins.Add(new OpenApiFeature
{
    DisableAutoDtoInBodyParam = true
});

Also you can use OperationFilter to change In property in Parameter to query

OpenAPI 2.0 does not allow to use different parameters for different content-types on the same endpoint (e. g. use formData property parameters with application/x-www-form-urlencoded and use the whole DTO in json with application/json) and this feature will be available only in OpenAPI 3.0

To be json valid I changed the generation in this commit So now if DisableAutoDtoInBodyParam=false and DTO is used as body then all previous formData parameters are generated as query parameter type and consumes = application/json
If DisableAutoDtoInBodyParam=true then all parameters in POST and PUT are generated as formData parameter type and consumes is application/x-www-form-urlencoded

This change is available from v4.5.9 on MyGet