Mandatory Validation for pathVariable not working - spring

Currently we are adding mandatory validation for pathVariable - having customized message in response.
When #PathVariable is removed, then it is giving 404 error and not going into any of the exceptionHandler. Can you please help to resolve this issue?
#PostMapping(path = "{id}")
public ResponseEntity<String> migrate(#PathVariable(value = "id")
#Size(min = 1, max = 64, message = INVALID_FIELD_LENGTH_DETAILS)
String id) {
...
}
Error response as below:
{
"timestamp": "2022-02-08T15:26:58.649+00:00",
"status": 404,
"error": "Not Found",
"message": "",
"path": "/migrations"
}

javax.validation annotations are not supported yet: https://github.com/spring-projects/spring-framework/issues/11041
You can try several workarounds, e.g.:
#PostMapping(path = {"", "/{id}"})
public ResponseEntity<String> migrate(#PathVariable(value = "id", required = false)
Optional<String> id) {
if (!id.isPresent() && id.get().length() > 64) {
return new ResponseEntity<>("Validation error", HttpStatus.BAD_REQUEST);
} else {
return new ResponseEntity<>(id.orElse(""), HttpStatus.OK);
}
}

Related

Swagger UI does not display some of the endpoints in some controllers

I am working on a bank application using Spring Boot 2.4.0 and Gradle 6.7 and I want to document and test my API using Swagger 3.0.0. I have four controllers for now: EmployeeController, ClientController, BankAccountController, TransactionController. Every controller has 5 end points:
GET /{entities}
GET /{entities}/{id}
POST /{entities}
PUT /{entities}/{id}
DELETE /{entities}/{id}
where {entities} is used here as a placeholder for the entities the specific controller is responsible for: employees, clients, accounts, transactions, respectively.
The problem is that Swagger displays all the 5 endpoints only for BankAccountsController. For the other controllers only GET /{entities}, POST /{entities}, PUT /{entities}/{id} are displayed. Endpoints GET /{entities}/{id}, DELETE /{entities}/{id} are missing, despite of the fact all controllers are implemented analogically. I tested endpoints in Postman and they work properly.
Here is the working BankController:
#Slf4j
#RestController
#RequestMapping("/accounts")
#Api(value = "Operations related to bank accounts", tags = {"Bank Accounts"})
#SwaggerDefinition(tags = {#Tag(name = "Bank Accounts", description = "Operations related to bank accounts") })
public class BankAccountController {
private final BankAccountService bankAccountService;
public BankAccountController(BankAccountService bankAccountService) {
this.bankAccountService = bankAccountService;
}
#ApiOperation(
value="Find a bank account by id",
notes = "Provide an id to lookup specific bank account from the database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#GetMapping(value = "/{id}", produces = {"application/json"})
public ResponseEntity<BankAccount> getBankAccountById(#PathVariable Long id) {
return ResponseEntity.of(bankAccountService.getBankAccountById(id));
}
#ApiOperation(
value="Retrieve all bank accounts",
notes = "Used to fetch all bank accounts from the database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#GetMapping(value = "/", produces = {"application/json"})
public ResponseEntity<List<BankAccount>> getAll() {
return ResponseEntity.of(
Optional.of(bankAccountService.getAll()));
}
#ApiOperation(
value="Add a new bank account",
notes = "Used to insert new bank account in the database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#PostMapping(value = "/", consumes = {"application/json"}, produces = {"application/json"})
public ResponseEntity<BankAccount> addBankAccount(#RequestBody BankAccount newaccount) {
return ResponseEntity.of(
Optional.of(bankAccountService.save(newaccount)));
}
#ApiOperation(
value="Modify a bank account",
notes = "Used to replace an old bank account with a new one with a certain id in the database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#PutMapping(value = "/{id}", consumes = {"application/json"}, produces = {"application/json"})
public ResponseEntity<BankAccount> mofifyBankAccount(#RequestBody BankAccount newAccount, #PathVariable Long id) {
return ResponseEntity.of(
bankAccountService.getBankAccountById(id)
.map(account -> {
account.setAccountNumber(newAccount.getAccountNumber());
account.setIban(newAccount.getIban());
account.setType(newAccount.getType());
account.setCurrency(newAccount.getCurrency());
account.setBalance(newAccount.getBalance());
return bankAccountService.save(account);
}));
}
#ApiOperation(
value="Delete a bank account with indicated id",
notes = "Used to delete a bank account by id from the database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#DeleteMapping(value = "/{id}", produces = {"application/json"})
public ResponseEntity<BankAccount> deleteBankAccount(#PathVariable Long id) {
return ResponseEntity.of(
bankAccountService.deleteBankAccount(id));
}
}
Here are the other 3 controllers:
EmployeeController:
#Slf4j
#RestController
#RequestMapping("/employees")
#Api(value ="Operations related to employees", tags = {"Employees"})
#SwaggerDefinition(tags = {#Tag(name = "Employees", description = "Operations related to employees")})
public class EmployeeController {
private final EmployeeService employeeService;
#Autowired
public EmployeeController(EmployeeService employeeService) {
this.employeeService = employeeService;
}
#ApiOperation(value="Find an employee by id",
notes = "Provide and id to lookup specific employee from database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#GetMapping(value = "/{id}", produces = {"application/json"})
public ResponseEntity<Employee> getEmployeeById(#PathVariable Long id) {
return ResponseEntity.of(employeeService.getEmployeeById(id));
}
#ApiOperation(
value="Retrieve all employees",
notes = "Used to fetch all employees from the database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#GetMapping(value = "/", produces = {"application/json"})
public ResponseEntity<List<Employee>> getAll() {
return ResponseEntity.of(Optional.of(employeeService.getAll()));
}
#ApiOperation(value="Add a new employee", notes = "Used to insert new employee in the database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#PostMapping(value = "/", consumes = {"application/json"}, produces = {"application/json"})
public ResponseEntity<Employee> addEmployee(#RequestBody Employee newEmployee) {
return ResponseEntity.of(
Optional.of(employeeService.save(newEmployee)));
}
#ApiOperation(
value="Modify an employee",
notes = "Used to replace an old employee with a new one with a certain id in the database ")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#PutMapping(value = "/{id}", consumes = {"application/json"}, produces = {"application/json"})
public ResponseEntity<Employee> modifyEmployee(#RequestBody Employee newEmployee, #PathVariable Long id) {
return ResponseEntity.of(
employeeService.getEmployeeById(id)
.map(employee -> {
employee.setName(newEmployee.getName());
employee.setMidName(newEmployee.getMidName());
employee.setSurname(newEmployee.getSurname());
employee.setPhone(newEmployee.getPhone());
employee.setAddress(newEmployee.getAddress());
employee.setPosition(newEmployee.getPosition());
employee.setDateHired(newEmployee.getDateHired());
employee.setStartOfExperience(newEmployee.getStartOfExperience());
return employeeService.save(newEmployee);
}));
}
#ApiOperation(
value="Delete an employee with indicated id",
notes = "Used to delete an employee by id from the database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#DeleteMapping(value = "/{id}", produces = {"application/json"})
public ResponseEntity<Employee> deleteEmployee(#PathVariable Long id) {
return ResponseEntity.of(
employeeService.deleteEmployee(id));
}
}
ClientController:
#Slf4j
#RestController
#RequestMapping("/clients")
#Api(value = "Operations related to clients", tags = {"Clients"})
#SwaggerDefinition(tags = {#Tag(name = "Clients", description = "Operations related to clients") })
public class ClientController {
private final ClientService clientService;
#Autowired
public ClientController(ClientService clientService) {
this.clientService = clientService;
}
#ApiOperation(
value="Find a client by id",
notes = "Provide and id to lookup specific client from the database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#GetMapping(value = "/{id}", produces = {"application/json"})
public ResponseEntity<Client> getClientById(#PathVariable Long id) {
return ResponseEntity.of(
clientService.getClientById(id));
}
#ApiOperation(
value="Retrieve all clients",
notes = "Used to fetch all clients from the database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#GetMapping(value = "/", produces = {"application/json"})
public ResponseEntity<List<Client>> getAll() {
return ResponseEntity.of(
Optional.of(clientService.getAll()));
}
#ApiOperation(
value="Add a new client",
notes = "Used to insert new client in the database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#PostMapping(value = "/", consumes = {"application/json"}, produces = {"application/json"})
public ResponseEntity<Client> addCustomer(#RequestBody Client newClient) {
return ResponseEntity.of(
Optional.of(clientService.save(newClient)));
}
#ApiOperation(
value="Modify a client",
notes = "Used to replace an old client with a new one with a certain id in the database ")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#PutMapping(value = "/{id}", consumes = {"application/json"}, produces = {"application/json"})
public ResponseEntity<Client> modifyClient(#RequestBody Client newClient, #PathVariable Long id) {
return ResponseEntity.of(
clientService.getClientById(id)
.map(customer -> {
customer.setName(newClient.getName());
customer.setMidName(newClient.getMidName());
customer.setSurname(newClient.getSurname());
customer.setAccountManager(newClient.getAccountManager());
customer.setAddress(newClient.getAddress());
customer.setEmail(newClient.getEmail());
customer.setIdCardNumber(newClient.getIdCardNumber());
customer.setIdCardIssueDate(newClient.getIdCardIssueDate());
customer.setDebitCardNumber(newClient.getDebitCardNumber());
customer.setCreditCardNumber(newClient.getDebitCardNumber());
return clientService.save(customer);
}));
}
#ApiOperation(value="Delete a client with indicated id.",
notes = "Used to delete a client by id from the database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#DeleteMapping(value = "/{id}", produces = {"application/json"})
public ResponseEntity<Client> deleteClient(#PathVariable Long id) {
return ResponseEntity.of(
clientService.deleteClient(id));
}
}
and the TransactionController:
#Slf4j
#RestController
#RequestMapping("/transactions")
#Api(value = "Operations related to financial transactions", tags = {"Transactions"})
#SwaggerDefinition(tags = {#Tag(name = "Transactions", description = "Operations related to financial transactions")})
public class TransactionController {
private TransactionService transactionService;
#Autowired
public TransactionController(TransactionService transactionService) {
this.transactionService = transactionService;
}
#ApiOperation(
value="Find a financial transaction by id",
notes = "Provide and id to lookup specific financial transaction from the database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#GetMapping(value = "/{id}", produces = {"application/json"})
public ResponseEntity<Transaction> getTransactionById(#PathVariable Long id) {
return ResponseEntity.of(transactionService.getTransactionById(id));
}
#ApiOperation(
value="Retrieve all financial transactions",
notes = "Used to fetch all financial transactions from the database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#GetMapping(value = "/", produces = {"application/json"})
public ResponseEntity<List<Transaction>> getAll() {
return ResponseEntity.of(
Optional.of(transactionService.getAll()));
}
#ApiOperation(
value="Add a new financial transaction",
notes = "Used to insert new financial transaction in the database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#PostMapping(value = "/", consumes = {"application/json"}, produces = {"application/json"})
public ResponseEntity<Transaction> addTransaction(#RequestBody Transaction newTransaction) {
return ResponseEntity.of(
Optional.of(transactionService.save(newTransaction)));
}
#ApiOperation(
value="Modify a financial transaction",
notes = "Used to replace an old financial transaction with a new one with a certain id in the database ")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#PutMapping(value = "/{id}", consumes = {"application/json"}, produces = {"application/json"})
public ResponseEntity<Transaction> modifyTransaction(#RequestBody Transaction newTransaction, #PathVariable Long id) {
return ResponseEntity.of(
transactionService.getTransactionById(id)
.map(transaction -> {
transaction.setAmount(newTransaction.getAmount());
transaction.setSender(newTransaction.getSender());
transaction.setReceiver(newTransaction.getReceiver());
return transactionService.save(transaction);
}));
}
#ApiOperation(
value="Delete a financial transaction with indicated id",
notes = "Used to delete a financial transaction by id from the database")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "Not found", response = ErrorStub.class),
#ApiResponse(code = 500, message = "Server failure", response = ErrorStub.class)
})
#DeleteMapping(value = "/{id}", produces = {"application/json"})
public ResponseEntity<Transaction> deleteTransaction(#PathVariable Long id) {
return ResponseEntity.of(transactionService.deleteTransaction(id));
}
}
I checked if everything is analogical really and I could not find a solution, searched in google as well but there found mostly questions for non-working Swagger UI, not for partially missing endpoints.
Here is my Swagger configurstion class:
#Import({SpringDataRestConfiguration.class, BeanValidatorPluginsConfiguration.class})
#Configuration
public class Swagger2Config {
private static final Set<String> DEFAULT_PRODUCES_AND_CONSUMES =
new HashSet<>(Arrays.asList("application/json"));
#Bean
public Docket swaggerConfiguration() {
return new Docket(DocumentationType.SWAGGER_2)
.host("http://localhost:8080")
.directModelSubstitute(LocalDate.class, Date.class)
.pathProvider(new DefaultPathProvider())
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.bank.controller"))
.paths(PathSelectors.any())
.build()
.pathMapping("/")
.genericModelSubstitutes(ResponseEntity.class)
.apiInfo(apiDetails())
.produces(DEFAULT_PRODUCES_AND_CONSUMES)
.consumes(DEFAULT_PRODUCES_AND_CONSUMES);
}
private ApiInfo apiDetails() {
return new ApiInfoBuilder()
.title("Bank API")
.description("Sample API for Bank Application")
.termsOfServiceUrl("Free to use")
.version("1.0")
.contact(new Contact("John Dooe", "http://wwww.example.com", "rosen.hristov#example.com"))
.license("API License")
.licenseUrl("http://wwww.example.com")
.build();
}
}
My Gradle dependencies:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.data:spring-data-rest-hal-explorer'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework:spring-core'
implementation 'javax.validation:validation-api'
implementation 'org.projectlombok:lombok'
implementation 'io.springfox:springfox-boot-starter:3.0.0'
implementation 'io.springfox:springfox-bean-validators:3.0.0'
implementation 'io.springfox:springfox-swagger-ui:3.0.0'
implementation 'org.springframework.plugin:spring-plugin-core:2.0.0.RELEASE'
implementation 'javax.activation:activation:1.1.1'
implementation 'javax.xml.bind:jaxb-api:2.3.1'
implementation 'org.glassfish.jaxb:jaxb-core:3.0.0-M5'
implementation 'javax.xml:jaxb-impl:2.1'
implementation 'org.springframework:spring-context'
implementation 'org.springframework.boot:spring-boot-devtools'
implementation "com.oracle.database.jdbc:ojdbc8-production:19.7.0.0",
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
annotationProcessor 'org.projectlombok:lombok'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
Here are screenshots from Swagger:
Bank Accounts Controller endpoints
Clients Controller endpoints
Employees Controller endpoints
Transactions Controller endpoints
Have you ever come upon such an issue and could someone help me, please?
Actually, I just solved the problem. I renamed path variables {id} in all endpoints with such a variable to {<entity>Id}, e.g. {clientId}, {employeeId}, etc. When the names are different for the respective entity Swagger handles everything successfully.
Interestingly, this problem existed just for GET (getById) and DELETE endpoints. The PUT endpoint was visualized successfully in all controllers although it also used an {id} path variable in all controllers.

Spring Boot #Valid doesn't display message from #NotBlank

Why message from #NotBlank is not displayed?
Controller API:
#PostMapping("/create-folder")
public SuccessResponse createFolder(Principal principal, #Valid #RequestBody CreateFolderRequest request) {
return historyService.createFolder(principal.getName(), request.getFolderName());
}
Request Body:
#Data
public class CreateFolderRequest {
#NotBlank(message = "Folder name is mandatory.")
private String folderName;
}
JSON Response:
{
"timestamp": "2020-11-18T11:24:19.769+00:00",
"status": 400,
"error": "Bad Request",
"message": "Validation failed for object='createFolderRequest'. Error count: 1",
"path": "/api/history/create-folder"
}
Packages:
Valid:
import javax.validation.Valid;
NotBlank:
import javax.validation.constraints.NotBlank;
There is no global exception handlers in the project.
#Valid throw an exception of MethodArgumentNotValidException you massege in #NotBlank is get throw inside exception detail which isn't return to the customer. you need to extract the messages so try adding this method to the controller.
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(MethodArgumentNotValidException.class)
public Map<String, String> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return errors;
}
above code read all errors inside the exception then get thier detail (filedName - errorMessage) and put them in list and then return the list to the clinet with 400 bad request status

I am not getting error message when validation fails in spring boot hibernate validation

I have created a simple Spring Boot application and I want to apply hibernate validation. But I don't get errors message. I get only below message if validation fails
{
"timestamp": "2020-06-11T09:51:55.695+00:00",
"status": 400,
"error": "Bad Request",
"message": "",
"path": "/users/"
}
my domain code is below
#NotNull
#Size(min = 8, max = 16)
private String password;
my controller
#PostMapping(consumes = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = {
MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<UserRest> createUser(#Valid #RequestBody UserDetailsRequestModel userDetails) {
UserRest userRest = new UserRest();
userRest.setFirstName(userDetails.getFirstName());
userRest.setLastName(userDetails.getLastName());
userRest.setEmail(userDetails.getEmail());
return new ResponseEntity<UserRest>(userRest, HttpStatus.OK);
}

How to wrap Path Not Found Exception in Spring Boot Rest using ExceptionHandler?

I am using Spring Boot and Spring Rest Example. In this example, I am passing custom header, if that value is valid, endpoint gets called successfully, if custom header value is not correct then I get below response, which I want to wrap into show it to the enduser using #ControllerAdvice ExceptionHandler.
Note: I went through Spring mvc - How to map all wrong request mapping to a single method, but here in my case I am taking decision based on CustomHeader.
{
"timestamp": "2020-01-28T13:47:16.201+0000",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/employee-data/employee-codes"
}
Controller
#Operation(summary = "Find Employee")
#ApiResponses(value = { #ApiResponse(code = 200, message = "SUCCESS"),
#ApiResponse(code = 500, message = "Internal Server Error") })
#Parameter(in = ParameterIn.HEADER, description = "X-Accept-Version", name = "X-Accept-Version",
content = #Content(schema = #Schema(type = "string", defaultValue = "v1",
allowableValues = {HeaderConst.V1}, implementation = Country.class)))
#GetMapping(value = "/employees/employee-codes", headers = "X-Accept-Version=v1")
public ResponseEntity<Employees> findEmployees(
#RequestParam(required = false) String employeeCd,
#RequestParam(required = false) String firstName,
#RequestParam(required = false) Integer lastName) {
Employees response = employeeService.getEmployees(employeeCd, firstName, lastName);
return new ResponseEntity<>(response, HttpStatus.OK);
}
I've implemented HttpMessageNotReadableException and HttpMediaTypeNotSupportedException and NoHandlerFoundException, but still not able to wrap this error.
Any suggestions?
I was able to find the solution for it.
# Whether a "NoHandlerFoundException" should be thrown if no Handler was found to process a request.
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
Error Handling Code:
#Override
protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
// custom logic here
return handleExceptionInternal(ex, error, getHeaders(), HttpStatus.BAD_REQUEST, request);
}
If you're using #ControllerAdvice,
do this:
#ControllerAdvice
public class RestResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler {
#ExceptionHandler(value
= { IllegalArgumentException.class, IllegalStateException.class })
protected ResponseEntity<Object> handleConflict(
RuntimeException ex, WebRequest request) {
String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse,
new HttpHeaders(), HttpStatus.CONFLICT, request);
}
}

useDefaultResponseMessages(false) not working

I'm using the swagger-codegen to create a spring-server.
I also used the .useDefaultResponseMessages(false)-attribute
as described in Swagger - Springfox always generates some response messages (401,403...) by default. How can I remove them?
SwaggerConfig.java:
public Docket customImplementation() {
return new Docket(DocumentationType.SWAGGER_2)
.useDefaultResponseMessages(false)
.select()
.apis(RequestHandlerSelectors.basePackage("myrest.api"))
.build()
.directModelSubstitute(org.joda.time.LocalDate.class, java.sql.Date.class)
.directModelSubstitute(org.joda.time.DateTime.class, java.util.Date.class)
.apiInfo(apiInfo());}
related apipart: Api.java:
#ApiOperation(value = "", notes = "Returns all clouds from the system that the user has access to ", response = Cloud.class, responseContainer = "List", tags = {
"cloud",})
#ApiResponses(value = {
#ApiResponse(code = 200, message = "All clouds ", response = Cloud.class),
/*#ApiResponse(code = 401, message = "Authorization for this action is missing", response = Error.class),
#ApiResponse(code = 403, message = "Forbidden action", response = Error.class),
#ApiResponse(code = 500, message = "An unexpected Error occured", response = Error.class),*/
#ApiResponse(code = 504, message = "Server temporary not available", response = Error.class)})
#RequestMapping(value = "/clouds",
produces = {"application/json"},
method = RequestMethod.GET)
ResponseEntity<List<Cloud>> findClouds();
But the swagger-ui still looks like:
swagger-ui: ResponseMessageTable
So it seems .useDefaultResponseMessages(false) is not working.
How do I disable these default error responses?
#John Duskin
I changes the Docketinitialization,changed the #Controller to #Restcontroller but I still get the 404 Message by Get
different looking 404-Message
The generated Serverstubs from Swagger-Codegen looks like:
Api.java:
#Api(value = "clouds", description = "the clouds API")
public interface CloudsApi {
#ApiOperation(value = "", notes = "Returns all clouds from the system that the user has access to ", response = Cloud.class, responseContainer = "List", tags={ "cloud", })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "All clouds ", response = Cloud.class),
#ApiResponse(code = 401, message = "Authorization for this action is missing", response = Cloud.class),
#ApiResponse(code = 403, message = "Forbidden action", response = Cloud.class),
#ApiResponse(code = 500, message = "An unexpected Error occured", response = Cloud.class),
#ApiResponse(code = 504, message = "Server temporary not available", response = Cloud.class) })
#RequestMapping(value = "/clouds",
produces = { "application/json" },
method = RequestMethod.GET)
ResponseEntity<List<Cloud>> findClouds();
and the separated Controller:
#RestController
public class CloudsApiController implements CloudsApi {
#Autowired
private UserService userService;
#Autowired
private CloudService cloudService;
public ResponseEntity<List<Cloud>> findClouds() {
//do some magic
return new ResponseEntity<List<Cloud>>(cloudList, HttpStatus.OK);
}
[...]
}
Try to put the call to useDefaultResponseMessages after the build method in the Docket. I've updated your code to show what I mean.
Hope that helps.
public Docket customImplementation() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("myrest.api"))
.build()
.useDefaultResponseMessages(false)
.directModelSubstitute(org.joda.time.LocalDate.class, java.sql.Date.class)
.directModelSubstitute(org.joda.time.DateTime.class, java.util.Date.class)
.apiInfo(apiInfo());
}
To get the calls working on my machine I've put the ApiReponses with the Controller
#RestController
#RequestMapping("/my_model")
#ApiResponses(value = { #ApiResponse(code = 200, message = "OK"),
#ApiResponse(code = 500, message = "Rocks fell, everyone died.") })
Update
Are you putting the description on the method?
Here is what I have working on my project. Note the API Responses are attached to the controller.
#RestController
#RequestMapping("/my_model/gogo")
#ApiResponses(value = { #ApiResponse(code = 200, message = "OK"),
#ApiResponse(code = 500, message = "Rocks Fall") })
public class GoGoClass {
#RequestMapping(method = RequestMethod.POST)
#ApiOperation(value = "Description")
public void run(
#ApiParam(value = "Param1 description") #RequestParam(required = true) final String param1,
#ApiParam(value = "Param 2 description") final String param2)
throws ModelException {
// Do stuff
}
}
fixed the problem beside working ...
this was a Spring Annotation Problem.
In my SwaggerDocumentationConfig.java I added #EnableSwagger2 and everything works as wanted
#Configuration
#EnableSwagger2
public class SwaggerDocumentationConfig {
#Bean
public Docket customImplementation(){
return new Docket(DocumentationType.SWAGGER_2)
.useDefaultResponseMessages(false)
.select()
.apis(RequestHandlerSelectors.basePackage("myrest.api"))
.build()
.directModelSubstitute(org.joda.time.LocalDate.class, java.sql.Date.class)
.directModelSubstitute(org.joda.time.DateTime.class, java.util.Date.class)
.apiInfo(apiInfo());
}
}

Resources