I have the following java endpoint inside a Springboot RestController annotated with some Swagger annotations for 4 ApiResponses:
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successfully sign in"),
#ApiResponse(code = 400, message = "Missing request body"),
#ApiResponse(code = 404, message = "Schema not found"),
#ApiResponse(code = 500, message = "Internal error")
})
#PostMapping(
path = "/login",
produces = "application/json; charset=utf-8")
public LoginResponse login(
#ApiParam(
name="cred",
value="Credenciales de quien intenta ingresar al sistema")
#RequestBody CredencialesRequest cred
) throws ControllerException {
return accessService.login(cred.getUsuario(), cred.getClave());
}
As you can see, I have declared 4 response codes as a possible HTTP responses: 200, 400, 404 and 500
When I run the application and go to http://localhost:8080/swagger-ui.html the UI shows the 4 codes that I have described in the endpoint. However, it shows MORE http codes. Please take a look at this picture:
The extra codes are: 201 (created), 401 (unauthorized) & 403 (forbidden). Why? For my use case, the "login" endpoint should be always accessible to any user, so at least, 401 & 403 doesn't make sense at all, in this context.
Just like it was said in the comments, in order to remove the extra http codes in the swagger UI, we need to modify our configuration file by adding useDefaultResponseMessages(false) to the api() method in our SwaggerConfig like this:
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.useDefaultResponseMessages(false) // I HAD TO ADD THIS LINE !!!!
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo());
}
That's it !
Related
i'm writing a java adapter for posting something to a webService.
In the http service i use an ExceptionHandler:
#org.springframework.web.bind.annotation.ExceptionHandler
public ResponseEntity<String> handle(BadCommandException ex) {
return ResponseEntity.badRequest().body(ex.getMessage());
}
That works well, if i call it with curl/postman.
But how can i get the error message that is in the body with a reactive WebClient ?
Part of my client-lib:
private Mono<Optional<String>> post(String bearerToken, Model model, IRI outbox) {
return WebClient.builder()
.build()
.post()
.uri(outbox.stringValue())
.contentType(new MediaType("text","turtle"))
.body(BodyInserters.fromValue(modelToTurtleString(model)))
.header("Authorization", "Bearer " + bearerToken)
.header("profile", "https://www.w3.org/ns/activitystreams")
.accept(new MediaType("text", "turtle"))
.retrieve()
.toEntity(String.class)
.map(res->res.getHeaders().get("Location").stream().findFirst());
}
In success case, i want to return the string from the location header. But how can i for example throw an Exception if the http-status is 400 and add the error message from the body to the exception?
Thanks
the spring documentation on webclient retreive() has examples of how to handle errors. I suggest you start there.
Mono<Person> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(HttpStatus::is4xxClientError, response -> ...)
.onStatus(HttpStatus::is5xxServerError, response -> ...)
.bodyToMono(Person.class);
.onStatus(HttpStatus::is4xxClientError,
clientResponse -> { return clientResponse.createException();
Javadoc:
/**
* Create a {#link WebClientResponseException} that contains the response
* status, headers, body, and the originating request.
* #return a {#code Mono} with the created exception
* #since 5.2
*/
Mono<WebClientResponseException> createException();
My problem was my testcase 'test bad request'. Because of a refactoring it was sending an empty request body instead of a bad one and the annotation #RequestBody has an attribute 'required' which is default true. So the response body was always empty and did not contain my error message as expected ;-)
public Mono<ResponseEntity<Void>> doIt(Principal principal, #PathVariable String actorId, #RequestBody String model) {
...
that cost me hours ;-)
I have a controller handler method:
#ApiResponses({
#ApiResponse(code = 201, message = "aaa", response = Response.class),
#ApiResponse(code = 400, message = "bbb")
})
#PostMapping("api/aa")
public Response save() {
...
}
...
Whenever I add a custom Docket configuration as below. #ApiResponse defined in #ApiResponses don't seem to work anymore. As I no longer see them showing up on the UI or under v2/api-docs's json. Any ideas what I am missing?
#Bean
public Docket api() {
return new Docket(DocumentationType.SPRING_WEB)
.host(kongHost)
.useDefaultResponseMessages(false)
.select()
.apis(RequestHandlerSelectors.basePackage("com.mypackage.controller"))
.paths(PathSelectors.ant("/api/*"))
.build();
}
Turns out I would need to specify the DocumentationType to be SWAGGER_2
I'm using Swagger annotations and SpringFox to produce a Swagger spec for my REST API (being built with Sprint Boot). I'm annotating each method with the #ApiResponse codes that will be returned. For example:
#DeleteMapping("/{pipelineName}")
#ApiOperation(value = "Delete a specific pipeline")
#ApiResponses({
#ApiResponse(code = 204, message = "Pipeline has been deleted"),
#ApiResponse(code = 404, message = "Pipeline does not exist")
})
public void deletePipeline(#PathVariable("pipelineName") #ApiParam(value = "Name of pipeline", required = true) String name){
...
}
However, something (I assume SpringFox) is adding a 200 response code to every API call regardless of whether it's defined or not. I'm aware that I can remove this by adding a #ResponseStatus annotation to each method, but a) that seems like unneccessary duplication of the #ApiResponse definitions and b) some methods could return one of multiple 2xx codes.
Is there a way or removing the 200 code that is added by default?
I believe you need to define the default response status for your method as you mentioned in your question.#ApiResponse is from Springfox, #ResponseStatus is from Spring framework. It is not exactly a duplication. An HTTP 200 response is the default behavior, and Springfox do not do anything to change that, so it just adds the additional codes on top of the automatically detected one.
Example for HTTP 204:
#ResponseStatus(value = HttpStatus.NO_CONTENT)
In your code:
DeleteMapping("/{pipelineName}")
#ApiOperation(value = "Delete a specific pipeline")
#ApiResponses({
#ApiResponse(code = 204, message = "Pipeline has been deleted"),
#ApiResponse(code = 404, message = "Pipeline does not exist")
})
#ResponseStatus(value = HttpStatus.NO_CONTENT)
public void deletePipeline(#PathVariable("pipelineName") #ApiParam(value = "Name of pipeline", required = true) String name){
...
}
For others that may be checking this topic: if Springfox is generating other HTTP codes, you may also need this.
What I would do is:
#DeleteMapping("/{pipelineName}")
#ApiOperation(value = "Delete a specific pipeline")
#ApiResponses({
#ApiResponse(code = 204, message = "Pipeline has been deleted"),
#ApiResponse(code = 404, message = "Pipeline does not exist")
})
public #ResponseBody ResponseEntity<Object> deletePipeline(#PathVariable("pipelineName") #ApiParam(value = "Name of pipeline", required = true) String name){
if (deleted) {
return new ResponseEntity<>("Pipeline has been deleted", HttpStatus.CREATED); //code 204
} else {
return new ResponseEntity<>("Pipeline does not exist", HttpStatus.NOT_FOUND); //code 404
}
}
Now, I wouldn't use 204 as a code to indicate that something was deleted, I would keep it at 200, but that's up to you.
Hope this helps!
Currently You can't remove standard 200 code. I found the same question on the github and developers said that is bug in springfox:
You can read about this in this link
I tried to remove the 200 code in 2.9.2 version of SpringFox and this bug is still exists
You need to mention useDefaultResponseMessages to false while creating the Docket object. Now it will document only that you have added in #ApiResponses.
new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.ty"))
.build()
.apiInfo(apiInfo)
.useDefaultResponseMessages(false);
I instatiate docket like this
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.config.internal"))
.paths(Predicates.or(PathSelectors.ant("/api**/**")))
.build();
}
I created a set of stub endpoints that imitate the real one for /login or /oauth.
#Api("Authentication")
#RequestMapping("/api")
public interface LoginEndpointApi {
#ApiOperation(value = "Github SSO endpoint", notes = "Endpoint for Github SSO authentication")
#ApiResponses({
#ApiResponse(code = 200, message = "HTML page of main application")
})
#GetMapping("/oauth/github")
default void oauthGithub() {
throw new UnsupportedOperationException();
}
#ApiOperation(value = "Get CSRF token", notes = "Returns current CSRF token")
#ApiResponses({
#ApiResponse(code = 200, message = "CSRF token response", response = String.class,
examples = #Example({#ExampleProperty(value = "015275eb-293d-4ce9-ba07-ff5e1c348092")}))
})
#GetMapping("/csrf-token")
default void csrfToken() {
throw new UnsupportedOperationException();
}
#ApiOperation(value = "Login endpoint", notes = "Login endpoint for authorization")
#ApiResponses({
#ApiResponse(code = 200, message = "Successful authentication")
})
#PostMapping("/login")
default void login(
#ApiParam(required = true, name = "login", value = "login body")
#RequestBody LoginRequest loginRequest) {
throw new UnsupportedOperationException();
}
}
But it doesn't recognize it. It is located in the same com.config.internal package as I described.
But the page swagger ui is empty and shows that No operations defined in spec!
What is the problem?
If you want to provide swagger documentation for your request mappings specified above you could simply describe it with .paths(Predicates.or(PathSelectors.ant("/api/**"))) path matchers. But if your path includes something more complicated like api + text without backslash separator then you should get known with
https://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/util/AntPathMatcher.html
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.