We have a Request object which has inside a single DTO member, which is a complex type.
[Route("/Hello", "POST", Summary = "Creates a new hello.")]
public class CreateHelloReq : IReturn<Hello>
{
[ApiMember(IsRequired = true, ParameterType = "body")]
public HelloDTO hello { get; set; }
}
We are seeing a number of issues:
IsRequired field on ApiMember attribute is being ignored on fields of that nested DTO - in previous versions it was correct. Now, everything shows as optional.
OpenAPI is not mapping the HelloDTO correctly so that it always comes in as null when executed from the documentation. When we access the API via Postman, with structure (we manually build) of:
{"hello":{DTO JSON HERE}}
it works correctly. However, when we submit via openAPI documentation, it submits the DTO as a top level object with json:
{DTO JSON HERE}
which doesn’t map correctly and is always coming back as null. This issue permeates through to the postman generated calls as well as the auto generated json from the postman plugin creates jsut the top level object and doesnt work.
we have disabled the auto body DTO as it was causing our users confusion.
Not sure if this is related, but the parameter content type for that property in OpenAPI is showing as application/x-www-form-urlencoded. Actually not sure what the point of that drop down is at all since there is only one option.
Can you provide stand-alone repro for the issue? I created
public class HelloDto
{
public string Name { get; set; }
}
[Route("/annotated-hello", "POST", Summary = "Creates a new hello.")]
public class CreateHelloReq : IReturn<Hello>
{
[ApiMember(IsRequired = true, ParameterType = "body")]
public HelloDto Hello { get; set; }
}
public class AnnotatedService : Service
{
public object Any(CreateHelloReq request) => new Hello { Name = "John"};
}
And resulting json is looking as expected: HelloDto is a first parameter of the request, required field is here too and
content-type is application/json
The IsRequired attribute on members of the nested property is being ignored by the generated site and it wasn’t previously. In your example, that would be the Name field which isn’t decorated in your example at the moment.
The generated UI isn’t serializing the json it is sending in correctly. Were you able to enable OpenAPI plugin and test the API post call you have there (not using the auto generated body DTO)?
The json of the types also looks as expected for me (e.g. required fields say required) - the issue is with the OpenAPI UI.
Can you show what do you expect to see in UI? And show API definitions which do you have in previous version (4.5.8) working correctly and which are showing differrently with current 4.5.11?
For example, if you change annotation ParameterType="body" to ParameterType="query" on Hello type you’ll get such picture in UI. Is this what do you expect or something another?
I’ll try and be more clear. Issue #1
In your example, change the DTO to:
public class HelloDto
{
[ApiMember(IsRequired = true)]
public string Name { get; set; }
[ApiMember(IsRequired = false)]
public List<string> Colors {get;set;}
}
In the UI when you click on Model on the right side of the Hello property, both fields, Name and Colors will show as optional even though Name is required. This was working correctly on previous versions.
Issue #2
If you click on the Example Value to the right of the Hello property, it will put json into the text box to the left. Click “Try it out!” with this json and you will see that the Hello property hitting the server is empty because the UI is not correctly building the json as I showed in post 1.
IMPORTANT: I am not referring to the “body” field at the bottom of the page. For us, that isn’t even showing because we have set DisableAutoDtoInBodyParam = true,
in the OpenApiFeature registration.
As you requested - a complete sample which reproduces both issues described above:
public class HelloDto
{
[ApiMember(IsRequired = true)]
public string Name { get; set; }
[ApiMember(IsRequired = false)]
public List<string> Colors { get; set; }
}
[Route("/annotated-hello", "POST", Summary = "Creates a new hello.")]
public class CreateHelloReq : IReturn<string>
{
[ApiMember(IsRequired = true, ParameterType = "body")]
public HelloDto Hello { get; set; }
}
public class AnnotatedService : Service
{
public object Any(CreateHelloReq request) => request.Hello.Name;
}
public override void Configure(Container container)
{
this.Plugins.Add(new OpenApiFeature());
}
I’m now adding generation of Required field not only for params like it implemented in current version, but also for defined
datatypes.
I still don’t get what was working in previous version for you and does not work in v4.5.11. Can you point me what was working correctly in previous version? Here is the screenshot of Swagger UI from your example (v4.5.8). Required field is set only for Hello but not for its internal content.
Please note that model on the right side is showing that incapsulated parameters such as Name and Colors are optional. This should be fixed when I’ll finish the change with required setting of ApiMemberAttribute for datatypes.
HI - it seems you understand the issue with the required parameters on the nested object not displaying correctly. Glad you are working on a fix. I am pretty sure it was working in previous version, perhaps I was mistaken. Regardless, glad you are fixing it.
As far as the suggestion of working around the serialization issue by changing the param type to model, this solution will not work as I mentioned above, we have set DisableAutoDtoInBodyParam = true. In such a case, setting the param type to model doesn’t allow the test to be generated at all (the window doesn’t display). Can you also fix this issue so the request is correctly generated when the param type is body? As I mentioned, this issue also causes the json auto generated for Postman not to work.
Today will be released new version which allows to enable/disable generation of body param for particular request type. In current version there is only one option to generate body parameter is not to set DisableAutoDtoInBodyParam to true. When you add ParameterType = "body" for inner property you change the parameter request type to type which is not and that is why you can not run Try it out in Swagger UI. If you enable auto generating body param and change ParameterType from body to model you can use Try it out in Swagger UI with current version also.
The generation of required field in nested types is already on MyGet, but looks like Swagger UI v2.2.10 itself has an issue with correct showing required/optional for models in UI.
That is very disappointing given that main purpose of providing the API documentation to the clients is so they can see the structure of what they need to submit and which fields are required vs optional. What can be done?
I think the best choice is post the issue about “required” field into Swagger UI project. This issue is partially fixed in Swagger UI 3.0 and you can try v3.0, but as last as I saw v3.0 it had other major issues (for example, header parameters did not work).
Here are some related issues about “required” field in model:
I’ve added BodyParameter member for [ApiAttribute] to control generation of body parameters per DTO. You can annotate request DTO with [Api(BodyParameter = GenerateBodyParameter.Always)] to create body parameter for particular request type when DisableAutoDtoInBodyParam = true.
[Api(Description = "Creates new hello", BodyParameter = GenerateBodyParameter.Always, IsRequired = true)]
[Route("/annotated-hello", "POST", Summary = "Creates a new hello.")]
public class CreateHelloReq : IReturn<Hello>
{
[ApiMember(IsRequired = true, ParameterType = "model")]
public HelloDto Hello { get; set; }
}
You can also use [Api(BodyParameter = GenerateBodyParameter.Never)] to not generate body param (useful if you do not want to generate empty parameter).
Thanks for the update. The issue with required parameters not working is actually larger then I thought previously.
Consider the following:
[Route("/annotated-hello", "POST", Summary = "Creates a new hello.")]
public class HelloDto
{
[ApiMember(IsRequired = true)]
public string Name { get; set; }
[ApiMember(IsRequired = false)]
public List<string> Colors { get; set; }
}
In the model view next to the body attribute, both properties are shown as optional! This means that swagger is never honoring the required member attribute in the body model! I am shocked that a tool used to help document APIs would have such a major bug.
To my original comments in this thread, I distinctly remember it working with a previous version of Service.Stack.OpenApi which led me to believe it has to do with how you guys were formatting the metadata for swagger, but I just reverted back several versions and it isn’t working with any of them.
With your current OpenApi implementation, is there any way at all to show parameters as required and still allow the “Try it out!” feature to function with a json DTO containing all params? The required params show correctly in the field by field view, which causes the “Try it Out” to fail because the individual text boxes weren’t filled out for the required parameters. For testing purposes, we are not asking anyone to fill out 100 separate boxes - they should past the json into the body text box, but this doesn’t allow the test to proceed. If we hide the text boxes then they don’t know which fields are required.
We don’t have a “separate OpenApi implementation”, all our implementation does is implement the OpenApi 2.0 spec which is returned as JSON and processed by the Swagger UI - which is embeded unmodified. The Try it out feature is apart of the Swagger UI . Setting IsRequired=true just sets the required flag in the returned JSON, that’s all the control we have. Any issues with Swagger UI should be submitted to the Swagger UI project who ultimately decides what behavior Swagger UI should have.
but in the json the plugin is outputting, that tag is missing. required is marked per property but not at the top level as per the spec. (similar to this discussion).
Apologies if I’m looking at the wrong thing, I’m just struggling to accept that Swagger UI would have such a significant functional flaw for so long where required fields aren’t marked as such in the model view.