I am trying to provide custom JSON example to the body of my request.
I am writing
#ApiImplicitParams({
#ApiImplicitParam(
paramType = "body",
//dataType = "com. ... .MyClass",
dataType = "java.lang.String",
name="payload",
examples = #Example(value = {
#ExampleProperty(value = "[\"haha\"]", mediaType = MediaType.APPLICATION_JSON_VALUE)
})
)
})
public OperationResponse createEntity(#RequestBody MyClass payload ...
If #ApiImplicitParams annotation is absent, Swagger draws deserialization of MyClass which is incorrect. I am trying to provide predefined example (haha string). When I am adding #ApiImplicitParams only
string
appears in the body, hot haha.
Why and how to fix?
Related
I have application in Spring and creating documentation for this in OpenAPI with annotations for controllers methods. For example I have method getById (simplified for readability):
#GetMapping("/{id}")
#ApiResponse(
responseCode = "200",
description = "Successful operation.",
content = #Content(
mediaType = "application/json",
schema = #Schema(implementation = ScheduleResponse.class)
)
)
#ApiResponse(
responseCode = "404",
description = "The object with the specified ID does not exist in the system.",
content = #Content(
mediaType = "application/json",
schema = #Schema(implementation = ApiError.class)
)
)
ScheduleResponse getById(#PathVariable Long id) throws EntityNotFoundException;
For 404 NOT_FOUND I returns my own ApiError with list of ApiErrorDetails interface:
#Getter
public class ApiError {
private final LocalDateTime timestamp;
private final String status;
private final String message;
private List < ApiErrorDetails > details;
}
public interface ApiErrorDetails {
}
In that case, I'm using a specific implementation of the interface:
#Getter
public class EntityNotFoundDetails implements ApiErrorDetails {
private final String field;
private final Object notFoundValue;
}
With the above implementation, I get JSON in the documentation with no specific field information inside details for example:
and for schema:
Instead, I'd like to prepare an example like this:
{
"timestamp": "2021-08-08T13:32:10.875Z",
"status": "string",
"message": "string",
"details": [
{
"field": "string",
"notFoundValue": {}
}
]
}
Of course, I need solution for that specific case. This means that I don't want to add the
#Schema(example = "value")
to the details list because I provide different implementations in different cases.
I found a solution that is not perfect but sufficient for documentation purposes.
All that is needed is to add #Schema annotation with the property oneOf over ApiErrorDetails. For example for two interface implementations: EntityNotFoundDetails and ValidationErrorDetails:
#Schema(oneOf = {EntityNotFoundDetails.class, ValidationErrorDetails.class})
interface ApiErrorDetails {
}
In the documentation, it looks like this:
which suggests a slightly different shape of JSON than in reality, but the schema tab dispels doubts:
Probably the only way to provide one implementation of your choice is to simply use different classes and not extend the interface.
I'm trying to use #Parameter for a path parameter in a #RestController, but it ignores the parameter.
Note: All works well if I use #PathVariable instead (commented). However, #PathVariable does not produce the OpenAPI yaml file with all properties like "description", "references", etc, that the springdoc-openapi-maven-plugin produces automatically.
Is it possible to use #Parameter in a REST controller?
Here's my code:
#RequestMapping("/channels")
#RestController
public class ChannelRESTController {
#PostMapping("{channelId}/connect")
#Operation(summary = "Initiates a session to a channel", tags = { "session" })
#ResponseBody
ResponseEntity<?> connect( //
#Parameter(name = "channelId", in = ParameterIn.PATH,
required = true, description = "The channel id") String channelId,
// #PathVariable(required = true) String channelId,
#Parameter(description = "Credentials' username") String username,
#Parameter(description = "Credentials' password") String password
) {
System.out.println(
"Starting channel #" + channelId); // displays null :(
return ...
}
In order to automate the OpenAPI YAML file generation it's possible to add both annotations to the parameters.
In short, the parameter channelId above can be annotated as:
#PathVariable(required = true)
#Parameter(name = "channelId", in = ParameterIn.PATH,
required = true, description = "The channel id")
String channelId,
In this case:
the first annotation #PathVariable allows Spring to retrieve the parameter from the URL.
the second annnotation #Parameter produces the correct description in OpenAPI (YAML) file generated by the springdoc-openapi-maven-plugin plugin.
This solution is not ideal, but does the trick. I wish in the future the plugin will recognize the second one by itself, to avoid typing them both.
I am new to java. Trying to develop a application to schedule http api calls in a cron job. Only the method name will be the input. All the apis are configured with swagger annotations. Can I use these annotations to determine whether the api is post or get or delete etc. For example
public class ABC {
#ApiOperation(
httpMethod = "GET",
value = "value",
notes = "notes",
response = ABC.class)
ABC getValue()
{
}
}
only getValue is the input to my application. Can I get the #ApiOperation values to determine the http method type.
You can, but it is in the RequestMapping annotation (the one where you specify which URL should be linked to the method):
For example, this method will be called when someone navigates to myBaseURl/persons in GET. It will return JSON.
#ApiOperation( value = "List of persons",
notes = "List all my base's persons. ",
response = Person.class,
responseContainer = "List",
tags = { "Persons", })
#RequestMapping(value = "/persons",
produces = { "application/json" },
method = RequestMethod.GET)
public PagedResources<PersonResource> persons(...) {}
The issue is that I have a complex Object as Request Param for a GET-Request and after I place the Swagger Annotations inside the Object. The Swagger UI shows that the entry param is a body in which i have to place the Params.
body: {
"modelId": 0,
"makeId": 0
}
My REST controller looks like this
#RequestMapping(method = RequestMethod.GET, value = "/model")
public SearchModelsResponse searchModels(
#ApiParam(value = "Search for something",
required = true) final ModelSearch search) {...}
And the Request object
public class ModelSearch {
#ApiParam(value = "Something important)", required = true)
private Long modelId;
#ApiParam(value = "Something else important)", required = false)
#Nullable
private Long makeId;
....
}
Is there a Way to Annotate it correctly so Swagger shows it as correctly as Request Parameters and not as a body construct ?
OK the solution in this kind of scenario is to manually define the Parameters, this is possible with the #ApiImplicitParam annotation.
So as result this looks like this.
#ApiImplicitParams({
#ApiImplicitParam(name = "modelId", value = "this is modelId", required = true, dataType = "string", paramType = "query"),
#ApiImplicitParam(name = "makeId", value = "this is makeId", required = true, dataType = "string", paramType = "query")
})
#RequestMapping(method = RequestMethod.GET, value = "/model")
public SearchModelsResponse searchModels(
final ModelSearch search) {...}
It's not a beautifull solution since I actually want swagger to interprete my code but the result gives the option to show it as request parameters not as a body construct.
I'm trying to setup an endpoint that can accept either a single object or a list of objects of the same type.
I've tried having two methods declared with the two data types but Spring doesn't like that (fails to start server)
#RequestMapping(
value = "",
method = RequestMethod.POST ,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<Object> single(#RequestBody Something something){
return ResponseEntity.ok("ok");
}
#RequestMapping(
value = "",
method = RequestMethod.POST ,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<Object> multiple(#RequestBody List<Something> somethingList){
return ResponseEntity.ok("ok");
}
The closest i've gotten is accepting a Something[] and having one method declared.
#RequestMapping(
value = "",
method = RequestMethod.POST ,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<Object> somethingArray(#RequestBody Something... something){
return ResponseEntity.ok("ok");
}
Question: Is there a way to do this without having to accept just an Object and do the deserialization manually?
EDIT: I also tried multiple #RequestBody's, ie
#RequestMapping(
value = "",
method = RequestMethod.POST ,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<Object> somethingArray(#RequestBody(required = false) Something something, #RequestBody(required = false) Something[] somethingArray){
return ResponseEntity.ok("ok");
}
You can create a one method, that receive an String for example. Then you need to analyze a String, marshal it to an Object, and call a private method, dependent on result type.
But i mean its wrong approach. Better define different endpoints for different parameters.
I usually make a plural endpoint when doing this.
If I am adding a Customer the endpoint would be /customer, if it is a List I do /customers.
To my knowledge you cannot overload the request method by parameter type.