I have a problem with a springdoc-openapi and swagger. In api.yml file I defined some responses for endpoint, for example:
responses:
200:
desription: example response
content:
......
404:
description: example response
And before that the behavior was expected to me. In swagger I saw an example value for 200 and I didn't see an example value for 404, only description. When I started to use springdoc-openapi, when I didn't provided content for example for 404, the content from 200 is applied to 404.
Is there a option how to hide it? For example in api.yml or any config property?
I tried to search config properties but I din't find anything. Moreover I tried with content: {} but It didn't work too.
It is be possible to handle as return an empty content as response using, one of the following syntaxes:
content = #Content
content = #Content(schema = #Schema(hidden = true))
For example:
#GetMapping
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "example response"),
#ApiResponse(responseCode = "404", description = "example response", content = #Content)
})
public String index() {
return "Hello";
}
For RestControllerAdvice
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler({ RuntimeException.class })
#ApiResponse(responseCode = "404", description = "example response", content = #Content)
#ResponseBody
public String handleError() {
return "NotFound";
}
Please see: https://springdoc.org/#how-can-i-return-an-empty-content-as-response
Related
#PostMapping(produces = "application/json", consumes = "application/json")
public ResponseEntity<Book> addBook(
#RequestBody(description = "Book to add.", required = true,
content = #Content(
schema=#Schema(implementation = Book.class)))
#Valid Book book
) {
return ResponseEntity.ok(bookRepository.add(Book));
}
2.
#PostMapping(produces = "application/json", consumes = "application/json")
public ResponseEntity<BasicBook> addBook(
#RequestBody(description = "Book to add.", required = true,
content = #Content(
schema=#Schema(allOf = BasicBook.class, oneOf = {
HomeScience.class, GeneralScience.class })))
#Valid BasicBook book
) {
return ResponseEntity.ok(bookRepository.add(Book));
}
Above snippet 1. is working fine. In the Swagger UI I can see the RequestBody with example value where I can execute that.
but for snippet 2. when I am trying inheritance (i.e. oneOf, allOf or anyOf), this schema is not working.
Only BasicBook properties are coming in (Swagger UI)RequestBody's example value.
I have tried with #Parameter also. Here is the code
3.
#PostMapping(produces = "application/json", consumes = "application/json")
public ResponseEntity<BasicBook> addBook(
#Parameter(required = true, description = "DTO for book.", content = {
#Content(mediaType = "application/json", schema = #Schema(allOf = BasicBook.class, oneOf = {
HomeScience.class, GeneralScience.class }, discriminatorMapping = {
#DiscriminatorMapping(value = "HOME", schema = HomeScience.class),
#DiscriminatorMapping(value = "GENERAL", schema = GeneralScience.class) }, discriminatorProperty = "type")) })
#RequestBody #Valid BasicBook book
) {
return ResponseEntity.ok(bookRepository.add(Book));
}
snippet 3. also not giving desired result.
How can I use oneOf, allOf or anyOf in the RequestBody.
I am getting Structural error "should NOT have additional properties" error due to $ref element present in type: array.
In https://editor.swagger.io when replace $ref: '#/definitions/EnumExample1' with type: array. I do not see the error. But I am not sure how to fix this in swagger-gen code.
If more information is required to understand this issue, please post in comments!
Swagger snippet
parameters:
- in: query
name: parameterNameX
description: parameterNameX
type: string
- in: query
name: name
type: string
- in: query
name: include
description: Comma-separated list of properties to include in the response
type: array
items:
$ref: '#/definitions/EnumExample1'
Errors
Structural error at paths./v1/workflows.get.parameters.2.items
should NOT have additional properties
additionalProperty: $ref
Jump to line 30
Startup.cs
services
.AddMvc(options =>
{
options.EnableEndpointRouting = false;
options.Conventions.Add(new CsvQueryStringConvention());
})
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
//ensure enums passed to client are strings
options.SerializerSettings.Converters.Add(new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy()});
})
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("",
new OpenApiInfo()
{
Title = "Service",
Version = "v1"
});
app.UseSwagger(c =>
{
c.RouteTemplate = "app/swagger/{documentName}/swagger.json";
c.PreSerializeFilters.Add((openApiDocument, httpReq) =>
{
openApiDocument.Servers = new List<OpenApiServer>
{
new OpenApiServer { Url = $"https://{httpReq.Host.Value}" }
#if DEBUG
, new OpenApiServer { Url = $"http://{httpReq.Host.Value}" }
#endif
};
});
c.SerializeAsV2 = true;
});
Model
public EnumExample1[] Example { get; set; }
From Swagger Example will be passed as comma-separated string. Since c.SerializeAsV2 = true; I am not sure why generated Swagger.json have $ref element which is OpenApi3 standard.
Adding UseInlineDefinitionsForEnums to swagger-gen removed reference to definitions and added type: string.
Found some open issues on swagger and openapi,
OpenAPI.NET : SerializeAsV2 produces invalid swagger (when SerializeAsV3 is valid) [Open]
Swashbuckle.AspNetCore : No type for enums in query parameters (using SerializeAsV2) (Closed with UseInlineDefinitionsForEnums solution)
services.AddSwaggerGen(c =>
{
c.SwaggerDoc(....);
//Generate inline schema definitions (as opposed to referencing a shared definition) for enum parameters and properties
c.UseInlineDefinitionsForEnums();
I generated swagger for micronaut using the instructions provided in https://micronaut-projects.github.io/micronaut-openapi/latest/guide/index.html
So I have a controller method like:
#Consumes("application/vnd.api+json")
#Produces("application/vnd.api+json")
#Post("/{id}/users")
#RequestBody
public HttpResponse addAndAssignTarget(#PathVariable("id") Long projectId, #Body #Parameter() JsonNode user) {
I am not using a POJO for adding users for another reason which is out of context for this question. Thus, the generated swagger ui shows {} as example for request body. I would like to change this to something like. How can I do this?
{
"data" : {
"type": "projects",
"attributes": {
"name": "some-name1",
"description": "some-description",
"partner_company": "some-compnay"
}
}
}
It looks much better if you use POJO. However, you can add an example as string, but the problem with that if you have any nested objects the quotation marks won't look nice (will be escaped "):
#Consumes("application/vnd.api+json")
#Produces("application/vnd.api+json")
#Post("/{id}/users")
#RequestBody
public HttpResponse addAndAssignTarget(#PathVariable("id") Long projectId,
#Body #RequestBody(content = #Content(schema = #Schema(example = "[0, 2, 3]"))) JsonNode user){
}
Also it is not a Paramater, but a #Body and #RequestBody for annotation purposes.
Output will be:
"[0, 2, 3]"
I want to return BadRequest status and error message from my MVC controller:
public class TestController : System.Web.Mvc.Controller
{
public ActionResult Bad()
{
Response.StatusCode = 400;
return new JsonResult()
{
Data = new { Message = "Request is bad!" },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
}
It works when I launch application in IISExpress, the method returns response body:
{"Message":"Request is bad!"}
But when I deploy same site to IIS (ver. 8.5) the response body changed to:
Bad Request
Why this happen? Is there some settings that allows to keep response body when status is not 200?
I found two solutions:
Set Response.TrySkipIisCustomErrors to true.
Set existingResponse to PassThrough in httpErrors
I have the following angularjs code:
$http.get("/Home/GetEmails").then(function (response) {
$scope.emails = response.data;
$scope.init($scope.emails);
$scope.totalEmails = $scope.emails.length;
});
When I develop locally, it works fine, but when I publish to a live server, it gives the following error message:
Failed to load resource: the server responded with a status of 404 (Not Found). It is looking for http://xaisoft.com/Home/GetEmails, but it can't find it. Is there something else I need to do in ASP.NET MVC and/or Angular to get this to work. Currently, I just have the GetEmails action return a JSON object in my HomeController.
HomeController
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
public ActionResult GetEmails()
{
return Json(new[]
{
new
{
from = "Jet Blue",
to = "Me",
subject = "Buy one seat, get one free!",
body = "That and helping Data finish 'Pop Goes the Weasel'. Also counts as a CMOH.",
date = "Dec 20th 12:22 PM",
isImportant = false,
isStarred = true,
isChecked = false,
isRead = true
},
new
{
from = "Publix",
to = "Me",
subject = "Check this weeks BOGO deals",
body = "Hurry, ends Thursday!",
date = "Mar 15th 8:15 AM",
isImportant = false,
isStarred = false,
isChecked = false,
isRead = false
},
new
{
from = "AJ Carpio",
to = "Me",
subject = "When Life Gives You Questions, Google has Answers",
body = "Get more life quotes here",
date = "Mar 15th 8:15 AM",
isImportant = true,
isStarred = false,
isChecked = false,
isRead = true
}
},JsonRequestBehavior.AllowGet);
}
}
You should never hardcode urls in an ASP.NET MVC application as you did:
$http.get("/Home/GetEmails")
You should always use url helpers. For example in your view you could set a variable:
<script type="text/javascript">
var getEmailsUrl = #Url.Action("GetEmails", "Home");
</script>
that you could later use in your ng script:
$http.get(getEmailsUrl)
The purpose of Url helpers such as Url.Action is to take into account things like the hosting environment virtual directory and your routing configuration. All those factors could change between the various environments and if you hardcode your urls in your javascript you will pretty much get lots of 404s.
Here's a more angular way of doing this instead of registering a global javascript variable. You could use the $provide:
<script type="text/javascript">
angular.module('myApp').config(['$provide', function ($provide) {
// Here you can also register a complex javascript object that will serve
// as configuration for your app and can contain multiple urls
$provide.value('getEmailsUrl', '#Url.Action("GetEmails", "Home")');
}]);
</script>
and then you can inject the getEmailsUrl in your controllers and services:
app.controller('HomeController', ['$scope', 'getEmailsUrl', function ($scope, getEmailsUrl) {
// You can use the getEmailsUrl variable here.
}]);