Почему мое пользовательское исключение не возвращается в формате JSON?

#java #reactjs #spring

#java #reactjs #весна

Вопрос:

Я подключил приложения Spring к интерфейсу React, где мне нужно отобразить мои пользовательские исключения. Пользовательские исключения отлично работают в Spring, но интерфейс (React) получает только код ошибки (417) и ничего больше.

Я определил, что проблема заключается в том, что исключение не возвращается в формате JSON, потому что сообщение об ошибке отображается полностью, когда я использую Postman, но не в формате JSON.

Мое исследование показало, что, поскольку я использую @RestController (для моего основного контроллера) и @ControllerAdvice (для моего пользовательского обработчика исключений), он должен возвращаться в формате JSON. Я также попытался добавить компонент ResponseBody к конкретной функции, но это тоже не помогло.

Controller.java

 package com.chess.controller;

import com.chess.board.*;
import com.chess.gameflow.Game;
import com.chess.gameflow.Move;
import com.chess.gameflow.Player;
import com.chess.gameflow.Status;
import com.chess.models.requests.BoardRequest;
import com.chess.models.requests.PlayerRequest;
import com.chess.models.requests.StatusRequest;
import com.chess.models.responses.MovesResponse;
import com.chess.models.responses.Response;
import com.chess.models.responses.StatusResponse;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@CrossOrigin(origins= "http://localhost:3000", maxAge=7200)
@RestController
@RequestMapping("/game")
public class Controller {
    Game game;
    Board board = Board.boardConstructor();


    @PostMapping("/players")
    public List<Response> createPlayer(@RequestBody PlayerRequest request){
        game = new Game(request.getName1(), request.getName2());
        List<Response> returnValue = board.returnBoard();
        Player player1= Game.players[0];
        StatusResponse status = new StatusResponse(Status.isActive(), Status.isCheck(), player1);
        returnValue.add(status);
        return returnValue;
    }

    @PostMapping
    public List<Response> makeMove(@RequestBody BoardRequest boardRequest){        
        StatusResponse status = Game.run(boardRequest);
        List<Response> returnValue = board.returnBoard();
        returnValue.add(status);
        return returnValue;
    }

    @PostMapping("/end")
    public StatusResponse endGame(@RequestBody StatusRequest statusRequest){
        Status.setActive(false);
        Board board = Board.boardConstructor();
        board.generateBoard();
        if (statusRequest.isForfeit()){
            StatusResponse statusResponse = new StatusResponse(statusRequest.getPlayerName()   " declares defeat! Game Over!");
            return statusResponse;
        }
        StatusResponse statusResponse = new StatusResponse("We have a draw! Good Game!");
        return statusResponse;
    }

    @GetMapping("/moves")
    public MovesResponse displayMoves(){
        MovesResponse movesResponse = new MovesResponse(Move.returnMoveMessages());
        return movesResponse;
    }
}
  

CustomExceptionsHandler.java

 package com.chess.exceptions;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@ControllerAdvice
public class CustomExceptionsHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(value = InvalidMoveException.class)
    @ResponseBody //this line shouldn't be necessary as I am using a RestController but I added it anyways in one of my futile attempts and I don't think it should hurt
    protected ResponseEntity<Object> resolveInvalidMove(InvalidMoveException e, WebRequest req) throws Exception {   
        ErrorResponse errorResponse = new ErrorResponse(HttpStatus.EXPECTATION_FAILED.value(),
                HttpStatus.EXPECTATION_FAILED.getReasonPhrase(),
                e.getMessage(),
                req.getDescription(true));
        return handleExceptionInternal(e, errorResponse.toString(), new HttpHeaders(), HttpStatus.EXPECTATION_FAILED, req);


    @ExceptionHandler(value = MustDefeatCheckException.class)
    protected ResponseEntity<Object> resolveCheckStatus(MustDefeatCheckException e, WebRequest req){
        ErrorResponse errorResponse = new ErrorResponse(HttpStatus.EXPECTATION_FAILED.value(),
                HttpStatus.EXPECTATION_FAILED.getReasonPhrase(),
                e.getMessage(),
                req.getDescription(true));
        return handleExceptionInternal(e, errorResponse, new HttpHeaders(), HttpStatus.EXPECTATION_FAILED, req);
    }
}
  

ErrorResponse.java

 package com.chess.exceptions;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "error")
public class ErrorResponse {
    private int status;
    private String errReason;
    private String errMessage;
    private String path;

    public ErrorResponse(int status, String errReason, String errMessage, String path) {
        this.status = status;
        this.errReason = errReason;
        this.errMessage = errMessage;
        this.path = path;
    }

    @Override
    public String toString(){
        return "Status Code: "    status   " "   errReason   " Message: "   errMessage   " at "   path;
    }
}
  

InvalidMoveException.java

 package com.chess.exceptions;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.EXPECTATION_FAILED, reason="Invalid Move")
public class InvalidMoveException extends RuntimeException {
    public InvalidMoveException(String msg){ super(msg); }
}
  

Ответ №1:

Я решил половину проблемы. Вместо того, чтобы возвращать ResponseEntity с помощью handleExceptionInternal , я смог вернуть сам ErrorResponse, сделав его дочерним элементом Response, который является отцом всех моих обычных ответов.

Итак, теперь мой CustomExceptionsHandler.java выглядит так-

 @RestControllerAdvice
public class CustomExceptionsHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(value = InvalidMoveException.class)
    @ResponseStatus(HttpStatus.EXPECTATION_FAILED)
    public ErrorResponse resolveInvalidMove(InvalidMoveException e, WebRequest req) {
        ErrorResponse errorResponse = new ErrorResponse(HttpStatus.EXPECTATION_FAILED.value(),
                HttpStatus.EXPECTATION_FAILED.getReasonPhrase(),
                e.getMessage(),
                req.getDescription(true));
        return errorResponse;
    }
}
  

И я получаю исключение в формате JSON, когда я использую Postman. Однако моя ошибка в
Реакция не изменилась. Я все еще получаю только код состояния и никакой другой информации.
введите описание изображения здесь

Board.js

  DataService.makeMove(move)
            .then(res => {
                //console.log(res.data);
                setIsWhite((prev) => !prev);                
                props.setTheBoard(res.data);
                setStatus(res.data[64]);
                updateMovesList();
            })
            .catch(err => {
                console.log(err)
                console.log(err.errMessage)
                console.log(err.message)
                console.log(err.status)
                console.log(err.errReason)
            })
  

DataService.js

     makeMove(move){
        return axios.post(`${url}`, move);
    }
  

Я всегда думал, что перехватывать ошибки очень просто, но, видимо, я чего-то не хватает