【发布时间】:2020-12-02 18:36:27
【问题描述】:
我正在使用 Spring Boot 2.4.0 和 Gradle 6.7 开发银行应用程序,我想使用 Swagger 3.0.0 记录和测试我的 API。我现在有四个控制器:EmployeeController, ClientController, BankAccountController, TransactionController。每个控制器有 5 个端点:
GET /{entities}
GET /{entities}/{id}
POST /{entities}
PUT /{entities}/{id}
删除 /{entities}/{id}
这里使用{entities} 作为特定控制器负责的实体的占位符:分别为employees, clients, accounts, transactions。
问题是 Swagger 仅显示所有 5 个端点 BankAccountsController。对于其他控制器,仅显示 GET /{entities}, POST /{entities}, PUT /{entities}/{id}。端点GET /{entities}/{id}, DELETE /{entities}/{id} 缺失,尽管所有控制器都是类比实现的。我在 Postman 中测试了端点,它们工作正常。
这是工作 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));
}
}
以下是其他 3 个控制器:
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));
}
}
客户端控制器:
@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));
}
}
和 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));
}
}
我检查了一切是否真的是类比的,但我找不到解决方案,也在谷歌中搜索过,但发现的问题大多是 Swagger UI 无法正常工作,而不是部分缺失的端点。
这是我的 Swagger 配置类:
@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();
}
}
我的 Gradle 依赖项:
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'
}
}
以下是 Swagger 的屏幕截图:
Bank Accounts Controller endpoints
Employees Controller endpoints
Transactions Controller endpoints
你有没有遇到过这样的问题,有人可以帮助我吗?
【问题讨论】:
-
这是正确的吗? com.example.
bank.controller.apis(RequestHandlerSelectors.basePackage("com.example.bank.controller")) -
是的。所有控制器都在这个包中。