springboot转发http请求_网易后端实习生分享:Springboot异常和错误处理规范

由于错误在所难免,异常处理已经成为开发工作中不可或缺的部分。

在web开发中,我们通常不希望用户看到一个写满StackTrace的错误页面;同时,我们希望出现错误或发生异常时,开发运维人员可以看到详细的错误信息,以便进行查错和DEBUG。

所以,在开发过程中,应重视异常处理。在进行业务逻辑开发之前,就应该定义好自己的异常处理流程。

1. 异常处理流程概述

b2bb6712a641a793a86237f881785749.png

异常处理的对象分为两类:

  1. 错误的请求:程序处理前就已经发生,如RequestMapping中不存在该请求的URL以及其他的一些HTTP错误。
  2. 程序中的错误:各种异常,包括Runtime异常。

Springboot会统一处理第1种错误,由ErrorController捕获并进行处理,根据请求的Accpet字段返回错误页面或json数据。这里贴出Springboot自己的BasicErrorController实现:

@Controllerpublic class BasicErrorController implements ErrorController {    @Value("${error.path:/error}")    private String errorPath;    private final ErrorAttributes errorAttributes;    public BasicErrorController(ErrorAttributes errorAttributes) {        Assert.notNull(errorAttributes, "ErrorAttributes must not be null");        this.errorAttributes = errorAttributes;    }    public String getErrorPath() {        return this.errorPath;    }    @RequestMapping(        value = {"${error.path:/error}"},        produces = {"text/html"}    )    public ModelAndView errorHtml(HttpServletRequest request) {        return new ModelAndView("error", this.getErrorAttributes(request, false));    }    @RequestMapping({"${error.path:/error}"})    @ResponseBody    public ResponseEntity> error(HttpServletRequest request) {        Map body = this.getErrorAttributes(request, this.getTraceParameter(request));        HttpStatus status = this.getStatus(request);        return new ResponseEntity(body, status);    }    //……}

也可以自己写一个ErrorController返回自定义页面。

对于程序中发送的异常,可以手动进行捕获。如果没有手动捕获或有所遗漏,Springboot提供了@ExceptionHandler(value={})对某种类型的异常统一进行处理。通常,可以通过转发(forward)或重定向(redirect)方式将该异常转给自己定制的ExceptionController进行处理。ExceptionController可以根据请求类型返回错误页面或json数据。

2. 自定义RuntimeException

可以定制自己的Exception进行异常信息记录:

public abstract class BaseRuntimeException extends RuntimeException {    private static final long serialVersionUID = -1842796916322056555L;    public BaseRuntimeException() {        super();    }    public BaseRuntimeException(String message) {        super(message);    }    public BaseRuntimeException(String message, Throwable cause) {        super(message, cause);    }    /**     * 返回异常的错误码     */    public abstract Integer getErrorCode();    /**     * 返回异常的描述(不带应用前缀)     */    public abstract String getErrorMessage();    /**     * 返回异常的日志(带应用前缀)     */    public abstract String getErrorLog();}

然后再Service层定义该服务模块异常信息。

public class MiscServiceException extends BaseRuntimeException implements IReThrowException {    private static final long serialVersionUID = -1844670008823631700L;    private MiscErrorCode miscErrorCode = MiscErrorCode.SUCCESS;    private String errorLog = MiscErrorCode.SUCCESS.getDesc();    public MiscServiceException() {        super();    }    public MiscServiceException(String message, Throwable cause) {        super(message, cause);        this.errorLog = message;    }    public MiscServiceException(String message) {        super(message);        this.errorLog = message;    }    public MiscServiceException(String message, MiscErrorCode miscErrorCode) {        super(message);        this.miscErrorCode = miscErrorCode;        this.errorLog = message;    }    /**     * 返回异常的错误码.
* 方便日志查看追踪. * * @see BaseRuntimeException#getErrorCode() */ @Override public Integer getErrorCode() { return miscErrorCode.getValue(); } /** * 返回异常的描述.
* 可直接用于前端错误提示. * * @see BaseRuntimeException#getErrorMessage() */ @Override public String getErrorMessage() { return miscErrorCode.getDesc(); } /** * 返回异常的日志.
* 用于服务器日志打印. * * @see BaseRuntimeException#getErrorLog() */ @Override public String getErrorLog() { return errorLog; }}

在Service层生成异常对象并抛出。

if (…)    throw new MiscServiceException(“log message…”, MiscErrorCode.XXXERRIR);

在Manager层继续向上抛出。

public interface SomeService {    DTO someFunc() throws MiscServiceException {    //…}

在Controller层捕获异常,进行处理——返回相关页面。

try {//…} catch (MiscServiceException e) {log.error(e.getErrorLog());return ResponseView.fail(e.getErrorCode(), e.getErrorMessage());}

如此以来,代码中定义的异常和错误均可以捕捉。

由于BaseRuntimeException是一种RuntimeException,Mananger层声明方法是不加throws Exception也可以通过编译。小猿建议每一个Manager的方法都加上throws Exception声明。另外,BaseRuntimeException实际上也可以直接继承Exception,这样编译器会强制要求对其进行异常进行处理。

3. @ExceptionHandler

上述方案解决了一部分自定义异常。对于其他的自己未定义的Runtime Exception,例如Null Pointer Exception,Springboot提供了ExceptionHandler,用于捕获所有代码中没有主动catch的异常。通常,我们该异常转发(forward)或重定向(redirect)至某个自定义的Controller进行处理。

转发和重定向的效果不同:在浏览器端,转发的请求,页面不会跳转,URL不会改变;重定向的请求,URL会改变。重定向实际上是浏览器进行了两次请求。对于参数传递,两种方式采取的方法也不同。重定向方式可以使用RedirectAttributes传递参数,转发方式则将参数放置在Request的Attributes中。

转发:

@ControllerAdvicepublic class CustomExceptionHandler {    /**     * 将异常页转发到错误页     */    @ExceptionHandler(Exception.class)    public ModelAndView handleError(HttpServletRequest req, Exception ex) {        log.info("Exception e : " + ex, ex);        if (BaseRuntimeException.class.isAssignableFrom(ex.getClass())) {            BaseRuntimeException e = (BaseRuntimeException) ex;            req.setAttribute("code", e.getErrorCode());            req.setAttribute("message", e.getErrorMessage());        } else {            req.setAttribute("code", FrontCode.OTHER_ERROR.getCode());            req.setAttribute("message", FrontCode.OTHER_ERROR.getDesc());        }        return new ModelAndView("forward:/exception");    }}

重定向:

/** * 将异常页使用重定向方式到错误页 * * @param req * @param ex * @param mode * @return */@ExceptionHandler(Exception.class) public ModelAndView handleError(HttpServletRequest req, Exception ex ,RedirectAttributes mode) {     log.info("Exception e : " + ex,ex);     ModelAndView mav = new ModelAndView();     if (BaseRuntimeException.class.isAssignableFrom(ex.getClass())) {     BaseRuntimeException e = (BaseRuntimeException) ex;     mode.addAttribute("code",e.getErrorCode());     mode.addAttribute("message",e.getErrorMessage());     }     else {     mode.addAttribute("code", FrontCode.OTHER_ERROR.getCode());     mode.addAttribute("message", FrontCode.OTHER_ERROR.getDesc());     }     return new ModelAndView("redirect:/exception");}

4. ErrorController

在下文贴出的示例中,我们将异常处理的Controller也放在ErrorController中。其中,errorHtml方法同于对没有经过Controller层的错误进行处理,返回自定义错误页;exception和exceptionHtml方法负责接收ExceptionHandler转发或重定向的异常处理流,根据produces的类型是”json/application”还是“text/html”,分别返回json和错误页面。

@Controllerpublic class CommonErrorController implements ErrorController {    @Autowired    private UserSecurityHelper userSecurityHelper;    private static final String ERROR_PATH = "error";    private static final String EXCEPTION_PATH = "exception";    @RequestMapping(value = ERROR_PATH)    public ModelAndView errorHtml(HttpServletRequest request) {        ModelAndView modelAndView = new ModelAndView("/error/error");        Object statusCode = request.getAttribute("javax.servlet.error.status_code");        //当请求的错误类型非404、403、402、401时,返回500的错误页面        if (statusCode == null                || (!statusCode.equals(HttpStatus.NOT_FOUND.value())                && !statusCode.equals(HttpStatus.UNAUTHORIZED.value())                && !statusCode.equals(HttpStatus.PAYMENT_REQUIRED.value()) && !statusCode                .equals(HttpStatus.FORBIDDEN.value()))) {            statusCode = HttpStatus.INTERNAL_SERVER_ERROR.value();        }        modelAndView.addObject("code", statusCode);        modelAndView.addObject("message", "你很神,找到了不存在的页面。");        return modelAndView;    }    /*    * 使用forward转发.    */    @RequestMapping(value = EXCEPTION_PATH, produces = "application/json")    @ResponseBody    public ResponseEntity exception(HttpServletRequest request) {        Integer code = (Integer) request.getAttribute("code");        String message = (String) request.getAttribute("message");        return ResponseView.fail(code, message);    }    @RequestMapping(value = EXCEPTION_PATH, produces = {"text/html"})    public ModelAndView exceptionHtml(HttpServletRequest request) {        EduWebUser eduWebUser = userSecurityHelper.getEduWebUser();        ModelAndView mav = new ModelAndView("/error/error");        mav.addObject("code", (Integer) request.getAttribute("code"));        mav.addObject("message", (String) request.getAttribute("message"));        mav.addObject("webUser", eduWebUser);        return mav;    }    @Override    public String getErrorPath() {        return ERROR_PATH;    }}

如果使用Redirect跳转,ErrorController中接收参数的相应代码要随之改变。

/*使用Redirect跳转*/@RequestMapping(value = EXCEPTION_PATH, produces = "application/json")@ResponseBodypublic ResponseEntity exception(HttpServletRequest request , @RequestParam(required = false) String message,                                @RequestParam(required = false) Integer code) {    Map body = new HashMap<>(4);    body.put("code", code);    body.put("message", message);    return new ResponseEntity(body, HttpStatus.OK);}@RequestMapping(value = EXCEPTION_PATH, produces = { "text/html"})public ModelAndView exceptionHtml(HttpServletRequest request , @RequestParam(required = false) String message,                                  @RequestParam(required = false) Integer code) {    ModelAndView mav = new ModelAndView("/error/error");    EduWebUser eduWebUser = userSecurityHelper.getEduWebUser();    mav.addObject("code", code);    mav.addObject("message", message);    mav.addObject("webUser", eduWebUser);    return mav;}

5. 测试

我们定义了专门用于测试的Service和Controller。其中,throw测试程序中代码捕获异常,silent测试由ExceptionHandler捕获的异常。

public interface ExceptionService {    public void testThrowException() throws MiscServiceException;    public void testSilentException();}@Service("exceptionService")public class ExceptionServiceImpl implements ExceptionService {    @Override    public void testThrowException() throws MiscServiceException {        throw new ForumException("Log Message");    }    @Override    public void testSilentException() {        throw new ForumException("Log Message");    }}@RestController@RequestMapping("/exception")public class ExceptionController {    @Resource    private ExceptionService exceptionService;    @RequestMapping("/throw")    public ResponseEntity testThrow() {        try {            exceptionService.testThrowException();            return ResponseView.success(null);        } catch (MiscServiceException e) {            e.printStackTrace();            return ResponseView.fail(e.getErrorCode(), e.getErrorMessage());        }    }    @RequestMapping("/silent")    public ResponseEntity testSilent() {        exceptionService.testSilentException();        return ResponseView.success(null);    }}

测试记录如下。

代码主动捕获异常:

9e84a425523ad0c229062d0f1293ac4a.png

Springboot通过forward方式统一捕获异常:

c5f27558632c68b0197ff38fda2edebe.png

Springboot通过redirect方式统一捕获异常:

eca52291060afb5fa27055c8061aaeb7.png

Springboot统一捕获异常,返回json:

38001ac989c8d506d843db846a9f025a.png

对于Controller之前发生的错误和异常,返回自定义页面:

b0cce0bcf4ccff08fd35f50bd680e9fe.png

如果对我的更新内容很感兴趣,希望可以得到您的一个点赞和关注,这将是对我最大的鼓励和支持,感谢~不论多与少,我都会坚持更新。另外,私聊我【Java学习资料】可以收获互联网大厂Java面经和Java最新最全资料~帮助你早日拿到互联网大厂的offer~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/502558.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springmvc 拦截器_Spring MVC拦截器学习

1 介绍Spring Web MVC是基于Servlet API构建的原始Web框架。2 拦截器2.1 定义springmvc框架的一种拦截机制2.2 使用2.2.1 两步走实现HandlerInterceptor接口注册&#xff08;xml或者注解 &#xff09;2.2.2 HandlerInterceptor接口实现HandlerInterceptor接口import org.spring…

java文件读入原理_描述一下JVM加载class文件的原理机制

1、JVM 简介JVM 是我们Javaer 的最基本功底了&#xff0c;刚开始学Java 的时候&#xff0c;一般都是从“Hello World ”开始的&#xff0c;然后会写个复杂点class &#xff0c;然后再找一些开源框架&#xff0c;比如Spring &#xff0c;Hibernate 等等&#xff0c;再然后就开发…

微信流媒体直播java_微信小程序直播带货教程

微信小程序直播带货教程微信小程序正式推出直播带货组件&#xff0c;很多正在使用 “api工厂” 的用户&#xff0c;这几天一直在问&#xff0c;现有小程序如何使用直播带货的功能&#xff0c;需要不需要做很复杂的改动&#xff1f;是否需要重新开发小程序&#xff1f;使用门槛高…

vue和java实现页面增删改_SpringBoot-Vue实现增删改查及分页小DEMO

前言主要通过后端 Spring Boot 技术和前端 Vue 技术来简单开发一个demo&#xff0c;实现增删改查、分页功能以及了解Springboot搭配vue完成前后端分离项目的开发流程。开发栈前端开发工具&#xff1a;WebStorm开发框架&#xff1a;vue axios包管理工具: npm打包工具&#xff1…

读取24位ad的值_实践案例丨利用小熊派开发板获取土壤湿度传感器的ADC值

摘要&#xff1a;一文带你用小熊派开发板动手做土壤湿度传感器。一、实验准备1.实验环境一块stm32开发板&#xff08;推荐使用小熊派&#xff09;&#xff0c;以及数据线已经安装STM32CubeMX已经安装KeilMDK,并导入stm32开发板对应的芯片包&#xff08;小熊派使用的是STM32L431…

mac 爱普生打印机驱动_高效打印企业首选 爱普生M2178黑白多功能一体机评测

提到打印机相信每一个公司都配备了一台&#xff0c;因为它是日常办公中不可缺少。尽管现在很多的工作流程都已经可以在线上执行&#xff0c;且提倡无纸化办公&#xff0c;但很多重要的材料还是需要打印出来存档、签字。因此打印机还是一个非常必要的配置。对于一个企业来讲&…

php ajax 加载列表,Ajax点击不断加载数据列表(图文教程)

这篇文章主要介绍了Ajax点击不断加载数据列表的相关资料,需要的朋友可以参考下Ajax简介AJAX即“Asynchronous Javascript And XML”(异步JavaScript和XML)&#xff0c;是指一种创建交互式网页应用的网页开发技术。AJAX 异步 JavaScript和XML(标准通用标记语言的子集)。AJAX 是…

linux版_微软爱 Linux:安全杀毒软件 Defender ATP 要出 Linux 版了! | Linux 中国

微软宣布将于 2020 年将其企业安全产品 Defender 高级威胁防护(ATP)引入 Linux。-- Abhishek Prakash微软的年度开发者大会 Microsoft Ignite 刚刚结束&#xff0c;会上发布了一些与 Linux 有关的重要公告。你可能已经知道微软将 Edge Web 浏览器引入 Linux&#xff0c;而下一个…

msp430中如何连续对位进行取反_四元数数控:如何保养视觉对位平台?

视觉对位平台由于人工衍生出来的各种问题应运而生,诸如高度要求的良率,生产速度的大幅提升,人员无法达成的精准度等等,对于未来这种高度自动化程度的产业更是不可或缺的产品。视觉对位平台就是一种利用XY向的移动单元加上θ角的微量转向,达到两个工作物体的组合。而且对位平台是…

ic启动器怎么导入模组_5G手机主板专题报告:高阶产能紧俏,5G手机主板升级蛋糕怎么分...

(获取报告请登陆未来智库www.vzkoo.com)一、智能机进入 5G 时代&#xff0c;主板方案望迎阶数跃迁5G 手机主板需求升级或将带来行业高端产能供给偏紧&#xff0c;A 股高端 HDI 供应商望受益。 当前时点手机各个维度的创新升级都对主板技术路线产生影响&#xff0c;芯片 I/O 数增…

element tree不刷新视图_Vue项目布署后,刷新页面404的真正原因找到了

从一篇日记说起我是一个小前端&#xff0c;我有写日记的习惯2020年10月17 天气 晴今天天气不错&#xff0c;心情也跟着好起来了辛辛苦苦加班两个星期终于完成了产品需求&#xff0c;到了要上线的时候了&#xff0c;嘴里也不知不觉哼起了“劳资今晚不加班”的小曲一顿操作猛如虎…

编程加速服务器_FPGA加速驱动新时代的数据洪流,下篇

目 录人工智能计算加速器产品系列的相关布局FPGA开发的痛点与潜在方案结语在《FPGA加速驱动新时代的数据洪流&#xff0c;上篇》中&#xff0c;老石介绍了英特尔应对大数据时代的整体战略布局&#xff0c;特别是基于FPGA的各类智能网卡和网络解决方案。在本篇中&#xff0c;老石…

respond with a status of 40_高中英语作文高分秘籍!50组高级替换词+40个高级句型+88个高级词组,还不快记下!...

很多同学都发现&#xff0c;高中阶段对于写作的考查要求更高&#xff0c;一篇没有错误但平淡无奇的文章是拿不到高分的&#xff0c;保证正确性的前提下&#xff0c;写作必须要有出彩之处才能得到阅卷老师的青睐&#xff0c;今天就为大家分享高中写作50组高级替换词40个高级句型…

iphone分屏功能怎么用_iPhone上10个隐藏小技巧,怎么用怎么爽

离上一期iPhone技巧已经过了四个多月时间了&#xff0c;不少朋友在后台一直催小雷赶紧出下一期。为了不让你等太久&#xff0c;所以今天小雷就奉上新的一期(也是防止你们等得不耐烦了以至于想捶我)。这期干货实在太多&#xff0c;如果其中有一些你们之前get过的话&#xff0c;就…

太阳能板如何串联_太阳能光伏系统单晶和多晶模组的差异?农村家庭自用如何科学选择...

太阳能光伏想必大家都耳熟能详&#xff0c;因其环保且属于可再生能源&#xff0c;目前在农村很多居民屋顶都已经铺设了光伏发电系统&#xff0c;即节省了电费开支又能并入国家电网&#xff0c;所以近些年来我国的光伏产业得到了很好的发展&#xff0c;是目前发展最快的清洁能源…

数据查询和业务流分开_TiDB HTAP 助力小红书业务升级

作者介绍&#xff1a;张亿皓&#xff0c;小红书基础技术部资深开发工程师&#xff0c;负责数据库相关的研发和落地工作。TiDB 在小红书业务场景的应用简介2017 年&#xff0c;小红书已经开始在生产业务中使用 TiDB &#xff0c;真正成体系的去做 TiDB 的落地是在 2018 年&#…

php server自定义函数,php:SQL Server用户自定义的函数种类详解

关于SQL Server用户自定义的函数&#xff0c;有标量函数、表值函数(内联表值函数、多语句表值函数)两种。题外话&#xff0c;可能有部分朋友不知道SQL Serve用户自定义的函数应该是写在哪里&#xff0c;这里简单提示一下&#xff0c;在Microsoft SQL Server Managerment Studio…

vlan为什么能隔离广播域_路由交换技术-VLAN原理及配置

VLAN原理及配置1. 背景网络中计算机的数量越来越多&#xff0c;传统的以太网开到面临冲突严重&#xff0c;广播泛滥及安全性无法保障等问题VLAN(virtual local area network)即虚拟局域网&#xff0c;是将一个物理的局域网在逻辑上划分为多个广播域的技术&#xff0c;通过在交换…

刀片 显卡 排行_AMD发布RadeonRX6000系列游戏显卡 4599元起

10月30日消息&#xff0c;据国外媒体报道&#xff0c;AMD日前发布了Radeon RX 6000系列游戏显卡&#xff0c;该系列显卡包括AMD Radeon(镭龙) RX 6800显卡和RX 6800 XT显卡&#xff0c;以及新旗舰RX 6900 XT显卡。AMD Radeon RX 6000系列显卡(图片来自AMD官网)AMD Radeon RX 68…

等保2.0安全管理制度对比_一手资料!等保2.0云计算安全与风险评估

一、等保2.0对云计算发展提出“新要求”与等保1.0的标准体系相比&#xff0c;等保2.0在适用性、时效性、易用性、可操作性上得到进一步扩充和完善&#xff0c;以适应云计算、物联网、工业控制系统等新技术的发展。如下表给出了等保2.0发生的重要变化。针对等保2.0标准提出的新要…