#ApiResponse with empty response body (Spring Boot) - java

I'm looking for a way to tell swagger that a certain API response code doesn't have a response body. A get response, for example, that can either return a 200 code with the actual object as a response or a 404 if the object associated with the passed ID doesn't exist:
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "Object found"),
#ApiResponse(responseCode = "404", description = "Invalid object ID", content = #Content)
})
This is the closest thing I could figure out but it's not perfect, I still get an annoying "Media type" under the description of the 404 response.
Thanks!

If you are not specifying the content attribute of #ApiResponse annotation the return type of the controller method will be your response content. To prevent this define content explicitly:
#ApiResponse(responseCode = "200", description = "OK",
content = #Content(schema = #Schema(implementation = Void.class)))
Or you can simply return ResponseEntity<Void>.

This is probably the better (and shorter) way:
#ApiResponse(
responseCode = "404",
description = "Not found",
content = #Content(schema = #Schema(hidden = true)))

You can use the following on top of your method in v2
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success", response = YourObject.class),
#ApiResponse(code = 401, message = "Unauthorized"),
#ApiResponse(code = 403, message="Forbidden"),
#ApiResponse(code = 404, message = "Not Found"),
#ApiResponse(code = 500, message = "Failure")
})
For V3, you could try something like this in case your method is returning some object
#Operation(summary = "Add a new object", description = "", tags = { "yourObject" })
#ApiResponses(value = {
#ApiResponse(responseCode = "201", description = "Object created",content = #Content(schema = #Schema(implementation = YourObject.class))),
#ApiResponse(responseCode = "400", description = "Invalid input"),
#ApiResponse(responseCode = "409", description = "Object already exists") })
#PostMapping(value = "/your-url", consumes = {"application/json","application/xml" })
public ResponseEntity<YourObject> addObject(
...
return ...
}
In case your method is returning void try this one
#Operation(summary = "Update an existing object", description = "", tags = { "yourObject" })
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "successful operation"),
#ApiResponse(responseCode = "400", description = "Invalid ID supplied"),
#ApiResponse(responseCode = "404", description = "Object not found"),
#ApiResponse(responseCode = "405", description = "Validation exception") })
#PutMapping(value = "/your-url/{id}", consumes = { "application/json", "application/xml" })
public ResponseEntity<Void> addObject(
...
return ...
}

Not sure if it's a feature, but an empty #Content worked for me:
interface MyControllerOpenApiSpec {
#ApiResponse(responseCode = "200") // shows MyDTO schema
#ApiResponse(responseCode = "404", content = #Content) // no schema shown
MyDTO getMyDTO();
}

There is not any content method; maybe, it is changed.
public #interface ApiResponse {
int code();
String message();
Class<?> response() default Void.class;
String reference() default "";
ResponseHeader[] responseHeaders() default {#ResponseHeader(
name = "",
response = Void.class
)};
String responseContainer() default "";
Example examples() default #Example({#ExampleProperty(
value = "",
mediaType = ""
)});
}

Related

I have two APIs Interfaces and i want to implement both in a single controller, but controller gets hit for the 1st api interface methods only

#Path("/avManageThemesService")
#Api(description = "the avManageThemesService API")
#javax.annotation.Generated(value =
"org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2022-09-
02T19:11:34.805+05:30[Asia/Calcutta]")public interface AvManageThemesServiceApi {
#PUT
#Consumes({ "application/json" })
#Produces({ "application/json" })
#ApiOperation(value = "Update Theme", notes = "", tags={ "Update Theme" })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "OK", response = SaveThemeResponse.class),
#ApiResponse(code = 400, message = "Bad Request", response = ErrorDetails.class),
#ApiResponse(code = 401, message = "Unauthorized", response = ErrorDetails.class) })
Response updateTheme(#Valid SaveThemeJSONRequest saveThemeJSONRequest);
}
#Path("/avListThemes")
#Api(description = "the avListThemes API")
#javax.annotation.Generated(value =
"org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2022-09-
02T19:11:34.805+05:30[Asia/Calcutta]")public interface AvListThemesApi {
#GET
#Produces({ "application/json" })
#ApiOperation(value = "Get all themes", notes = "", tags={ "getAllThemes" })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "OK", response = GetThemesResponse.class),
#ApiResponse(code = 400, message = "Bad Request", response = ErrorDetails.class),
#ApiResponse(code = 401, message = "Unauthorized", response = ErrorDetails.class) })
Response getAllThemes();
}
public class Example implements AvManageThemesServiceApi, AvListThemesApi{
#Override
public Response updateTheme(SaveThemeJSONRequest aSaveThemeJSONRequest){
return Response.ok(200).build();
}
#Override
public Response getAllThemes(){
return Response.ok(200).build();
}
}
when i hit url from postman, it only gives response for the 1st implemented interface(here:AvManageThemesServiceApi) and gives response RESTEASY003210: Could not find resource for full path: for the other one(here:AvListThemesApi). Reverse happens when interfaces positions are exhanged.
Though when implemented with separate controllers, both works fine.

How to show multiple example in swagger documentation?

In my REST API PATCH operation, I am using v3 swagger-annotation:2.0.6
I was trying to add more examples as swagger schema for my patch operation PATCH /users/id.
Request Body:
{
"operationList": [
{
"op": "replace",
"path": "/username",
"value": "john1991"
}
]
}
Currently I have following class for request model.
public class UserPatchOp extends PatchOperation {
#Override
#Schema(description = "some description", example = "replace", required = true)
public PatchOperator getOp() {
return super.getOp();
}
#Override
#Schema(description = "some description", example = "/username", required = true)
public String getPath() {
return super.getPath();
}
#Override
#Schema(description = "some description", , example = "john1991", required = true)
public Object getValue() {
return super.getValue();
}
}
In PatchOperation.java
public class PatchOperation {
/**
* {#link PatchOperator} operation to be performed
*/
#Schema(description = "Operation to be performed", required = true)
#JsonProperty
#NotNull
private PatchOperator op;
#Schema(description = "Path to target where operation will be performed", required = true)
#JsonProperty
#Pattern(regexp = "/([/A-Za-z0-9~])*-*", message = "Invalid path. Please follow regex pattern")
private String path;
#Schema(description = "Value used by operation")
#JsonProperty
private Object value;
public PatchOperation() {
}
}
In the swagger document, in UserPatchOp.java I have shown to the end-user that how to replace username.
On swagger, the request bogy comes with this example.
other than username through this patch operation, end-user can update description then path would be /description.
End-user can also update the usergroup from which it belongs to '/usergroups'
So in general, same way I want to add more example to swagger schema.
But I am not able to do it. Because at one time I can show one example only.
I want to show following multiple operations to the client on swagger page.
{
"operationList": [
{
"op": "replace",
"path": "/username",
"value": "john1991"
},
{
"op": "remove",
"path": "/description"
},
{
"op": "add",
"path": "/memberships"
"value":["1224","8907"]
}
]
}
And entry point method is
#PATCH
#Path("users/{id}")
#Consumes({MediaType.APPLICATION_JSON, APPLICATION_JSON_PATCH_JSON})
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = MessageConstants.OK, content = #Content(schema = #Schema(implementation = UserInfo.class))),
#ApiResponse(responseCode = "500", description = MessageConstants.SERVER_ERROR, content = #Content(schema = #Schema(implementation = RestError.class))),
#ApiResponse(responseCode = "400", description = MessageConstants.BAD_REQUEST, content = #Content(schema = #Schema(implementation = RestError.class))),
#ApiResponse(responseCode = "401", description = MessageConstants.UNAUTHORIZED, content = #Content(schema = #Schema(implementation = RestError.class))),
#ApiResponse(responseCode = "404", description = MessageConstants.NOT_FOUND, content = #Content(schema = #Schema(implementation = RestError.class)))})
public Response updatePartialUser(
#Parameter(description = "User Id", required = true) #PathParam("id") String id,
#Parameter(description = "User Update Info", required = true) #Valid PatchOperations<UserPatchOperation> operationList) {
Is there any way, I can add multiple examples for getOP, getPath and getValue method? Thank you.
It is possible to create multiple examples of responses which one method can return but it is possible to do only one example for one response code.
#Operation(description = "Retrieves status of application",
responses = {
#ApiResponse(responseCode = "200",
description = "Everything works fine.",
content = #Content(mediaType = "application/json",
examples = #ExampleObject(value = "{\n" +
" \"success\" : true,\n" +
" \"message\" : \"OK\"\n" +
"}"))),
#ApiResponse(responseCode = "500",
description = "Application not working properly",
content = #Content(mediaType = "application/json",
examples = #ExampleObject(value = "{\n" +
" \"success\" : false,\n" +
" \"message\" : \"Application not working properly\"\n" +
"}")))
})
#GetMapping("haproxy")
ResponseEntity<BaseResponse> getHaProxy();
I'm not sure if it's what you want but I don't see other way around.
Keep in mind that swagger documentation should be done in a way that someone will be able connect to your api and do some operations. You don't need to provide too much responses there. That's for OPTIONS rest method is. OPTIONS method is basically a GET which don't need any parameters and in response will return complete informations about what certain method can do and what the request/response will be. Here you have better explanation of that: RESTful API methods; HEAD & OPTIONS
Btw. you should update your dependencies, swagger-annotations is on 2.1.4 now, 2.0.6 is from 2 years ago
EDIT 2020-09-30 About request's body:
It is possible to add multiple requests examples like that:
#Operation(description = "Creates a User",
requestBody = #io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Request examples",
content = #Content(examples = {
#ExampleObject(name = "doing weird stuff", value = "http://localhost:7080"),
#ExampleObject(name = "doing weirdest stuff", value = "{\n\"test\":\"12\"\n}"),
})),
responses = {
#ApiResponse(responseCode = "200",
description = "Everything works fine.",
content = #Content(mediaType = "application/json",
schema = #Schema(implementation = UserResponse.class))),
#ApiResponse(responseCode = "404",
description = "Not found",
content = #Content(mediaType = "application/json",
examples = #ExampleObject(value = "{\"success\":false,\"message\":\"That shop or API version is not accessible yet\",\"httpStatus\":\"NOT_FOUND\"}"))),
#ApiResponse(responseCode = "500",
description = "Something went wrong",
content = #Content(mediaType = "application/json",
schema = #Schema(implementation = SomeBasicResponse.class)))
})
#Parameters({
#Parameter(in = ParameterIn.HEADER, name = "url",
content = #Content(schema = #Schema(type = "string", defaultValue = "localhost:7080")))
})
#PostMapping
ResponseEntity<UserResponse> createUser(#RequestHeader(name = "login", required = false) String login,
#RequestHeader(name = "password") String password,
#RequestBody UserRequest request);
I hope that's what you are looking for.
I have added example at entry point with help of schema
#Parameter(description = "Update User", required = true, schema = #Schema (example = "{\n "
+ "\"operationList\": [\n "
+ "{\n \"op\": \"replace\",\n \"path\": \"/username\",\n \"value\": \"john1991\"\n },\n "
+ "{\n \"op\": \"replace\",\n \"path\": \"/description\",\n \"value\": \"NewDescription\"\n },\n "
+ "{\n \"op\": \"add\",\n \"path\": \"/memberships\",\n "
+ "\"value\":[\"1234\",\"6789\"]\n "
+ "{\n \"op\": \"remove\",\n \"path\": \"/privileges\",\n \"value\":[\"148\",\"155\"]\n "
+ "}\n ]\n}")) #Valid PatchOperations<UserPatchOperation> operationList) throws RestException

Swager CodGen generating code ApiController.java with multiple try catch

I am looking for generating code with multiple try catch in APIController.java which is generated by SwagerCodeGen.
swagger.yaml file
paths:
/system:
post:
tags:
- New
summary: System info
description: Initial System Info
operationId: createSystem
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: systemDetails
description: Contains the JSON Object
required: true
schema:
$ref: '#/definitions/SystemDetails'
- name: AuthKey
in: query
description: Key for Authentication and Authorization.
required: true
type: string
format: uuid
responses:
'201':
description: System Created Successfully
schema:
$ref: '#/definitions/Response'
'400':
description: Request Failed
'401':
description: Unauthorized
'500':
description: Internal Server Error
Swager generated code as below,
SystemApi.java
#ApiOperation(value = "System", notes = "Initial System Info", response = Response.class, tags={ "System", })
#ApiResponses(value = {
#ApiResponse(code = 201, message = "System Created Successfully", response = Response.class),
#ApiResponse(code = 400, message = "Request Failed", response = Void.class),
#ApiResponse(code = 401, message = "Unauthorized", response = Void.class),
#ApiResponse(code = 404, message = "Not Found", response = Void.class),
#ApiResponse(code = 500, message = "Internal Server Error", response = Void.class) })
#RequestMapping(value = "/system",
produces = { "application/json" },
consumes = { "application/json" },
method = RequestMethod.POST)
ResponseEntity<Response> createSystem(#ApiParam(value = "Contains the JSON Object" ,required=true ) #Valid #RequestBody SystemDetails systemDetails, #NotNull#ApiParam(value = "Key for Authentication and Authorization.", required = true) #RequestParam(value = "AuthKey", required = true) UUID authKey);
SystemApiController.Java
public ResponseEntity<Response> createSystem(
#ApiParam(value = "Contains the JSON Object",
required = true) #Valid #RequestBody SystemDetails systemDetails,
#NotNull #ApiParam(
value = "Key for Authentication and Authorization.",
required = true) #RequestParam(value = "AuthKey", required = true) UUID authKey) {
// do some magic!
return delegate.createSystem(systemDetails, authKey);
}
Is there any way to add try catch auto generated from Swagger CodeGen like below?
public ResponseEntity<Response> createSystem(
#ApiParam(value = "Contains the JSON Object",
required = true) #Valid #RequestBody SystemDetails systemDetails,
#NotNull #ApiParam(
value = "Key for Authentication and Authorization.",
required = true) #RequestParam(value = "AuthKey", required = true) UUID authKey) {
// do some magic!
try{
return delegate.createSystem(systemDetails, authKey);
}catch(InvalidInputException e){
return new ResponseEntity(new Response(HTTPSTATUS.BAD_REQUEST));
}
}
Or please suggest alternate way. Now I am handling all exceptions in SystemService.java, not throwing any exception to Controller. Because of this I need to rollback all Transactions manually in service side.
Swagger codegen uses moustache templates to generate your code.
You can refer to https://github.com/swagger-api/swagger-codegen#modifying-the-client-library-format to customize the syntax of generated classes

File Upload on Swagger Controller Interface

I'm creating an API micro-service that provides file storage with AWS. I'm putting together the Swagger and Controller, and we need to be able to allow users to upload a file on the Swagger. The problem is our controller is set up as an interface instead of a class, and solutions from Google/SO aren't making the cut to be able to work with interfaces. To clarify, I don't need to manipulate the file at all, just take it in. Our internal implementation methods will take then send it off to S3.
This is using Java 11, AWS S3, Spring Boot, and Swagger 2. I've tried using #ApiParam and #FormDataParam inside the method createFile, but I've gotten two different errors:
method must be abstract
annotations are not allowed here.
#RequestMapping(value = {"v3/registration/documents", "v4/registration/documents"})
#RestController
#Api(
value = "file-storage",
description = "File storage service",
tags = {"file-storage"})
public interface FileController {
#PostMapping(
value = "/{salesPlanAff}",
produces = {MediaType.APPLICATION_JSON_VALUE},
consumes = {MediaType.APPLICATION_JSON_VALUE})
#ApiOperation(value = "Upload a file")
#ApiResponses(
value = {
#ApiResponse(code = 200, message = "Success", response = FileResponseDTO.class),
#ApiResponse(code = 201, message = "Created"),
#ApiResponse(code = 400, message = "Bad Request"),
#ApiResponse(code = 401, message = "Unauthorized"),
#ApiResponse(code = 403, message = "Forbidden"),
#ApiResponse(code = 404, message = "Not Found"),
#ApiResponse(code = 500, message = "Internal Server Error")
})
void createFile(
#PathVariable(required = true, name = "webSessionId") String webSessionId,
#PathVariable(required = false, name = "salesPlanAff") String salesPlanAff);
What I expected is to have a button on my swagger page allowing file upload, didn't quite expect this much difficulty in adding file upload.
I have a FileUpload in my swagger page and it works like a charm. The only difference from your is that I am not doing it on an interface...
import org.springframework.web.multipart.MultipartFile;
...
#ApiOperation(value = "Analyse the identifiers in the file")
#RequestMapping(value = "/form", method = RequestMethod.POST, produces = "application/json")
#ResponseBody
public AnalysisResult getPostFile( #ApiParam(name = "file", value = "The file")
#RequestPart MultipartFile file,
HttpServletRequest r) {
UserData ud = controller.getUserData(file);
return controller.analyse(ud, r, file.getOriginalFilename());
}
I trimmed off a little bit this code, but you can find a the original one in our repository
Also, working version of this code can be executed tested here
Thanks
I figured out how to get the annotations in, part of it stemmed from typos. For anyone interested, here's the solution:
public interface FileController {
#PostMapping(
value = "/{salesPlanAff}",
produces = {MediaType.APPLICATION_JSON_VALUE},
consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
#ApiOperation(value = "Upload a file")
#ApiResponses(
value = {
#ApiResponse(code = 200, message = "Success", response = FileResponseDTO.class),
#ApiResponse(code = 201, message = "Created"),
#ApiResponse(code = 400, message = "Bad Request"),
#ApiResponse(code = 401, message = "Unauthorized"),
#ApiResponse(code = 403, message = "Forbidden"),
#ApiResponse(code = 404, message = "Not Found"),
#ApiResponse(code = 500, message = "Internal Server Error")
})
void createFile(
#PathVariable(required = true, name = "webSessionId") String webSessionId,
#PathVariable(required = false, name = "salesPlanAff") String salesPlanAff,
#ApiParam(required = true, value = "Document to be uploaded")
#RequestPart MultipartFile multipartFile,
#ApiParam(required = true, value = "File Type")
#QueryParam("documentType") String documentType);

Mass Assignment: Insecure Binder Configuration : How to use Spring Framework's #initBinder with Jersey framework

I want to avoid Mass Assignment: Insecure Binder Configuration issue for our application which is written in Jersey framework. I was thinking is there any other way we can use #InitBinder from spring and for each request to this service only allow to set the allowed properties and set all other properties to null.
#Controller
#Path("/ar")
#Api(tags = { "Request" })
public class RequestService extends AbstractService {
static final Logger logger = Logger
.getLogger("RequestServiceLogger");
#InitBinder
public void customizeBinding (WebDataBinder binder) {
System.out.println("Inside init binder ============== ");
//I want to allow the allowed field only for AccountRequest object
binder.setAllowedFields(allowedFields);
}
#Path("/submitrequest")
#POST
#Consumes({ "application/json" })
#Produces({ "application/json" })
#ApiOperation(value = "Validates a request", notes = "Validates a request", response = RequestResponse.class)
#ApiImplicitParams({ #io.swagger.annotations.ApiImplicitParam(name = "Auth", value = "value", required = true, dataType = "string", paramType = "header") })
#ApiResponses({
#io.swagger.annotations.ApiResponse(code = 200, message = "OK", responseHeaders = { #io.swagger.annotations.ResponseHeader(name = "X-ResponseTime", description = "Total Time Taken", response = String.class) }, response = RequestResponse.class),
#io.swagger.annotations.ApiResponse(code = 400, message = "Bad Request", response = com.model.ErrorDetail.class),
#io.swagger.annotations.ApiResponse(code = 401, message = "Unauthorized", response = com.model.ErrorDetail.class),
#io.swagger.annotations.ApiResponse(code = 403, message = "Forbidden", response = com.model.ErrorDetail.class),
#io.swagger.annotations.ApiResponse(code = 404, message = "Not Found", response = com.model.ErrorDetail.class),
#io.swagger.annotations.ApiResponse(code = 405, message = "Method Not Allowed", response = com.model.ErrorDetail.class),
#io.swagger.annotations.ApiResponse(code = 415, message = "Unsupported Media Type", response = com.model.ErrorDetail.class),
#io.swagger.annotations.ApiResponse(code = 500, message = "Internal Server error", response = com.ErrorDetail.class) })
public Response submitRequest(#ApiParam(value = "AccountRequest JSON input data.", required = true) AccountRequest accountRequest,
#Context HttpServletRequest request) throws Exception {
System.out.println("Inside submitRequest ============== ");
}
}
*** If there are any other alternative ways to filter request object properties please let me know.

Categories