Spring Boot Swagger выдает «Не удалось загрузить определение API. Статус ответа 500 /v3/api-docs» после добавления обрабJAVA

Программисты JAVA общаются здесь
Ответить
Anonymous
 Spring Boot Swagger выдает «Не удалось загрузить определение API. Статус ответа 500 /v3/api-docs» после добавления обраб

Сообщение Anonymous »

Я впервые задаю здесь вопрос, поэтому, пожалуйста, будьте терпеливы, если я что-то пропущу.
Я создал простой банковский проект Spring Boot со следующими API-интерфейсами REST:

Код: Выделить всё

/create
/getAccount
/deposit
/withdraw
/deleteAccount
Все работает отлично:
  • API работают правильно и обновляют базу данных, как и ожидалось.
  • Пользовательский интерфейс Swagger показывает все конечные точки как задумано.
  • Тесты Postman также работают без проблем.
Однако, когда я добавил пользовательскую обработку исключений с помощью @ControllerAdvice, Swagger начал выдавать эту ошибку:
Не удалось загрузить определение API. Статус ответа — 500 /v3/api-docs
И вот что странно: когда я удаляю классы исключений, Swagger снова начинает работать! Итак, очевидно, что в настройке обработки исключений есть что-то, вызывающее эту проблему.
Может ли кто-нибудь помочь мне понять, что происходит не так?
Класс ErrorResponse:

Код: Выделить всё

package com.bank.bankingApplication.exceptions;

import java.time.LocalDateTime;

public class ErrorResponse {
private String message;
private String timestamp;
private String path;

public ErrorResponse(String message, String path) {
this.message = message;
this.timestamp = LocalDateTime.now().toString();
this.path = path;
}

// Getters and Setters

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public String getTimestamp() {
return timestamp;
}

public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}

public String getPath() {
return path;
}

public void setPath(String path) {
this.path = path;
}
}

Класс GlobalExceptionHandler:

Код: Выделить всё

package com.bank.bankingApplication.exceptions;

import com.bank.bankingApplication.controller.BankController;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

@ControllerAdvice
@RestControllerAdvice(assignableTypes = {BankController.class})
public class GlobalExceptionHandler {

//    @ExceptionHandler(Exception.class)
//    public ResponseEntity handleAllExceptions(Exception ex, HttpServletRequest request) {
//        ErrorResponse error = new ErrorResponse(ex.getMessage(), request.getRequestURI());
//        return new ResponseEntity(error, HttpStatus.INTERNAL_SERVER_ERROR);
//    }

@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity handleTypeMismatch(MethodArgumentTypeMismatchException ex, HttpServletRequest request) {
String msg = "Invalid type for parameter: " + ex.getName();
ErrorResponse error = new ErrorResponse(msg, request.getRequestURI());
return new ResponseEntity(error, HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity  handleIllegalArgs(IllegalArgumentException ex, HttpServletRequest request) {
ErrorResponse error = new ErrorResponse(ex.getMessage(), request.getRequestURI());
return new ResponseEntity(error, HttpStatus.BAD_REQUEST);
}

// You can add more specific ones like @ExceptionHandler(AccountNotFoundException.class) here.
}

Контроллер:

Код: Выделить всё

package com.bank.bankingApplication.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import com.bank.bankingApplication.model.Account;
import com.bank.bankingApplication.repository.AccountRepository;
import com.bank.bankingApplication.service.AccountService;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;

@RestController
@RequestMapping("/bank")
@Tag(name = "Bank Controller", description = "Handles operations related to bank accounts")
public class BankController {
@Autowired
private AccountService accountService;

@Autowired
private AccountRepository accountRepository;

@Operation(
summary = "Create a new bank account",
description = "This endpoint is used to create a new bank account for a user."
)
@PostMapping("/create")
public Account createAccount(@RequestBody Account account){
return accountService.createAccount(account);
}

@Operation(
summary = "Fetches the existing bank account",
description = "This endpoint is used to fetch an existing bank account for a user."
)
@PostMapping("/getAccount")
public ResponseEntity getAccountDetails(@RequestBody Account account) {
Long id = account.getId();
Optional accountOpt = accountRepository.findById(id);
if (accountOpt.isPresent()) {
return ResponseEntity.ok(accountOpt.get());
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Account not found");
}
}

@Operation(
summary = "Deposits the amount",
description = "This endpoint is used to deposit amount for a user."
)
@PostMapping("/deposit")
public ResponseEntity deposit(@RequestBody DepositAndWithdrawRequest request) {
Optional optionalAccount = accountRepository.findById(request.getId());
if (optionalAccount.isPresent()) {
Account account = optionalAccount.get();
account.setBalance(account.getBalance() + request.getAmount());
accountRepository.save(account);
DepositAndWithdrawResponse response = new DepositAndWithdrawResponse();
response.setId(account.getId());
response.setAccountHolder(account.getAccountHolder());
response.setBalance(account.getBalance());
response.setRemarks("Deposit successful.  New balance: " + account.getBalance());
return ResponseEntity.ok(response);
} else {
return ResponseEntity.notFound().build();
}
}

@Operation(
summary = "withdraws the amount",
description = "This endpoint is used to withdraw amount for a user."
)
@PostMapping("/withdraw")
public ResponseEntity withdraw(@RequestBody DepositAndWithdrawRequest request) {
Optional optionalAccount = accountRepository.findById(request.getId());
if (optionalAccount.isPresent()) {
Account account = optionalAccount.get();
if(account.getBalance() >= request.getAmount()){
account.setBalance(account.getBalance() - request.getAmount());
accountRepository.save(account);
DepositAndWithdrawResponse response = new DepositAndWithdrawResponse();
response.setId(account.getId());
response.setAccountHolder(account.getAccountHolder());
response.setBalance(account.getBalance());
response.setRemarks("Withdraw successful.  New balance: " + account.getBalance());
return ResponseEntity.ok(response);
}
else{
account.setBalance(account.getBalance());
accountRepository.save(account);
DepositAndWithdrawResponse response = new DepositAndWithdrawResponse();
response.setId(account.getId());
response.setAccountHolder(account.getAccountHolder());
response.setBalance(account.getBalance());
response.setRemarks("Withdraw unsuccessful because entered amount was greater than the current balance: " + account.getBalance());
return ResponseEntity.ok(response);
}

} else {
return ResponseEntity.notFound().build();
}
}

@DeleteMapping("/deleteAccount")
@Operation(
summary = "Deletes an existing bank account",
description = "This endpoint is used to delete an existing bank account for a user."
)
public ResponseEntity deleteAccountById(@RequestBody Map request) {
Long id = request.get("id");
Optional account = accountRepository.findById(id);
if (account.isPresent()) {
accountRepository.deleteById(id);
return ResponseEntity.ok("Account with ID " + id + " has been deleted successfully.");
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Account with ID " + id + " not found.");
}
}

}

application.properties:

Код: Выделить всё

spring.datasource.url=jdbc:mysql://localhost:3306/bank
spring.datasource.username=root
spring.datasource.password=MyPassword
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
pom.xml:

Код: Выделить всё

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0

org.springframework.boot
spring-boot-starter-parent
3.4.4
  

com.bank
bankingApplication
0.0.1-SNAPSHOT
bankingApplication
Demo project for Spring Boot














21



org.springframework.boot
spring-boot-starter-data-jpa


org.springframework.boot
spring-boot-starter-web



org.springframework.boot
spring-boot-devtools
runtime
true


com.mysql
mysql-connector-j
runtime


org.springframework.boot
spring-boot-starter-test
test


org.springdoc
springdoc-openapi-starter-webmvc-ui
2.3.0






org.springframework.boot
spring-boot-maven-plugin






Я добавил @ControllerAdvice со специальными классами исключений для возврата структурированных ответов об ошибках. Я ожидал, что Swagger продолжит работать так же, как и раньше, но вместо этого я получил ошибку 500 в /v3/api-docs. Удаление обработчика исключений исправляет Swagger, поэтому я считаю, что что-то не так внутри уровня обработки исключений. Для устранения неполадок я закомментировал обобщенный @ExceptionHandler(Exception.class) и оставил только конкретные, такие как MethodArgumentTypeMismatchException и IllegalArgumentException, предполагая, что Swagger может полагаться на некоторую встроенную обработку исключений. Но результат остался тот же — Swagger не загружается с ошибкой 500, а Postman работает нормально.


Подробнее здесь: https://stackoverflow.com/questions/795 ... atus-is-50
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «JAVA»