前言

我有一个朋友,昨天和前端工程师联调一个接口,然后被狠狠鄙视了一番。

大家知道,自从前后端分离以后,像我一样一直以Java工程师为傲而自居的码圣们就砍掉了一半脊梁,从此被贴上了“Java服务端工程师”、“Java后端工程师”等等这样的标签。

同时,前端爸比越来越多,也让我们写个接口都如履薄冰。

那么到底发生了审麽事情咧?

经过

梳理出来,大体经过是这样滴:

1)、我朋友是Java工程师,入职公司四个月,刚转正一个月,目前正在参与一个紧急的项目开发;

2)、他写完了接口,自测没问题,然后发布到测试环境,再测没问题,欧克,输出文档给前端,准备联调咯;

3)、前端工程师是个爸比,三十出头,追求细节,人狠话不多,入职三年多,公司大半前端页面和数据绑定都由他完成,是前端扛把子,看完了文档,调了下接口,欧克,没问题,开干;

4)、一上午过去了,很简单的接口并没有联调完,甚至两人发生了些许不愉快;

5)、前端爸比认为接口正常情况下可以,但异常情况下状态给的不明确,没办法根据状态值给用户友好提示;

6)、我朋友来的时间短,敢怒不敢言,畏畏缩缩指出了自己接口自定义了返回对象,正常时状态返回200,异常时会触发全局异常处理,返回状态500,很明确并没有什么问题嘤嘤婴;

7)、前端爸比一声嗤笑,哼小伙子,你一看就道行尚浅,和我有一腿……有交集的后端如过江之鲫,我联调过的接口比你拉的SHI还多,你快坐回去好好看看代码,是不是接口加了trycatch,然后捕获异常时直接返回了自定义响应对象;

8)、我朋友心中一慌,这老银币有点东西,一个前端连我Java代码怎么写的都知道,赶忙跑回去重新审视代码,来回审视和自测了好几遍,终于发现了不算问题的问题;

9)、原来接口返回的业务状态有很多,但HTTP状态永远是200成功,但这对你前端有毛的影响?

10)、前端爸比说确实没啥影响,但我就是要判断HTTP状态码,我有强迫症;

11)、没办法,毕竟是爸比,我朋友之后参考了我所负责的项目里面的接口代码,顺利完成了之后的联调工作,但从此在前端爸比心里打上了菜鸟的标签。

问题重现

为了节省时间,我直接以renren-fast作为脚手架来重现这个问题。

首先,我们定义一个简单的接口,使用自定义响应对象R返回,对接口进行try..catch,成功时返回R.ok(),异常时在catch中返回R.error()及错误信息。

(PS:题外话,工作这些年换过几个公司,其实看到不少同事喜欢这么写,我想其他公司也不在少数。)

/**
* 自定义响应对象返回
* @return 结果
*/
@GetMapping("getUserInfo")
@ApiOperation("获取用户信息")
public R getUserInfo() {
Map<String, Object> map = new HashMap<>();
try {
map.put("id", "1001");
map.put("name", "张三");
map.put("age", "33");
map.put("address", "湖北省神农架野人洞");
} catch (Exception ex) {
return R.error("异常:" + ex.getMessage());
}
return R.ok().put("data", map);
}

使用Postman调试一下接口,嗯可以正常返回。

接下来,模拟接口发生一个异常。

/**
* 自定义响应对象返回
* @return 结果
*/
@GetMapping("getUserInfo")
@ApiOperation("获取用户信息")
public R getUserInfo() {
Map<String, Object> map = new HashMap<>();
try {
map.put("id", "1001");
map.put("name", "张三");
map.put("age", "33");
map.put("address", "湖北省神农架野人洞");
// 模拟异常
int i = 1/0;
} catch (Exception ex) {
return R.error("异常:" + ex.getMessage());
}
return R.ok().put("data", map);
}

再使用Postman调用下看看,是返回500异常了,HTTP状态是200,没啥问题啊。

如果抛出一个异常呢

/**
* 自定义响应对象返回
* @return 结果
*/
@GetMapping("getUserInfo")
@ApiOperation("获取用户信息")
public R getUserInfo() {
Map<String, Object> map = new HashMap<>();
try {
map.put("id", "1001");
map.put("name", "张三");
map.put("age", "33");
map.put("address", "湖北省神农架野人洞");
// 模拟异常
int i = 1/0;
} catch (Exception ex) {
// 抛出一个运行时异常
throw new RuntimeException(ex.getMessage());
}
return R.ok().put("data", map);
}

一般会交由项目的全局异常进行处理,实际返回的还是自定义的响应对象R.error()。

@ExceptionHandler(Exception.class)
public R handleException(Exception e){
logger.error(e.getMessage(), e);
return R.error();
}

然后Postman再调试,可以看到,HTTP状态不变,接口业务状态返回500并提示异常,和前面一样,确实牟闷提啊。

好,这里说下,程序实际上是发生了异常,由代码自行捕获并返回了自定义响应结果,HTTP状态是200表示接口连通性正常,业务状态是500表示业务程序发生了异常。

其实大部分项目都这么做的,本身没什么问题,但有时会给前端工程师对接口状态的逻辑判断产生误解,再有,如果你是给另一个厂家写接口,你是提供方,对方是消费方,这么写会给对方制造麻烦。



正常来讲,有经验的前端工程师一般会这么判断:

1)、先判断HTTP状态,不是200表示失败则给出友好提示,成功则继续判断接口业务状态;

2)、判断接口业务状态,若返回200表示成功,则绑定数据,若不是200,给出友好提示,若有特殊业务状态,另行判断并处理。



那么,当后端工程师返回的是如示例所示的自定义响应对象,且全局异常处理中返回的也是示例中的自定义响应对象时,就意味着我们的接口HTTP状态永远都是200成功,前端对这一块的判断完全是失效的,一旦线上的项目出现特殊情景,可能造成意外假象。



再者,如前面所说,你是给其他公司厂家甚至第三方组件提供接口,这么写的话HTTP状态永远是200,也存在隐患,比如本人第一家公司用的XXLJOB,我们需要写接口给XXLJOB进行任务调度,这个接口就是上面那样返回的,一开始是好的,后来有同事改代码改出点问题,线上刚好也出现了该异常,而XXLJOB就是判断HTTP_STATUS的,结果它怎么识别我们接口都是返回200成功,它就没有反馈任何异常警告,导致这个重要的调度任务虽然正常执行却是无效的,我们也没留意,直到一堆待退费订单没有处理才发现问题。

优化处理

上面展示的实际上本身不是问题,大部分项目这么写也能正常在线上运行,只是存在小概率的风险,当项目规模较大时,存在很多不确定性,接口的返回状态是消费方进行逻辑处理的唯一依赖,因此,我的建议是最好同时返回更准确的HTTP状态和接口业务状态。

处理方式十分简单,使用spring-web自带的ResponseEntity包装一下即可。

/**
* 自定义响应对象返回(外层包装ResponseEntity)
* @return 结果
*/
@GetMapping("getUserInfo2")
@ApiOperation("获取用户信息")
public ResponseEntity<R> getUserInfo2() {
Map<String, Object> map = new HashMap<>();
try {
map.put("id", "1001");
map.put("name", "张三");
map.put("age", "33");
map.put("address", "湖北省神农架野人洞");
// 模拟异常
int i = 1/0;
} catch (Exception ex) {
// return ResponseEntity.badRequest().body(R.error("异常:" + ex.getMessage()));
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(R.error("异常:" + ex.getMessage()));
}
return ResponseEntity.ok().body(R.ok().put("data", map));
}

效果:

ResponseEntity封装了几乎所有的HTTP状态,上面示例代码包含注释掉的那行,一共两种方式,都可以自行返回具体的HTTP状态给前端.

如果选择自定义响应对象作为返回,那么就放到body里面即可,相当于ResponseEntity做了一层外包装,这样就能保证返回的接口既有具体HTTP状态,也有具体的业务状态,前后端工程师从此成为相亲相爱一家人。

总结

我给大家的最终建议是这样的:

1)、整个项目都规范好以ResponseEntity作为响应对象;

2)、如果有使用自定义响应对象,最好用ResponseEntity进行一层外包装;

3)、如果嫌弃这种写法,还可以这样,接口依然返回自定义响应对象,但全局异常处理中返回对象进行ResponseEntity包装,最后在出问题的地方throw自定义异常即可。

现在各种新技术层出不穷且内卷的状况下,不要过分追求强大流行的技术,反而要多关注基本功和编码小细节。

尤其是对尚未工作及工作年限不久的同行们而言,不要小看写接口的能力,否则也会被公司的爸比所鄙视哦。


本人专注于分享各种技术、工作中的趣事及经验,喜欢或有收获的朋友们,不要吝啬您的一个小小推荐哦~~

也可以查看个人主页关注一下里面的信息哦~~

【Java分享客栈】我有一个朋友,和前端工程师联调接口被狠狠鄙视了一番。的更多相关文章

  1. 【Java分享客栈】我为什么极力推荐XXL-JOB作为中小厂的分布式任务调度平台

    前言   大家好,我是福隆苑居士,今天给大家聊聊XXL-JOB的使用.   XXL-JOB是本人呆过的三家公司都使用到的分布式任务调度平台,前两家都是服务于传统行业(某大型移动基地和某大型电网),现在 ...

  2. 【Java分享客栈】SpringBoot整合WebSocket+Stomp搭建群聊项目

    前言 前两周经常有大学生小伙伴私信给我,问我可否有偿提供毕设帮助,我说暂时没有这个打算,因为工作实在太忙,现阶段无法投入到这样的领域内,其中有两个小伙伴又问到我websocket该怎么使用,想给自己的 ...

  3. 【Java分享客栈】一文搞定CompletableFuture并行处理,成倍缩短查询时间。

    前言   工作中你可能会遇到很多这样的场景,一个接口,要从其他几个service调用查询方法,分别获取到需要的值之后再封装数据返回.   还可能在微服务中遇到类似的情况,某个服务的接口,要使用好几次f ...

  4. 【Java分享客栈】一文搞定京东零售开源的AsyncTool,彻底解决异步编排问题。

    一.前言 本章主要是承接上一篇讲CompletableFuture的文章,想了解的可以先去看看案例: https://juejin.cn/post/7091132240574283813 Comple ...

  5. 【Java分享客栈】SpringBoot线程池参数搜一堆资料还是不会配,我花一天测试换你此生明白。

    一.前言   首先说一句,如果比较忙顺路点进来的,可以先收藏,有时间或用到了再看也行:   我相信很多人会有一个困惑,这个困惑和我之前一样,就是线程池这个玩意儿,感觉很高大上,用起来很fashion, ...

  6. 【Java分享客栈】超简洁SpringBoot使用AOP统一日志管理-纯干货干到便秘

    前言 请问今天您便秘了吗?程序员坐久了真的会便秘哦,如果偶然点进了这篇小干货,就麻烦您喝杯水然后去趟厕所一边用左手托起对准嘘嘘,一边用右手滑动手机看完本篇吧. 实现 本篇AOP统一日志管理写法来源于国 ...

  7. 【Java分享客栈】我曾经的两个Java老师一个找不到工作了一个被迫转行了

    前言 写这篇文章的初衷主要是最近发生了两件事,让我感慨良多,觉得踏入这个行业的初始,有些事情就应该长远考虑,这样对职业发展才更有利,仅仅停留在技术的追求上固然能壮大自身,可逆水行舟的程序员们终究会面临 ...

  8. 【Java分享客栈】Java程序员为争一口气熬夜硬刚CSS实现掘金首页

    前言 如果我做不了最厉害的Java工程师,那我就做Java工程师中最厉害的前端工程师. 前段时间,我默默给自己又喂了这碗心灵鸡汤-- 我不是很厉害的Java工程师,哪怕我已经工作八年,我依然觉得自己和 ...

  9. 【Java分享客栈】从线上环境摘取了四个代码优化记录分享给大家

    前言 因为前段时间新项目已经完成目前趋于稳定,所以最近我被分配到了公司的运维组,负责维护另外一个项目,包含处理客户反馈的日常问题,以及对系统缺陷进行优化. 经过了接近两周的维护,除了日常问题以外,代码 ...

随机推荐

  1. 【C++】STL容器

    STL容器 标签:c++ 目录 STL容器 容器的成员函数 所有容器都有的 顺序容器和关联容器 顺序容器(vector/string/list/deque) 容器 vector 构造函数 操作 set ...

  2. 【海淘域名】GoDaddy账户被锁定后的解决方法

    转载自[美国海淘网]http://www.usahaitao.com/Experience/Detail_2886.html   通过ICANN申诉顺利的从国内无良奸商(35互联与商务中国,小编的域名 ...

  3. Cesium中级教程4 - 空间数据可视化(二)

    Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ Viewer中的Entity功能 让我们看看Viewer为操作e ...

  4. npm 和 yarn 前端包管理工具

    前言 前端开发逐渐工程化,npm作为我们的依赖管理工具起到十分重要的作用,本文就来总结一下 npm 和 yarn 相关知识点. 正文 1.什么是npm (1)node的包管理器(node packag ...

  5. WebGPU相关的资源学习和社区

    我在网上搜寻了很多关于WebGPU相关的资料: #我觉得首先在B站上看到的徐博士免费教程非常好,讲解详细,并且评论回复比较快,都会有回应,徐博士B站网址:https://space.bilibili. ...

  6. vue3源码node的问题

    下载vue3源码后,下载依赖时,node的版本需要在10.0.0以上,并且不同的vue3里面的插件的配置对版本依赖还不同,14.0.0以上的版本基本都不支持win7了, win7系统可以安装12.0. ...

  7. IoC容器-Bean管理(bean作用域)

    IoC操作Bean管理(bean作用域) 1,在Spring里面,设置创建bean实例是单实例还是多实例 2,在Spring里面,默认情况下,bean是单实例对象 3,如何设置单实例还是多实例 (1) ...

  8. 机器学习-softmax回归 python实现

    ---恢复内容开始--- Softmax Regression 可以看做是 LR 算法在多分类上的推广,即类标签 y 的取值大于或者等于 2. 假设数据样本集为:$\left \{ \left ( X ...

  9. Programiz 中文系列教程·翻译完成

    原文:Programiz 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远. 在线阅读 ApacheCN 学习资源 目录 Programiz C ...

  10. NOIP2018 Day2T3 保卫王国

    首先不考虑强制要求的话是一个经典问题,令 \(f_{i, 0 / 1}\) 为 \(i\) 选或不选时以 \(i\) 为根的子树的最优答案.那么就有转移 \(f_{u, 0} = \sum f_{v, ...