写给前端的甩锅指南

1
2
3
4
5
6
7
8
9
前端小白,“老王,你的接口不能用了”
后端老王一脸不屑,“怎么可能,刚刚还能用,你看看参数传对了没有。”
前端小白吭哧吭哧检查了几遍,“参数没问题啊,你看看是不是有 username, id。。。”
后端老王顿了许久, “参数应该没问题,你再清下浏览器缓存试试”
前端小白摁着 "ctr + f5" 刷新了好多次, 一脸苦逼的说:“清完浏览器缓存了,还是有问题”
后端老王略过一丝疑虑,难道是我接口出问题了,不该啊,刚还能用,都测试好几遍了,说道:“你看看是不是数据库存的测试数据有问题,删了再试试,或者换个浏览器,重启下 server。”
前端小白照着老王的方法逐一尝试着。。。
突然老王拍了下脑瓜,一脸尴尬的说:“小白,我刚 fix 一个线上 bug, 测试环境换了分支,所以接口出问题了。”
小白一脸生无可恋的望着老王头顶的地中海发呆。。。

上面情节纯属虚构,作为一个后端开发人员,我怎么忍心这么黑自己的同行呢 😭。不过思考下这么一个问题,前端如何能够快速的定位问题呢。这里面跟很多因素有关,比如前后端协作流程是否完善,前后端人员的经验。

我经常看见前端工程师的招聘贴上会有这么一个要求:了解一门后台语言,有后台开发经验者优先。很多人可能心里嘀咕:这是准备拿我当全栈去用啊。先不说这个算不算全栈,就说这个要求具体指什么。其实它指的是对于 web 开发流程的熟悉,你需要理解一个 ajax post 请求最后如何变成一条数据库记录的。流程其实很复杂,本文就说一个重点,前后端信息交互的信使,http 请求。

说到这,很多朋友觉得没意思,后端的同学会说不就是处理个请求吗,写个方法,拿到数据,存个数据库,返回个 success ,完事。前端的也说 ajax 我用的可溜了,post 一下,拿到后端返回的 success,弹个窗,皆大欢喜。

好吧,其实确实是这个道理。但如果不 success 了,怎么能够知道问题在哪。我相信大家对下面那张图都很熟悉,chrome 的 devtool ,一般情况下大家在这里看请求传递的参数,返回的结果。但是很多人都会忽略一些默认传递的 headers,比如 cookie,content-type

QQ20170308-1

举个 csrf_token 的例子,经常有一些刚入门的前端会遇到这么个情况,页面上 form 表单 post 提交好好的。但是一旦换成 ajax 请求发现怎么就返回 403 了呢,搜了下最大工单系统 stackoverflow,一下子就有解决方案了,至于为什么,就没有去深入探究了。

1
2
3
4
5
6
7
8
9
10
11
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});

入正题,对于一个前端来说,了解下 http 协议很有用。只有你了解发出去的包是什么,你才会有这个胆量说这个锅前端不背,浏览器也不背。

http 协议基于 tcp 协议之上,所以 tcp 将一些粗活累活都揽在自己手里了,比如分片,重试,连接。http 只需要将规定好的格式传给接收方就行了。

比如这个

1
2
3
4
GET / HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) Gecko/20050225 Firefox/1.0.1
Connection: Keep-Alive

由于 http 经过这么多年的发展,加的东西也是越来越多,权威指南越来越厚。基本上活到老学到老,但是添加新东西总有他的道理,如果我们能够添加的那些东西都解决了什么问题,其实比死记硬背效果更好。我们往下看

首先是交互的方法,最基本的方法有4种,分别是POST, GET, PUT,DELETE 。后端工程师平时工作有很大的部分就是做 CRUD,跟这些方法较劲。每个方法的出现都有它的作用,比如 HEAD,大家只是想知道 https://zhihu.com 这个 url 对应的 http 头部里面有啥信息,用 GET 做请求把所有页面信息都返回了,太不经济,所以就有了 HEAD 这个方法。当然你也可以自己创造一个方法,比如 ‘FUCK’, 需要后端写个相应的方法支持。
QQ20170308-2

server端代码

其实 http 里面最难理解的就是各种头,如果要一个个去记住实在太难,但是其实他们也是为了解决各种问题的。比如为了缓存页面,有 cache-control,If-Match, If-None-Match, If-Modified-Since, ETag。可以参考这篇: HTTP缓存控制小结

网站总会遇到一些安全问题,比如 xss, csrf 之类。开发人员很难保证写出来的程序没有漏洞,浏览器加了些安全策略来防御 xss,同样的,加一些 http 的头也能有效的防御攻击,比如 csp(Content Security Policy), 参考文章: Content Security Policy 介绍

还比如说 Cookie 这个头,它本身的值就有很多选项,控制 Cookie 的作用范围,失效时间。浏览器是默认传 Cookie 的,但是如果不在浏览器上,写爬虫脚本的话需要自己把 Cookie 这个 header 带上。很多的 web server 是借助 Cookie 作为用户会话的凭证的,比如存一个唯一的 sessionid 值到 Cookie,每次请求都带上,来判断是哪个用户。

另外如果不用 Cookie 作为会话认证,还有其他方式吗?其实说到底认证就是需要传给服务端有个字段标识哪个用户,那我们可以在头里面加个 Authorization 的字段,其他名字也行,只要跟服务端商量好。

还有一种情况,如果你请求的接口和你当前的域不一样,叫跨域请求,几种解决方案,其中一种是服务端加 Access-Control-Allow-Origin 头,其实就是浏览器看见了这种头,就给了权限访问其他域。
最后 http 会有一些状态码返回,状态码对应表,状态码也可以自定义,比如定义个 233。

QQ20170308-3

如果你写的是手机端的混合页面,没有 dev_tool 调试很心塞,这个时候你可以用 charles 或者 mitmproxy 做代理。mitmproxy 作为中间人劫持工具,还能满足你的好奇心,比如看看隔壁妹子在刷什么微博,给她弹个窗什么的。

所以问题来了,最终锅甩给谁了呢?