I'm using Swagger2 with Springfox and Spring Boot. I have an endpoint defined like so:
#ApiOperation(value = "save", nickname = "Save Store")
#ApiResponses(value = {
#ApiResponse(code = 201, message = "Created"),
#ApiResponse(code = 401, message = "Unauthorized"),
#ApiResponse(code = 403, message = "Forbidden"),
#ApiResponse(code = 500, message = "Failure", response = ErrorResource.class)})
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.CREATED)
public void save(#Valid #RequestBody Store store, BindingResult bindingResult, HttpServletRequest request, HttpServletResponse response) {
if (bindingResult.hasErrors()) {
throw new InvalidRequestException("Invalid Store", bindingResult);
}
this.storeService.save(store);
response.setHeader("Location", request.getRequestURL().append("/").append(store.getId()).toString());
}
The generated API docs are showing the id of Store in the Model Schema. Technically, when creating a Store the JSON should not contain the id. I'm trying to figure out how to tell Swagger/Springfox to ignore the id but only for this endpoint.
You can hide a field from a model by annotating the property of the class with #ApiModelProperty and setting its hidden property to true.
import io.swagger.annotations.ApiModelProperty;
public class Store {
#ApiModelProperty(hidden = true)
private Long id;
}
Unfortunately, by doing so, you will hide the id field on every endpoint which uses the Store class as an input. Showing the field for another endpoint would require a separate class.
Related
As shown in the picture, I want to add my description in my Swagger UI. The value "Example" (in name and description) comes from the parameter "Example" in the controller where I have used the #Requestbody. I am using Spring framework. How do I add my description to it?
This is my controller file
#ApiOperation(value = "It will be used to print the document of quotation.",response = GenerateDocPrintResponse.class)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successfully retrieved list"),
#ApiResponse(code = 401, message = "You are not authorized to view the resource"),
#ApiResponse(code = 403, message = "Accessing the resource you were trying to reach is forbidden"),
#ApiResponse(code = 404, message = "The resource you were trying to reach is not found")
}
)
#RequestMapping(value = "/service/generateDocPrint", method = RequestMethod.POST)
public #ResponseBody String generateDocPrint(#RequestBody GetdocprintRequest Example,HttpServletRequest request, HttpServletResponse response,
#RequestBody String newJson) throws Exception {
................
}
This is my Pojo file
package com.iii.fw.models.generatedocprint;
import com.iii.fw.models.common.RequestHeader;
import io.swagger.annotations.ApiModelProperty;
public class GetdocprintRequest {
RequestHeader reqHeader;
private ReqPayloadGDP reqPayload;
public ReqPayloadGDP getReqPayload() {
return reqPayload;
}
public void setReqPayload(ReqPayloadGDP reqPayload) {
this.reqPayload = reqPayload;
}
public GetdocprintRequest withReqPayload(ReqPayloadGDP reqPayload) {
this.reqPayload = reqPayload;
return this;
}
}
#ApiOperation(value = "It will be used to print the document of quotation. ", notes = "put your description here ", response = )
Does this answer your question ?
Use the notes proptery.
Using notes, we can provide more details about the operation. For instance, we can place a text describing the endpoint's restrictions:
https://www.baeldung.com/swagger-apioperation-vs-apiresponse#2-the-notes-property
#ApiOperation(value = "Gets customer by ID", notes = "Customer must exist")
In my spring controller class i have below two methods
#GetMapping(value = "/published_messages", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> handleEmptyQueryParam() throws Exception
{
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Invalid Request , No Request Param received in the request");
}
#GetMapping(value = "/published_messages", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> getEdiDetailsByusername(#RequestParam(required=false, value="username") String username) throws Exception
{
List<String> userList = userService.getUserList(username);
return isAValidResponse(userList);
}
with this when starting the app , got below exception
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'UserAppController' method
public org.springframework.http.ResponseEntity<java.lang.String> com.rby.trans.controller.UserAppController.getEdiDetailsByusername(java.lang.String) throws java.lang.Exception
to {[/published_messages],methods=[GET],produces=[application/json]}: There is already 'UserAppController' bean method
public org.springframework.http.ResponseEntity<java.lang.String> com.tgt.trans.controller.UserAppController.handleEmptyQueryParam() throws java.lang.Exception mapped.
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.assertUniqueMethodMapping(AbstractHandlerMethodMapping.java:576)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.register(AbstractHandlerMethodMapping.java:540)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerHandlerMethod(AbstractHandlerMethodMapping.java:264)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.detectHandlerMethods(AbstractHandlerMethodMapping.java:250)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.initHandlerMethods(AbstractHandlerMethodMapping.java:214)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.afterPropertiesSet(AbstractHandlerMethodMapping.java:184)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.afterPropertiesSet(RequestMappingHandlerMapping.java:127)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1642)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1579)
... 50 common frames omitted
seems something issue with swagger , which we have below dependencies in our build file
compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.4.0'
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.4.0'
can anyone suggest what to in this case ,
whatever am following and doing in the rest controller is correct only and it is possible to do that in spring , then why swagger giving errors on that ?
Note : I got the answer , we need to use params attribute in the #GetMapping , it solved my issue .
Thanks to all .
I had a similar error some time ago when I was creating a demo app during a presentation. The problem why this issue occurred was because in one of my endpoints mappings (e.g. other #GetMapping) I used 'name' instead of 'value' attribute so the problem might be in the part of the code which you did not provide.
We had the same error specifically for SpringBoot 2.5, but with the following setup
Setup:
Controller Api:
#Api(value = "items", description = "the items API")
public interface ItemsApi {
#GetMapping(value = "items/{itemId}",
produces = {"application/json"})
Item getItem(#ApiParam(value = "The item Id number in the app, a UUID", required = true) #PathVariable("itemId") UUID itemId,
HttpServletRequest httpServletRequest);
}
Feign Client Api:
#FeignClient(value = "Foo-v1-0-Server", contextId = "Foo-v1-0-Server-ItemClient")
#RequestMapping("/")
public interface FooFeignClient {
#ApiOperation(value = "Gets a list of item metadata")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success", responseContainer = "List", response = FooItemResponse.class),
#ApiResponse(code = 401, message = "Unauthorized"),
#ApiResponse(code = 404, message = "Not Found"),
#ApiResponse(code = 500, message = "Failure")})
#RequestMapping(value = "items/{itemId}", method = RequestMethod.GET, produces = "application/json")
List<FooItemResponse> getDocumentList(#PathVariable("itemId") String itemId);
Error:
java.lang.IllegalStateException: Ambiguous mapping. Cannot map ‘com.contoso.foo.client.feign.FooFeignClient
Cause:
We tracked it down to feign client having the #RequestMapping annotation at the class level. This was causing it to be picked up for WebMvcRegistrations.
Resolution:
Remove the #RequestMapping annotation at the class level on the feign client and republish a new version.
please i have this error when trying to create a customer. Can some one help me? May be i am missing some thing. I have even try to change the #PostMapping to #RequestMapping till yet. Thks
My Controller code
`#PostMapping("CREATE_CUSTOMER_ENDPOINT")
#ResponseStatus(value = HttpStatus.OK)
#ApiResponses(value = {
#ApiResponse(code = 201, message = "The Customer was Created", response = CustomerDto.class),
#ApiResponse(code = 400, message = "Bad Request", response = ResponseError.class),
#ApiResponse(code = 500, message = "Unexpected error")
})
public ResponseEntity createCustomer(final HttpServletRequest request, #RequestBody CustomerDto customerDto)
{
if (log.isDebugEnabled()){
log.debug("[CustomerResource] POST {} : Creating customer ", CREATE_CUSTOMER_ENDPOINT);
}
if(customerDto.getUidpk()!=null) {
ResponseError error = new ResponseError(HttpStatus.BAD_REQUEST.getReasonPhrase(), "A customer Already exist with an Uidpk");
log.error("[CustomerResource] The customer Already exist ({}) with an Uidpk", customerDto.getUidpk());
return new ResponseEntity<>(error, null, HttpStatus.BAD_REQUEST);
}
CustomerDto result = customerService.createCustomer(customerDto);
log.debug("[CustomerResource] Customer created ({})", result.getUidpk());
return new ResponseEntity<>(result, HeaderUtil.putLocationHeader(request.getRequestURL().toString() + "/" + result.getUidpk()), HttpStatus.CREATED);
} `
My endpoints
private static final String CUSTOMER_SEARCH_USER_ID_ENDPOINT = "/customers/{userId:.+}";
private static final String CREATE_CUSTOMER_ENDPOINT= "/customer";
private static final String UPDATE_CUSTOMER_ENDPOINT= "/customer";
private static final String DELETE_CUSTOMER_ENDPOINT = CREATE_CUSTOMER_ENDPOINT + "/{uidpk}";
This is the response of Postman
Postman sample
When you send JSON payloads in HTTP request, you need to specify Content-Type HTTP header with value application/json.
I want to add a header parameter field in the auto-generated swagger ui documentation of my rest service. I use Spring and Springfox.
public ResponseEntity<User> saveNewUser(
#ApiParam(value = "the user to create", required = true) #RequestBody User user) throws RestServiceException {
userService.save(user);
return new ResponseEntity<User>(user, HttpStatus.OK);
}
As you see I have already a body type parameter. I just want to add a header type one.
I prefer to use #ApiImplicitParam after my #RequestMapping rather than as function parameters because generally you might process your headers in a filter (eg authentication) and you are not needing the values in that method.
Besides if you need them in the method Swagger auto provides the field for a #HeaderParam
This style also Improves readability and flexibility when some calls need headers and other don't.
Example
#PostMapping
#ApiImplicitParam(name = "Authorization", value = "Access Token", required = true, allowEmptyValue = false, paramType = "header", dataTypeClass = String::class, example = "Bearer access_token")
fun addJob(jobRequest: Job): ResponseEntity<*>{}
If all or most for your endpoints need header that I'll rather configure it as seen here
If you have to declare several header params, you need to use the #ApiImplicitParams annotation:
#PostMapping
#ApiImplicitParams({
#ApiImplicitParam(name = "Authorization", value = "Access Token", required = true, allowEmptyValue = false, paramType = "header", dataTypeClass = String.class, example = "Bearer access_token"),
#ApiImplicitParam(name = "X-Custom-Header", value = "A Custom Header", required = true, allowEmptyValue = false, paramType = "header", dataTypeClass = String.class, example = "my header example")
})
fun addJob(jobRequest: Job): ResponseEntity<*>{}
I just added #RequestHeader(value="myHeader") String headerStr :
public ResponseEntity<User> saveNewUser(
#RequestHeader(value="myHeader") String headerStr,
#ApiParam(value = "the user to create", required = true) #RequestBody User user) throws RestServiceException {
userService.save(user);
return new ResponseEntity<User>(user, HttpStatus.OK);
}
(import org.springframework.web.bind.annotation.RequestHeader;)
You can also add a global header on every service in your documentation with the solution described here : Spring + Springfox + Header Parameters
If you are having more header parameters, then every API will have that many #RequestHeader
To avoid this and your API looks simple you can use HeaderInterceptor to capture the header information.
In preHandle() , you need to extract the headerInfo in to a an Object and set it as RequestAttribute
public class MyHeaderInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HeaderVo headerVo = HeaderVo.createReqHeaderinput(
request.getHeader("authorization"),
request.getHeader("contentType"),
request.getHeader("myHeaderParam0"),
request.getHeader("myHeaderParam1"),
request.getHeader("myHeaderParam3"),
request.getHeader("myHeaderParam4"),
request.getHeader("myHeaderParam5")
);
// You can do any validation of any headerInfo here.
validateHeader(headerVo);
request.setAttribute("headerName", headerVo);
return true;
}
}
Your API will looks like the below with a #RequestAttribute("headerName")
public #ResponseBody
ResponseEntity<MyResponse> getSomeApi(
//Headers common for all the API's
#RequestAttribute("headerName") HeaderVo header ,
#ApiParam(value = "otherAPiParam", required = true, defaultValue = "")
#PathVariable(value = "otherAPiParam") String otherAPiParam,
#ApiParam(value = "otherAPiParam1", required = true, defaultValue = "")
#RequestParam(value = "otherAPiParam1") String otherAPiParam1,
#ApiParam(value = "otherAPiParam2, required = true, defaultValue = "")
#RequestParam(value = "otherAPiParam2") String otherAPiParam2
) throws MyExcp {
....
}
Your Swagger still should describes all headers of the API, for that you can add parameters in swagger Docket, SwaggerConfig
Please note ignoredParameterTypes, we mentioned to ignore HeaderVo, because that is internal to the application. swagger doesnt require to show that
#Bean
public Docket postsApi() {
//Adding Header
ParameterBuilder aParameterBuilder = new ParameterBuilder();
List<Parameter> aParameters = new ArrayList<Parameter>();
aParameters.clear();
aParameterBuilder.name("myHeaderParam0").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
aParameters.add(aParameterBuilder.build());
aParameterBuilder.name("myHeaderParam1").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
aParameters.add(aParameterBuilder.build());
....
....
return new Docket(DocumentationType.SWAGGER_2).groupName("public-api")
.apiInfo(apiInfo()).select().paths(postPaths()).build().ignoredParameterTypes(HeaderVo.class).globalOperationParameters(aParameters);
}
sujith kasthoori's answer is the correct answer if one wants to send a header key-value from swagger UI for every end point. Its just the case that ParameterBuilder & Parameter are deprecated now.
List<RequestParameter> globalRequestParameters = new ArrayList<>();
RequestParameterBuilder customHeaders = new RequestParameterBuilder();
customHeaders.name("X-TenantID").in(ParameterType.HEADER)
.required(true)
.build();
Above is for when a header parameter named "X-TenantID" is mandatory to be sent with every request.
Then it needs to be added to Docket definition , .globalRequestParameters(globalRequestParameters)
These are the method headers for the sample application I'm trying to make. Unfortunately I don't see the #ApiOperation or #ApiResponse or #ApiResponses annotations taking effect.
I also have a sample class and an Application class(which contains swagger configuration info and the like)
#Api(value = "/v1/", description = "Test API", produces = "application/json")
#RestController
#RequestMapping("/v1/")
class SampleRestController {
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successfully added"),
#ApiResponse(code = 416, message = "List is empty") })
#RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, value = "samples/{sb}",
method = RequestMethod.GET)
public Map<String, String> sbs(#PathVariable("sb") String sb) {
}
#ApiOperation(
value = "Seeing where this shows up",
notes = "we should see this in the implememtation notes",
response = Sample.class,
httpMethod = "GET"
)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successfully added"),
#ApiResponse(code = 416, message = "List is empty") })
#RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, value = "samples/{pb}",
method = RequestMethod.GET)
public Sample ps(#PathVariable String pb) {
}
#RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, value = "samples",
method = RequestMethod.GET)
public Collection<Map<String, String>> cs(#RequestParam(value = "cid", required = true, defaultValue = "")
String cid) {
}
}
Here is the relevant portion of my swagger json
{"swagger":"2.0","info":{"description":"All about the Samples","title":"Samples API","contact":{},"license":{}},"host":"localhost:8080","basePath":"/","tags":[{"name":"sample-rest-controller","description":"Sample Rest Controller"}],
"paths":{
"/v1/samples":{"get":{"tags":["sample-rest-controller"],"summary":"cs","operationId":"csUsingGET","consumes":["application/json"],"produces":["application/json"],"parameters":[{"name":"cid","in":"query","description":"cid","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/Collection«Map«string,string»»"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},
"/v1/samples/{pb}":{"get":{"tags":["sample-rest-controller"],"summary":"ps","operationId":"psUsingGET","consumes":["application/json"],"produces":["application/json"],"parameters":[{"name":"pb","in":"path","description":"pb","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/Sample"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},
"/v1/samples/{sb}":{"get":{"tags":["sample-rest-controller"],"summary":"sbs","operationId":"sbsUsingGET","consumes":["application/json"],"produces":["application/json"],"parameters":[{"name":"sb","in":"path","description":"sb","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}}},
I'm using springfox-swagger2 version 2.1.1 and I'm viewing my UI through the default UI provided by swagger at their live demo app.
Can someone tell me what I'm doing wrong?
Thanks!
I was playing around and changed my import from
import com.wordnik.swagger.annotations.;
to
import io.swagger.annotations.;
and all the annotations appear to work.
So, looks like the com.wordnik.swagger.annotations import no longer works, or at least doesn't work properly?
Confirmed by Dilip Krish here