Manually specify POST request body in spring boot swagger - java

I wrote the piece of code below:
#PostMapping(path = "/process", produces = MediaType.APPLICATION_JSON_VALUE)
#ApiOperation(value = "Get process with given ID", produces = MediaType.APPLICATION_JSON_VALUE,
response = ProcessType.class)
public ResponseEntity<ProcessType> createProcessType(
#RequestBody
#DTO(ProcessTypeDto.class) ProcessType processType
) {
log.info("POST called on /process");
ProcessType processTypeResult;
...
...
}
which works great. But my problem is with swagger. I made a custom annotation #DTO which automatically maps one class to another. But, swagger sees my ProcessType request body and shows examples in the UI of that class rather than ProcessTypeDto. If I delete what swagger shows and POST ProcessTypeDto the code works I would just like swagger to show ProcessTypeDto as the default example for this endpoint as it would break codegen.
Is there a way to manually specify what request body I would like from swaggers POV overriding what my#Requestbody is?

looks like you are not lucky so far, it will be released in version 2.0
here is what you are lookin for https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Annotations#requestbody
or at least you can start using a release candidate
https://mvnrepository.com/artifact/io.swagger/swagger-core

Related

Using Swagger Documentation for HttpServletRequest

I am new to swagger and using it's documentation. I am currently trying to use swagger to display the request body of a PATCH request. Previously, the parameter of the PATCH method was the DTO of the object that was being updated, which made it very easy to display the attributes of the object (as I am using SpringBoot, and using #Schema worked perfectly). However, now the parameter of the PATCH method is an HttpServletRequest. Instead of displaying the HttpServletRequest in the swagger doc (which seems to automatically be happening), I want to show the DTOs attributes (just as had been done before). I was wondering if there was a way to do that?
Any advice is much appreciated!
I am assuming you are using springdoc-openapi for generating SwaggerUI.
To use this you can use the below Maven dependencies,
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webmvc-core</artifactId>
<version>1.4.2</version>
</dependency>
From v1.1.25 of springdoc-openapi, HttpServletRequest and HttpServletResponse will be added to the list of ignored types.
See below,
https://github.com/springdoc/springdoc-openapi/issues/57
So even if we add HttpServletRequest as a parameter inside the controller method, it will be ignored and will not be displayed in the swagger.
So coming back to your question, to display the model of a class, you can describe another parameter along with HttpServletRequest as below,
#Operation(summary = "Returns a token", description = "Returns A token API", tags = "tokenGeneration", responses = {
#ApiResponse(description = "Successful Operation", responseCode = "200", content = #Content(mediaType = "application/json", schema = #Schema(implementation = Timeresponse.class))),
#ApiResponse(description = "not found Operation", responseCode = "404") })
#PatchMapping("/getTokenPatchRequest")
public ResponseEntity getTokenpatch(HttpServletRequest request, #RequestBody AuthReq2 req) {
log.info("The HttpServlet request header contains the information : " + request.getHeader("Authorization"));
The model class of Auth2 is as below which can describe your example value of username and passwords etc.
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
#Data
public class AuthReq2 {
#Schema(example = "diannamcallister")
private String userName;
#Schema(example = "test")
private String password;
}
Ands finally the swagger page looks like this,
When you enter something in the authorization header, as below,
This can be accessed via the HTTP servlet request via the below code,
log.info("The HttpServlet request header contains the information : " + request.getHeader("Authorization"));
The log entry in the springboot application will look like below,
10:40:01.876 INFO OpenApiController.getTokenpatch:163 - The HttpServlet request header contains the information : stackoverflow
The above answer did not work since adding another parameter to the method broke the functionality of the method itself.
The solution that worked was in the controller to add the content parameter to the #RequestBody annotation:
#RequestBody(description = "Description.",
content = #Content(schema = #Schema(implementation = ObjectDTO.class)));
How to displaying the HttpServletRequest in the swagger doc?
You can set in the configuration of swagger2 ,SwaggerConfig.java
new Docket(DocumentationType.SWAGGER_2)
...
.ignoredParameterTypes(HttpSession.class, HttpServletRequest.class, HttpServletResponse.class)
.build();

Lazy loading object causing issue during serialization. Getting 500 internal server error

I have written API's which are resulting in 500 error when hit through postman or browser. However, when I debug and see server is not throwing any error and in fact returning a proper response. Other controller I have implemented in a similar way is returning expected result. below is my controller code. Has anyone faced similar situation. Kindly help.
#CrossOrigin
#GetMapping(value="/byPatientId/{patientId}", produces = "application/json")
public List<ContactInfo> getAllContacts(#PathVariable String patientId) {
logger.info("Received request for List of ContactInfo for patientId: "+patientId);
List<ContactInfo> list =
contactInfoService.getAllContacts(patientId);
return list;
}
#CrossOrigin
#GetMapping("/byContactId/{contactId}")
public ContactInfo getContactById(#PathVariable Integer contactId) {
logger.info("Received request for ContactInfo for contactId: "+contactId);
return contactInfoService.getContactById(contactId);
}
The problem was with one of the dependent object which was having oneToMany relationship with the return type object and it was set to Lazy loading and issue was during the serialization.
Either we can change it to Eager loading or ignore the dependent object by adding #JsonIgnore on dependent object.
I handled it by adding #JsonIgnore annotation on top of the dependent object as I don't need the dependent object in this particular usecase. Issue is solved now.
How is your Controller annotated? is it with #Controller or #Rest?
#RestController = #Controller + #ResponseBody(for serializing the response and pass it into the HttpResponse.
Add the #ResponseBody in your methods on the controller or change the #Controller tag into a #RestController(take into account that #RestController is available since 4.0 Spring version).
More info:https://www.baeldung.com/spring-controller-vs-restcontroller

Spring boot PutMapping with Enum as RequestBody issue

I have a spring boot controller endpoint as follows.
#PutMapping("/manage/{id}")
public ResponseEntity<Boolean> manage(#PathVariable Long id, #RequestBody Type type) {
...
}
Where Type is an Enum as follows.
public enum Type {
ONE,
TWO
}
ISSUE 1: When I test this controller, I have to send the content as "ONE" instead of ONE for a successful invocation. i.e. it works with the following code.
mvc.perform(put("/api/manage/1")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content("\"" + Type.ONE + '\"'))
.andExpect(status().isOk());
It does not work with
mvc.perform(put("/api/manage/1")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(Type.ONE.name()))
.andExpect(status().isOk());
ISSUE 2: I am not able to invoke this method from the Angular service.
this.http.put<string>('/api/manage/' + id, type)
gives me
org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/plain;charset=UTF-8' not supported
Everything works when I add the Enum to a Dto and send an object from the client. But due to some business requirements, I want to use the current structure itself. i.e the Enum as a RequestBody.
UPDATE
I also tried to change the controller method structure to
#PutMapping(value = "/manage/{id}", consumes = MediaType.TEXT_PLAIN_VALUE)
I get the following error.
Content type 'text/plain' not supported
Both issues stem from trying to use a JSON endpoint as a plain text endpoint.
Ad 1, ONE is invalid JSON ("ONE" is valid)
Ad 2, when you just post a string, it is sent as text/plain and the endpoint complains.
Probably adding consumes="text/plain" to your #PutMapping will solve the problem, but frankly - I am not sure if string/enum mappings work out-of-the-box in the hodge-podge that is spring boot.

Java: Is it possible to use swagger-core annotations w/o any adapters?

I'm wondering is it possible to build Swagger documentation based only on swagger-core annotations w/o help of any adapters like jax-rs or springfox?
Why asking the question: e.g. when using springfox it relies on Spring annotations like #RequestMapping, #ResponseStatus etc. Imagine I want to specify a POST method which returns 201 by default:
#ApiOperation(value = "some api")
#ApiResponses(value = {
#ApiResponse(code = 201, message = "Created")
})
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
Without the last line springfox will generate the 200 status by default which I don't want to be a part of the spec. And ofc I don't want to add an imperative #ResponseStatus annotation to the controller.
I.e. I want to be in full control of the generated API spec which should rely on swagger core annotations only.

Why does MockMvc always return empty content()?

I'm trying to test my rest api with mockMvc.
mockMvc.perform(get("/users/1/mobile")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print())
.andExpect(content().string("iPhone"))
The test failed because of:
java.lang.AssertionError: Response content
Expected :iPhone
Actual :
From the output of print(), I can know the API actually returned the expected string "iPhone".
ModelAndView:
View name = users/1/mobile
View = null
Attribute = treeNode
value = "iPhone"
errors = []
And I guess the empty "Actual" above is caused by empty "Body" below
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = users/1/mobile
Redirected URL = null
Cookies = []
My questions are:
Why MockHttpServletResponse's Body is empty;
How can I correctly test the response of API.
If your action methods (methods with #RequestMapping annotation) return instances of ModelAndView or you work with Model, you have to test it using MockMvcResultMatchers#model function:
.andExpect(MockMvcResultMatchers.model().attribute("phone", "iPhone"))
.andExpect(MockMvcResultMatchers.model().size(1))
MockMvcResultMatchers#content is appropriate for REST action methods (methods with #RequestBody annotation).
To have a better understanding about testing Spring MVC and Spring REST controllers check these links:
Testing of Spring MVC Applications: Forms
Testing of Spring MVC Applications: REST API
Just adding another reason for this error, that took me a whole day to discover. I successfully created an APITest using mockito and mockmvc class, using the perform method. Then copied the code to produce another service and I started to get an empty body over and over again.
Nonetheless, at the end of the day I decided to compare each copied class from one project to another. The only one difference that I found was the #EqualsAndHashCode annotation in my request DTO that is received by the new controller.
So, the recommendation is: add the #EqualsAndHashCode annotation in your DTO classes.

Categories

Resources