彻底解决SpringBoot 接口404异常响应~

发布时间:2025-05-19 00:20:34 作者:益华网络 来源:undefined 浏览量(1) 点赞(1)
摘要:来源:JAVA日知录 大家好,我是飘渺。今天,我们将继续探讨DDD与微服务架构的系列主题。 在前一篇文章 优雅远程调用,微服务中ACL与OpenFeign的绝佳配合!,一位粉丝提到在DailyMart中,关于Feign的error解码器对404错误处理不够友好,会返

来源:JAVA日知录

大家好,我是飘渺。今天,我们将继续探讨DDD与微服务架构的系列主题。

在前一篇文章 优雅远程调用,微服务中ACL与OpenFeign的绝佳配合!,一位粉丝提到在DailyMart中,关于Feign的error解码器对404错误处理不够友好,会返回空对象而不报错。

实际上,这个问题并不是Feign解码器的问题。

众所周知,在Spring Boot中,当我们访问一个不存在的接口时,会触发404异常,接口调用会返回如下JSON格式的错误消息:

{

  "timestamp""2023-09-23T03:36:50.503+00:00"

,

  "status"404

,

  "error""Not Found"

,

  "path""/api/inventory/xxxx"

}

但是在DailyMart中,这个错误消息会被全局包装类GlobalResponseBodyAdvice处理,因此出现了以下奇怪的错误信息:code码为OK表示操作正常,但data消息体却表示404异常。由于这种情况,OpenFeign的异常解码器无法正确处理。

{

    "code""OK"

,

    "message"null

,

    "data"

: {

        "timestamp""2023-09-23T03:36:50.503+00:00"

,

        "status"404

,

        "error""Not Found"

,

        "path""/api/inventory/xxxx"

    },

    "timestamp"1695440210514

}

因此,问题的关键在于解决Spring Boot中404异常处理的问题,以便能够返回正确的响应结果。

1. SpringBoot 的默认错误处理机制

Spring Boot默认为我们提供了BasicErrorController来处理全局/error请求。BasicErrorController提供两种错误响应方式,一种是针对页面请求的错误响应,另一种是针对JSON请求的错误响应。

2. 自定义错误响应

在SpringBoot中,BasicErrorController是由自动配置类ErrorMvcAutoConfiguration负责加载。

@AutoConfiguration(before = WebMvcAutoConfiguration.class

)

@ConditionalOnWebApplication(type 

= Type.SERVLET)

@ConditionalOnClass({ Servlet.classDispatcherServlet.class

 })

@EnableConfigurationProperties(
{ ServerProperties.classWebMvcProperties.class

 })

public class ErrorMvcAutoConfiguration 

{

  ...

 @Bean @ConditionalOnMissingBean(value = ErrorController.classsearch 

= SearchStrategy.CURRENT)

 public BasicErrorController basicErrorController

(ErrorAttributes errorAttributes,

   ObjectProvider<ErrorViewResolver> errorViewResolvers)
 

{

  return new BasicErrorController(errorAttributes, this

.serverProperties.getError(),

    errorViewResolvers.orderedStream().toList());

 }

}

根据上述配置,只要我们自己配置一个ErrorController,就可以覆盖掉BasicErrorController的默认行为。下面的代码示例展示了如何通过继承AbstractErrorController并重写error方法来自定义错误响应,以使其与项目整体风格一致:

@Controller@RequestMapping("${server.error.path:${error.path:/error}}"

)

public class CustomErrorController extends AbstractErrorController 

{

 ...

    @RequestMapping    public Result<Void> error(HttpServletRequest request) 

{

        return

 ResultFactory.fail(String.valueOf(status),message);

    }

    @RequestMapping

(produces = MediaType.TEXT_HTML_VALUE)

    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) 

{

        ...

    }

}

通过这种方式,当我们访问不存在的方法时,将会返回符合预期的JSON响应结果:

{

    "code""404 NOT_FOUND"

,

    "message""No message available: /api/inventory/verify"

,

    "data"null

,

    "timestamp""1696598118098"

}

3. 微服务中的解决方案

上述方案在单服务中表现良好,但在微服务体系中,每个服务都需要创建一个独立的CustomErrorController,这显然违反了DRY(Dont Repeat Yourself)原则。

在DailyMart中,我们封装了一个公共模块dailymart-web-spring-boot-starter,专门处理Web请求的逻辑,包括全局响应包装和异常处理。我们可以将CustomErrorController放置在此模块中,并在配置类中加载该Controller。

/**

 *  注册SpringBoot默认异常处理器

 */
@Beanpublic CusotmErrorController globalErrorController(ErrorAttributes errorAttributes)

{

  return new

 CusotmErrorController(errorAttributes);

}

然而,这种做法在启动服务时可能会引发以下异常:

Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map customerErrorController

 method 

com.jianzh5.dailymart.springboot.starter.web.CustomerErrorController#errorHtml(HttpServletRequest, HttpServletResponse)

to { [/error], produces [text/html]}: There is already basicErrorController

 bean method

这是因为Spring Boot本身已经有一个处理/error请求的控制器BasicErrorController,而我们又定义了一个CustomErrorController来处理/error请求。

3.1 解决方案

这个问题有两种解决方案。

第一种解决方案是确保系统在启动时能够优先加载自定义的CustomErrorController。

根据前文提到的BasicErrorController配置的条件注解,一旦定义了ErrorController就不会再加载BasicErrorController,所以只需要在自动配置类上加上@AutoConfiguration(before = ErrorMvcAutoConfiguration.class)注解,确保CustomErrorController在BasicErrorController之前加载。

第二种解决方案是在加载CustomErrorController时修改默认的BasicErrorController的拦截路径。

如前所述,BasicErrorController的拦截路径为@RequestMapping("${server.error.path:${error.path:/error}}"),如果我们定义了error.path的值,它就会优先使用。因此,只需设置error.path的值即可解决问题。

@PostConstructpublic void customizeErrorPath() 

{

  // 修改 server.error.path 的值为自定义值  System.setProperty("error.path""/deprecated/error"

);

}

小结

通过本文,我们探讨了Spring Boot中的404异常处理问题,以及如何通过自定义ErrorController来解决这一问题。特别是在微服务架构中,我们介绍了如何利用共享模块的方法来更有效地管理异常处理。希望这些实践经验对您有所帮助,提高了您在微服务开发中处理异常情况的能力。我们非常欢迎各位读者提出宝贵的意见和建议,以进一步完善这个系列文章。

二维码

扫一扫,关注我们

声明:本文由【益华网络】编辑上传发布,转载此文章须经作者同意,并请附上出处【益华网络】及本页链接。如内容、图片有任何版权问题,请联系我们进行处理。

感兴趣吗?

欢迎联系我们,我们愿意为您解答任何有关网站疑难问题!

您身边的【网站建设专家】

搜索千万次不如咨询1次

主营项目:网站建设,手机网站,响应式网站,SEO优化,小程序开发,公众号系统,软件开发等

立即咨询 15368564009
在线客服
嘿,我来帮您!