【问题标题】:Spring Boot : How to pass errors through JSON response?Spring Boot:如何通过 JSON 响应传递错误?
【发布时间】:2020-02-23 13:02:45
【问题描述】:

我有一个使用 Spring Boot 制作的非常基本的 OpenWeathermap 应用程序,当找不到城市时,OpenWeathermap 返回{"cod":"404","message":"city not found"},但我无法将它作为 JSON 从后端传递到前端。我的前端回复只是Error: Request failed with status code 500

我正在以 JSON 对象的形式处理从后端到前端的响应,并在前端解析天气数据。

我的Controller 看起来像这样

@RestController
@RequestMapping("/backend")
public class Controller {

    @GetMapping("/{CITY}")
    public @ResponseBody Object getWeather(@PathVariable String CITY) {

        Object response = WeatherAPI.getCity(CITY);
        return response;
    }

而我的WeatherAPI 类看起来像这样

public class WeatherAPI {

    public static Object getCity(String CITY) {

        RestTemplate template = new RestTemplate();
        ResponseEntity<Object> response = template.getForEntity("https://api.openweathermap.org/data/2.5/weather?q=" + CITY + "&units=metric&lang=fi&appid=" + API-KEY-HERE, Object.class);
        return response;
    }
}

例如,我尝试过:https://mkyong.com/spring-boot/spring-rest-error-handling-example/ 并像这样修改了我的代码:

return response.orElseThrow(() -&gt; new NotFoundException());,但上面写着.orElseThrow is undefined for the type

如何将带有正确状态代码的“未找到城市”错误传递给我的前端?

【问题讨论】:

  • 您不应混合使用@RestController@ResponseBodyResponseEntity&lt;Object&gt;@RestController 在类中的所有 REST 端点上暗示 @ResponseBody@ResponseBodyResponseEntity&lt;Object&gt; 互斥,不能同时使用。我建议检查控制器方法是否引发了异常。如果不是,则删除 @RestController@ResponseBody 两者。

标签: java spring spring-boot


【解决方案1】:

您可以使用通用错误处理程序,例如 ControllerAdvice,它可以处理某些特定类型的异常。

考虑到这种机制,我们可以通过以下方式调整您的代码:

  1. 天气 API 的异常将在定义的控制器通知中被捕获和处理
  2. 您的服务将为此用例引发自定义异常
  3. ControllerAdvice 将知道将哪个错误代码发送回客户端。

一些最小的配置是:

  1. 在spring mvc框架中添加controllerAdvice,这样当没有找到某个城市的数据时,可以返回404代码
@ControllerAdvice
public class GlobalExceptionHandler {
    /** Provides handling for exceptions throughout this service. */
    @ExceptionHandler({ CityWeatherNotFountException.class })
    @ResponseCode(HttpStatus.NOT_FOUND)
    public final ResponseEntity<ApiError> handleException(Exception ex, WebRequest request) {
  1. 调整您的服务实现,使其符合以下要求:
public class WeatherAPI {

    public static Object getCity(String CITY) {

        try{

       RestTemplate template = new RestTemplate();
        ResponseEntity<Object> response = template.getForEntity("https://api.openweathermap.org/data/2.5/weather?q=" + CITY + "&units=metric&lang=fi&appid=" + API-KEY-HERE, Object.class);
        return response;}

     catch(RestException ex){
        throw new CityWeatherNotFountException(ex);}
    }
}

【讨论】:

    【解决方案2】:

    RestTemplate 将对任何错误响应抛出异常。您可以将 template.getForEntity 调用包围在 try-catch 块中并处理异常,以您想要的任何格式返回消息。

    您正在寻找的例外是HttpStatusCodeException

    【讨论】:

      【解决方案3】:

      实现一个通用自定义 -> RestTemplateResponseErrorHandler 来处理客户端特定和服务器特定的错误,同时调用 RestTemplate 调用。

      参考:https://www.baeldung.com/spring-rest-template-error-handling 使用 Spring boot 和 Spring 5 类似用例的示例。

      【讨论】:

        【解决方案4】:

        首先,我认为您应该尝试了解@RestController 的实现。此注解包括简化控制器实现的@Controller 和@ResponseBody 注解 (more info here)。

        其次,不能使用 orElseThrow,因为它是 Optional 类 (more info here) 下引入的方法。您给出的示例不使用任何 Optional 对象。 我认为您应该正确研究 mkyong 链接提供的示例(测试一下)。您给出的示例与 mkyong 示例相差太远。

        对于您的实际问题,您可以更改代码如下: 您可以使用 HttpServletResponse 类返回您想要的状态和错误消息。 您不需要输入@ResponseBody,因为@RestController 会自动为您完成。

            @RestController()
            @RequestMapping("/backend")
            public class Controller {
        
            @GetMapping("/{city}")
            public ResponseObject getWeather(@PathVariable String city, HttpServletResponse response) {
        
                ResponseObject response = WeatherAPI.getCity(city);
        
                if (response == null) {
                    return response.sendError(HttpStatus.PAGE_NOT_FOUND.value(), "message here");
                }
        
                return response;
            }
        

        最后,我不确定您为什么使用静态方法作为示例。您应该使用 @Service 为您的 WeatherAPI 创建一个服务层。

        【讨论】:

          【解决方案5】:

          试试这个:

          Controller 更新为:

             @RestController
             @RequestMapping("/backend")
             public class Controller {
          
                  @GetMapping("/{CITY}")
                  public ResponseEntity<Object> getWeather(@PathVariable String CITY) {
                      Object response = WeatherAPI.getCity(CITY);
                      if(response == null) {
                         return new ResponseEntity<Object>("city not found", HttpStatus.PAGE_NOT_FOUND);
                      }
                      return new ResponseEntity<Object>(response, HttpStatus.OK);
                  }
          
             }
          

          WeatherAPI 更新为:

          public class WeatherAPI {
          
              public static Object getCity(String CITY) {
                  RestTemplate template = new RestTemplate();
                  Object response = template.getForEntity("https://api.openweathermap.org/data/2.5/weather?q=" + CITY + "&units=metric&lang=fi&appid=" + API-KEY-HERE, Object.class);
                  return response;
              }
          }
          

          这应该会相应地工作。

          【讨论】:

            【解决方案6】:

            谢谢大家的回答。我真的很感激,我喜欢研究所有这些代码是如何工作的。我必须进一步深入研究 Spring Boot 文档,做一些教程,并进一步研究这个主题。我尝试了一些答案,但它们实际上不起作用。

            这就是我解决问题的方法:

                    try {
                        ResponseEntity<Object> response = template.getForEntity("https://api.openweathermap.org/data/2.5/" + QUERY
                                + "?q=" + CITY + "&units=metric&lang=fi&appid=" + API_KEY, Object.class);
                        return response;
                    } catch (HttpStatusCodeException exception) {
                        return exception.getResponseBodyAsString();
                    }
            

            声明:return exception.getResponseBodyAsString(); 将错误响应返回到我的前端,我可以用 JavaScript 解析它,保存到数组等中并呈现给用户。

            我仍然想学习如何捕获更多错误并将它们带到前端,这样我就可以构建一个“管理面板”来查看和记录后端发生的所有事情。我真的很想学习 Java 和 Spring Boot 框架。

            我在前端编写了一些 JavaScript,以防止出现空输入或搜索查询导致后端出错,因为我还不能正确处理它们。我还在后端进行了一些重定向,因此错误不会破坏任何功能。

            再次感谢大家的支持和详细的描述。

            【讨论】:

              【解决方案7】:

              使用响应实体

              @RestController
              @RequestMapping("/backend")
              public class Controller {
              
                  @GetMapping("/{CITY}")
                  public ResponseEntity<?> Object getWeather(@PathVariable String CITY) {
              
                      Object response = WeatherAPI.getCity(CITY);
                      if (response != null) {
                       return new ResponseEntity<Object>(response, HttpStatus.OK);
                      } else {
                        return new ResponseEntity<String>("City Not Found", HttpStatus.Not_Found);
                     }
                  }
              

              ResponseEntity 还将返回状态代码,例如 200 表示成功 404 表示未找到等,它将绑定到您的响应

              【讨论】:

              • 我真的很喜欢它显示响应的方式,但只有“200”通过,404 被 Spring Boot 在后端捕获并在前端给出“错误 500”。所以只有 200 人在使用这个实现,但我真的很想得到所有的错误代码,404 等:(
              • 您可以根据需要获取每个错误代码。您可以通过 OK、NOT_FOUND 等枚举(eclipse 将提供枚举建议)。除了 OK、NOT_FOUND 之外还有更多枚举,每个枚举都有单独的状态码
              猜你喜欢
              • 2014-06-09
              • 1970-01-01
              • 1970-01-01
              • 2020-04-20
              • 2015-11-09
              • 2022-02-03
              • 2018-07-13
              • 2014-01-09
              • 2021-02-20
              相关资源
              最近更新 更多