Swagger example post body - how to show JSON Body- Swagger-annotations - java

Requirement: I have a POST method which takes the input JSON as a String and passes it to another microservice. I don't want to create an Object (Bean) of this input JSON.
method:
#ApiOperation(notes = "example" value = "/example", consumes = ".." , method= "..")
#RequestMapping(name = "xxx" value ="/hello" ..)
#ApiResponses(..)
public #ResponseBody String getXXX (#Apiparam(name="JSONrequest", required = true) #RequestBody String JSONrequest){
}
Problem:
The generated Swagger doesn't show the input as a JSON model where all the JSON attributes are displayed.
Expectation:
I want to display my Swagger Something like this :
Definately I am missing the key thing. Any thoughts?

If changing from String to a concrete object is not okay (although that's what I would recommend you to do since it's cleaner), you can try using #ApiImplicitParams (check out their documentation)
#ApiOperation(notes = "example" value = "/example", consumes = ".." , method= "..")
#ApiImplicitParams({
#ApiImplicitParam(name = "Object", value = "Object to be created", required = true, dataType = "your.package.BodyClass", paramType = "body")
})
#RequestMapping(name = "xxx" value ="/hello" ..)
#ApiResponses(..)
public #ResponseBody String getXXX (#Apiparam(name="JSONrequest", required = true) #RequestBody String JSONrequest){
}
(not sure if you still need the #Apiparam(name="JSONrequest", required = true) bit from the method parameter)

It's an old question but since I haven't found a solution online here how I to customized the example value in the swagger documentation produce automatically by the java annotations.
I use swagger 2.0 and springfox.version 2.10.5.
The Idea is documenting the class of the request parameter that has the #RequestBody annotation. for example my method is
#ApiOperation(
value = "Start ListBuilder extraction",
response = ExtractionLogEntity.class,
produces = "application/json"
)
#PostMapping("/extraction/start")
public ExtractionLogEntity startTask(
#RequestBody(required = true) ExtractionRequest request,
In order to expose request json object example I added a #ApiModelProperty(example = "...") annotation to the properties of ExtractionRequest .
#ApiModelProperty(example = "[{ 'field':'value'}]")
#NotNull
private List<ListBuilderFieldEntity> fields;
#ApiModelProperty(example = "1000")
private String ied;
#ApiModelProperty(example = "US")
private String codebase;
And that's the result

I had the similar issue. My Service Class takes #RequestBody argument in String.
So, what I did :
Created a POJO and used #RequestBody annotation with it instead of inputString.
#RequestMapping(value = "/api/entity/{entityId}/user/query", method = {RequestMethod.POST}, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody
ResponseEntity<String> queryUser(#PathVariable("entityId") String entityId,
#RequestBody QueryUserJsonSchemaPOJO queryUserJsonSchemaPOJO, String inputString,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
return userService.queryUserService(inputString, entityId, request);
}
Created an AOP with #Around annotation which update the inputString argument.
#Around(value = "execution(* com.athmin.rest.UserController.*(..)) || execution(* com.athmin.rest.CityController.*(..)), and args(..) " +
" && #annotation(com.athmin.annotations.JSONSchemaFileName) ")
public Object validateRequestBodyAgainstJsonSchema(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object[] modifiedArgs = proceedingJoinPoint.getArgs();
for (Object o : proceedingJoinPoint.getArgs()) {
if (o instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) o;
requestBody = httpServletRequest.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
}
});
for (int i = 0; i < modifiedArgs.length; i++) {
if (modifiedArgs[i] == null) { // Only inputString is null in my case
modifiedArgs[i] = requestBody;
}
}
proceedingJoinPoint.proceed(modifiedArgs);
}

Related

MultipartFile and nested object in a json to test post request in POSTMAN?

Wanted to test a post request in POSTMAN.
public enum TimeUnit {
HOURS("hours"),
MINUTE("mins");
private String value;
public static TimeUnit get(String text) {
return Arrays.stream(TimeUnit.values())
.filter(a -> Objects.equals(a.getValue(), text))
.findFirst()
.orElse(null);
}
}
public final class SchoolTimeTable {
private Double value;
private TimeUnit unit;
public SchoolTimeTable (double value, TimeUnit unit) {
this.value = value;
this.unit=unit;
}
}
public class SchoolDto {
private String name;
private String address;
private MultipartFile profileImage;
private MultipartFile[] galleryImages;
private SchoolTimeTable openCloseTime;
}
Spring MVC Controller
#PostMapping(value = "/schoolInfo", produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<Object> saveSchoolInfo( #Parameter(required = true, schema = #Schema(implementation = SchoolDto.class)) SchoolDto schoolDto) throws IOException, InterruptedException {
...
}
I want to send SchoolDto (POSTMAN: body->raw->json) in post request to get desired result. But I am not able to create the json which supports SchoolTimeTable (Object) and MultipartFile types. I don't even know whether it is possible with JSON or not.
Note: Same could be achieved using body->form-data with key/value.
Please help.
I think you should not upload files within a application/json request, to do so you should use a multipart/form-data request. Your request may have three parts profileImage, galleryImages and schoolInfo.
Remove profileImage and galleryImages from SchoolDto class
Modify your method signature to support the multipart request
#PostMapping(value = "/schoolInfo", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Object> saveSchoolInfo(#RequestPart(value = "profileImage") MultipartFile profileImage, #RequestPart(value = "galleryImages") MultipartFile[] galleryImages, #RequestPart(value = "schoolInfo") SchoolDto schoolInfo) throws IOException, InterruptedException {
...
}
In addtion you can implement a #SpringBootTest unit test using RestDocumentationExtension to check whether your code works and to produce a curl request sample that will help you to understand how to make a request to your endpoint
See sb-mvc-multipart-demo
When I have used multipart/form-data I was using Spring Boot 2.0.x, Java 8 and with the same method = POST
There are points that you need to implement correctly. Maybe this can help you:
#RequestMapping(value = MEUVOUCHER_FILE, method = POST, consumes = "multipart/form-data", produces = APPLICATION_JSON_UTF8_VALUE)
#ResponseStatus(code = CREATED)
public ObjectResponse<MEUVOUCHERFileRetrievalDto> createMEUVOUCHERFile(
#RequestPart("MEUVOUCHERFile") MultipartFile MEUVOUCHERFile,
#ApiParam(name = "metadata", value = "{ \"MEUVOUCHERTypeId\" : 0, \"relatedContentId\" : 0, \"expireDate\" : \"datetime\" }", required = true)

Spring request params to accept only Integers

I am working on an spring restful endpoint which accepts page range(start & end page number). I want my request params- pageStart and pageEnd to accept only integers. When I pass 'pageStart = a' through postman I get below error:
#RequestMapping(value = "/{accNumber}/abc/xyz", method = RequestMethod.GET)
#Loggable
#ResponseBody
public RestResponse<Class1> getData(
#Loggable #PathVariable(value = "accNumber") String accNumber,
#RequestParam(value = "pageStart", required = false, defaultValue = "0") Integer pageStart,
#RequestParam(value = "pageEnd", required = false, defaultValue = "10") Integer pageEnd,
HttpServletResponse response) throws Exception {
Class1 class1 = new Class1();
class1 = retrieveData(accNumber, pageStart, pageEnd);
RestResponse<Class1> restResponse = new RestResponse<Class1>(
class1);
return restResponse;
}
The request is not valid [Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer'; nested exception is java.lang.NumberFormatException: For input string: \"a\"]
How do I handle this exception and let the user know that he should pass only integers?
You can handle it in two ways
1) Using exception handler method
Have a method in the controller
#ExceptionHandler({Exception.class})
public ModelAndView handleException(Exception ex) {
ModelAndView model = new ModelAndView("Exception");
model.addObject("exception", ex.getMessage());
return model;
}
http://www.codejava.net/frameworks/spring/how-to-handle-exceptions-in-spring-mvc
2) Use String parameter
Use String as the type for all #PathVariable and #RequestParameter parameters then do the parsing inside the handler method.

Swagger: How to document REST imput param, when dtois implicitly coverted to entity?

There isa problem, when we use Jersey2 resource with implicitly dto usage.
Example:
#POST
#ApiOperation(value = "Create pet", response = PetDTO.class)
public Pet create(Pet pet) throws IOException {
return this.petService.save(pet);
}
In this example we implicitly get petDto as param, and than map it to entity.
The question is, is the way to how to configure swagger to document PetDTO as api param, not Pet?
It can be done next way:
#POST
#ApiOperation(value = "Create pet", response = PetDTO.class)
#ApiImplicitParams({
#ApiImplicitParam(name = "Pet dto",
value = "pet", required = true,
dataType = "com.example.PetDTO", paramType = "body")
})
public Pet create(#ApiParam(hidden = true) Pet pet) throws IOException {
}

How do I retrieve query parameters in a Spring Boot controller?

I am developing a project using Spring Boot. I've a controller which accepts GET requests.
Currently I'm accepting requests to the following kind of URLs:
http://localhost:8888/user/data/002
but I want to accept requests using query parameters:
http://localhost:8888/user?data=002
Here's the code of my controller:
#RequestMapping(value="/data/{itemid}", method = RequestMethod.GET)
public #ResponseBody
item getitem(#PathVariable("itemid") String itemid) {
item i = itemDao.findOne(itemid);
String itemname = i.getItemname();
String price = i.getPrice();
return i;
}
Use #RequestParam
#RequestMapping(value="user", method = RequestMethod.GET)
public #ResponseBody Item getItem(#RequestParam("data") String itemid){
Item i = itemDao.findOne(itemid);
String itemName = i.getItemName();
String price = i.getPrice();
return i;
}
While the accepted answer by afraisse is absolutely correct in terms of using #RequestParam, I would further suggest to use an Optional<> as you cannot always ensure the right parameter is used. Also, if you need an Integer or Long just use that data type to avoid casting types later on in the DAO.
#RequestMapping(value="/data", method = RequestMethod.GET)
public #ResponseBody
Item getItem(#RequestParam("itemid") Optional<Integer> itemid) {
if( itemid.isPresent()){
Item i = itemDao.findOne(itemid.get());
return i;
} else ....
}
To accept both #PathVariable and #RequestParam in the same /user endpoint:
#GetMapping(path = {"/user", "/user/{data}"})
public void user(#PathVariable(required=false,name="data") String data,
#RequestParam(required=false) Map<String,String> qparams) {
qparams.forEach((a,b) -> {
System.out.println(String.format("%s -> %s",a,b));
}
if (data != null) {
System.out.println(data);
}
}
Testing with curl:
curl 'http://localhost:8080/user/books'
curl 'http://localhost:8080/user?book=ofdreams&name=nietzsche'
In Spring boot: 2.1.6, you can use like below:
#GetMapping("/orders")
#ApiOperation(value = "retrieve orders", response = OrderResponse.class, responseContainer = "List")
public List<OrderResponse> getOrders(
#RequestParam(value = "creationDateTimeFrom", required = true) String creationDateTimeFrom,
#RequestParam(value = "creationDateTimeTo", required = true) String creationDateTimeTo,
#RequestParam(value = "location_id", required = true) String location_id) {
// TODO...
return response;
#ApiOperation is an annotation that comes from Swagger api, It is used for documenting the apis.
To accept both Path Variable and query Param in the same endpoint:
#RequestMapping(value = "/hello/{name}", method = RequestMethod.POST)
public String sayHi(
#PathVariable("name") String name,
#RequestBody Topic topic,
//#RequestParam(required = false, name = "s") String s,
#RequestParam Map<String, String> req) {
return "Hi "+name +" Topic : "+ topic+" RequestParams : "+req;
}
URL looks like : http://localhost:8080/hello/testUser?city=Pune&Pin=411058&state=Maha
I was interested in this as well and came across some examples on the Spring Boot site.
// get with query string parameters e.g. /system/resource?id="rtze1cd2"&person="sam smith"
// so below the first query parameter id is the variable and name is the variable
// id is shown below as a RequestParam
#GetMapping("/system/resource")
// this is for swagger docs
#ApiOperation(value = "Get the resource identified by id and person")
ResponseEntity<?> getSomeResourceWithParameters(#RequestParam String id, #RequestParam("person") String name) {
InterestingResource resource = getMyInterestingResourc(id, name);
logger.info("Request to get an id of "+id+" with a name of person: "+name);
return new ResponseEntity<Object>(resource, HttpStatus.OK);
}
See here also

Spring controller main methods

I'm new to Spring and Spring MVC. I'm trying to implement a basic web-application to test the functionalities of this framework and I'm finding some difficulties.
I discovered that from version 3, annotations brought many advantages so, controller classes do not have to implement abstract classes (i.e. SimpleFormController), but this means that there are not mandatory methods to implement.
So my question is: which are the common useful methods that one should implement in a controller class?
You only have to implement the methods you wish to correspond to the actions from your various webpages - e.g. to accept the input of a form.
What specific difficulties are you having?
The methods will have relevant annotations to denote that they should be invoked for a particular URL. For example in following code (taken from Official Doc.),
#Controller
public class HelloWorldController {
#RequestMapping("/helloWorld")
public String helloWorld(Model model) {
model.addAttribute("message", "Hello World!");
return "helloWorld";
}
}
helloWorld(Model model) is just any method in a any class. What makes it special is the annotation #RequestMapping which tell that this method should invoked when the requesting URL is /helloWorld
Similar to Santosh I recommend you look at the official doc and the Javadoc: http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/web/bind/annotation/RequestMapping.html
Basically instead of overriding methods your going to use annotations on methods and based on the parameter annotations and method arguments different things will happen. The Javadoc above states the equivalent annotations to be used instead overriding the simpleform methods.
Here is a complete example of CRUD controller that I generated with Roo:
#Controller
#RequestMapping("/timers")
public class MyTimer {
#RequestMapping(method = RequestMethod.POST, produces = "text/html")
public String create(#Valid Timer timer, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
if (bindingResult.hasErrors()) {
populateEditForm(uiModel, timer);
return "timers/create";
}
uiModel.asMap().clear();
timer.persist();
return "redirect:/timers/" + encodeUrlPathSegment(timer.getId().toString(), httpServletRequest);
}
#RequestMapping(params = "form", produces = "text/html")
public String createForm(Model uiModel) {
populateEditForm(uiModel, new Timer());
return "timers/create";
}
#RequestMapping(value = "/{id}", produces = "text/html")
public String show(#PathVariable("id") Long id, Model uiModel) {
uiModel.addAttribute("timer", Timer.findTimer(id));
uiModel.addAttribute("itemId", id);
return "timers/show";
}
#RequestMapping(produces = "text/html")
public String list(#RequestParam(value = "page", required = false) Integer page, #RequestParam(value = "size", required = false) Integer size, Model uiModel) {
if (page != null || size != null) {
int sizeNo = size == null ? 10 : size.intValue();
final int firstResult = page == null ? 0 : (page.intValue() - 1) * sizeNo;
uiModel.addAttribute("timers", Timer.findTimerEntries(firstResult, sizeNo));
float nrOfPages = (float) Timer.countTimers() / sizeNo;
uiModel.addAttribute("maxPages", (int) ((nrOfPages > (int) nrOfPages || nrOfPages == 0.0) ? nrOfPages + 1 : nrOfPages));
} else {
uiModel.addAttribute("timers", Timer.findAllTimers());
}
return "timers/list";
}
#RequestMapping(method = RequestMethod.PUT, produces = "text/html")
public String update(#Valid Timer timer, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
if (bindingResult.hasErrors()) {
populateEditForm(uiModel, timer);
return "timers/update";
}
uiModel.asMap().clear();
timer.merge();
return "redirect:/timers/" + encodeUrlPathSegment(timer.getId().toString(), httpServletRequest);
}
#RequestMapping(value = "/{id}", params = "form", produces = "text/html")
public String updateForm(#PathVariable("id") Long id, Model uiModel) {
populateEditForm(uiModel, Timer.findTimer(id));
return "timers/update";
}
#RequestMapping(value = "/{id}", method = RequestMethod.DELETE, produces = "text/html")
public String delete(#PathVariable("id") Long id, #RequestParam(value = "page", required = false) Integer page, #RequestParam(value = "size", required = false) Integer size, Model uiModel) {
Timer timer = Timer.findTimer(id);
timer.remove();
uiModel.asMap().clear();
uiModel.addAttribute("page", (page == null) ? "1" : page.toString());
uiModel.addAttribute("size", (size == null) ? "10" : size.toString());
return "redirect:/timers";
}
void populateEditForm(Model uiModel, Timer timer) {
uiModel.addAttribute("timer", timer);
}
}

Categories