您的位置 首页 java

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

前言

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

大家知道,自从前后端分离以后,像我一样一直以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.bad Request ().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自定义异常即可。

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

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

文章来源:智云一二三科技

文章标题:我有一个朋友,和前端工程师联调接口被狠狠鄙视了一番

文章地址:https://www.zhihuclub.com/194473.shtml

关于作者: 智云科技

热门文章

网站地图