【问题标题】:Swagger UI does not display some of the endpoints in some controllersSwagger UI 不显示某些控制器中的某些端点
【发布时间】: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

Clients Controller endpoints

Employees Controller endpoints

Transactions Controller endpoints

你有没有遇到过这样的问题,有人可以帮助我吗?

【问题讨论】:

  • 这是正确的吗? com.example.bank.controller .apis(RequestHandlerSelectors.basePackage("com.example.bank.controller"))
  • 是的。所有控制器都在这个包中。

标签: spring swagger springfox


【解决方案1】:

其实我刚刚解决了这个问题。我将所有端点中的路径变量{id}重命名为{&lt;entity&gt;Id},例如{clientId}{employeeId} 等。当相应实体的名称不同时,Swagger 会成功处理所有事情。 有趣的是,这个问题只存在于GET (getById)DELETE 端点。 PUT 端点已在所有控制器中成功可视化,尽管它还在所有控制器中使用了 {id} 路径变量。

【讨论】:

    猜你喜欢
    • 2015-03-27
    • 2022-06-11
    • 1970-01-01
    • 2020-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-08
    相关资源
    最近更新 更多