Vue与API请求遇到的坑

Author Avatar
patrickcty 11月 07, 2017

开始之前

最近在写比赛项目 TeamSeeker 的代码,后台写得差不多了,于是乎决定写一下网页前端来请求 API,顺便再次测试一下后台的合理性,同时也接触一下一直都想尝试的 Vue。只是没想到居然会遇到这么多坑。

遇到的坑

初始化 Vue 项目

本来只是准备写单个的 HTML 来组织成前端的,后来想来想去还是跟着教程用 Webpack 来初始化一个比较大的项目。

但是项目是成功初始了,但是在引用第三方的组件的时候,明明已经使用 npm 安装了但是就是引用不了 CSS。

去网上找了一圈发现是没有在项目的配置文件中对这些文件类型进行配置,然而试了半天配置的还是不成功……

最后发现了一个简单的方法,就是在用 npm 安装模块的时候使用 -S 参数,这样就会自动保存在配置中了。这个参数表示保存的意思,等同于 --save

npm i materialize-css -S

发送请求时候的跨域问题

因为使用的是前后端分离的设计思想,因此难免会产生跨域问题。其实这个问题我之前遇到过,当时是直接在返回的内容中带上 Access-Control-Allow-Origin 这个请求头。

不过坑爹的是,在有的时候,在发送跨域请求之前,浏览器会发送 OPTIONS 请求来检查服务器有没有返回 Access-Control-Allow-Origin 这个请求头从而判断服务器是否支持跨域。于是问题就来了,因为我根本没有定义这个方法,因此也自然不会返回这个请求头了,于是就产生了跨域的问题……

不过这个问题在 Google 上找到了答案,具体原理就是在服务器自定义 Response 类,使得在每次请求后都返回所需要的请求头,这样就可以解决这个问题了。

class MyResponse(Response):
    def __init__(self, response=None, **kwargs):
        kwargs['headers'] = ''
        headers = kwargs.get('headers')
        # 跨域控制
        origin = ('Access-Control-Allow-Origin', '*')
        methods = ('Access-Control-Allow-Methods', 'HEAD, OPTIONS, GET, POST, DELETE, PUT')
        if headers:
            headers.add(*origin)
            headers.add(*methods)
        else:
            headers = Headers([origin, methods])
        kwargs['headers'] = headers
        return super().__init__(response, **kwargs)

然后在 create_app 里面使用这个类:

app.response_class = MyResponse

原文参考这个博客

顺便一提,在解决这个问题的时候还出现了一个问题,那就是在请求第二页的时候不管后端前端怎么改都得不到正确的结果,结果找了半天发现是并没有第二页的内容……真的是智障了……

使用 axios 无法将 response 保存下来

axios 是 Vue 官方推荐的 AJAX 请求发送的模块,一个 GET 请求是这样发送的:

axios.get('http://example.com/api/index', {
    params: {
      "page": page
    }
})
    .then(function (response) {
      console.log(response);
    })
    .catch(function (error) {
      console.log(error);
    })

但是我如果想保存 response 对象到 Vue 的一个对象中(需要使用 this) 来访问,如果直接使用 this.$store.dispatch('getIndex', response.data); 是不能保存进去的,原因在于 this 在 axios.get() 函数中也起作用,在函数中的作用域就不是 Vue 的对象了,因此我们要在调用函数之前把 this 赋值给另一个变量,然后再用另一个变量来引用 Vue 的对象的内容:

var _self = this;
axios.get(
    ...
    _this.$store.dispatch('getIndex', response.data);
)

数据首次加载

这个到现在还是不是很清楚,先留个坑。

使用 axios 发送 POST 请求遇到的坑

继使用 axios 发送 GET 请求遇到坑之后,用 POST 请求又踩到了坑,真的是体验极差啊(哭。

这次又是什么问题呢?那就是:

Request header field Access-Control-Allow-Headers is not allowed by Access-Control-Allow-Headers

为什么会出现这个问题呢?又是 OPTIONS 这个磨人的小妖精……StackOverflow 上是这样解释的:

For cross-domain requests, setting the content type to anything other than application/x-www-form-urlencoded, multipart/form-data, or text/plain will trigger the browser to send a preflight OPTIONS request to the server.

而 axios 发送 POST 请求的时候默认是用 JSON 作为 Content-Type 来发送了,而这正好不是上面的这些,因此就又要提前发送一次 OPTIONS 请求,而正好我又没有对这个处理……

不过这次就不用改服务器的代码了,axios 官方已经给出了解决方法了,那就是把 Content-Type 设置为 application/x-www-form-urlencoded,然后再对 JSON 数据进行处理,具体方法如下:

var qs = require('qs');
axios.post('http://example.com/api/wxlogin',    
    qs.stringify({
        js_code: '001U3te40K6KSD1Bpaf40DKAe40U3tel'
    })
)
    .then(function (response) {
        console.log(response);
    })
    .catch(function (error) {
        console.log('aaa', error);
    })

当然这个 qs 模块也是要事先安装的:

npm i qs -S

使用 font-awesome 但是死活引入不了 CSS

网站当然少不了图标,一个比较简单好用的方法就是使用 font-awesome 提供的图标,平常我都是使用 CDN 来引入的,但是这个项目结构下好像不适合直接引入,而是应该使用 npm 安装模块。

但是安装之后死活找不到安装好的模块,也就无法引入了。于是不得不去安装 vue-awesome,然而这个的图标死活和文字对齐不了……这个简直不能忍啊,最后不得不又得回到 font-awesome。

又找了一圈之后发现是我安装的方法不对,我一开始是使用下面这个来安装的:

npm i fontawesome -S(不要用这个安装!!!)

确实提示已经安装成功了,但是不能用。我之后又使用了下面这个来安装:

npm i font-awesome -S

加了中间这个短横线之后就可以引入了……虽然我发现安装后的提示都是一样的……简直坑爹啊……还是我大 Python 的 pip 要好用……

最后

这几天真的踩了超多的坑,简单的一个页面结果写了一周才大概完成……中间的心酸真的是只有自己知道了……

最近一段时间也是事情堆起来了……哎只希望下次驾照考试能顺利一点吧……已经成了我心中的梦魇了……真的很害怕很无助……