【问题标题】:Why is my custom exception not returning in JSON format?为什么我的自定义异常没有以 JSON 格式返回?
【发布时间】:2020-12-29 03:26:47
【问题描述】:

我已经将一个 Spring 应用程序连接到一个 React 前端,我需要在其中显示我的自定义异常。自定义异常在 Spring 中完美运行,但前端 (React) 只收到错误代码 (417) 而没有其他任何内容。

我已经确定问题在于异常没有以 JSON 格式返回,因为当我使用 Postman 时错误消息是完整显示的,而不是 JSON 格式。

我的研究表明,由于我使用的是@RestController(用于我的主控制器)和@ControllerAdvice(用于我的自定义异常处理程序),它应该以 JSON 格式返回。我还尝试将 ResponseBody bean 添加到特定函数,但这也无济于事。

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); }
}

【问题讨论】:

    标签: java reactjs spring


    【解决方案1】:

    我解决了一半的问题。我可以返回 ErrorResponse 本身,而不是返回带有 handleExceptionInternal 的 ResponseEntity,方法是将它设为 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;
        }
    }
    

    当我使用 Postman 时,我收到 JSON 格式的异常。但是,我的错误 反应没有改变。我仍然只得到状态码,没有其他信息。

    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);
        }
    

    我一直认为捕捉错误很简单,但显然我错过了一些东西

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-22
      • 1970-01-01
      • 2014-09-14
      • 1970-01-01
      • 2017-03-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多