小复盘:Vue+Flask实战Web应用

浏览:7933次阅读
没有评论

共计 6119 个字符,预计需要花费 16 分钟才能阅读完成。

本文主题

对于还不是很明白前后端分离的 Web 应用开发的朋友,可以通过本文,了解一下通过前端访问的 URL 路径,是如何一步步找到后端去执行具体函数的。

在这个过程中,主要聊到如下主题,您感兴趣就随便看看:

  • 初遇路由
  • 蓝图的注册
  • Vue 单页面应用
  • Vue-cli 配置文件中的跨域和路由
  • 路由和视图函数

〇、前提

项目相关环境
前端:http://127.0.0.1:8010
后端:http://127.0.0.1:8089

一、路由的秘密

很多地方都会用到路由的概念,也很难一句话解释清楚,大家应该知道有个叫“路由器”的东西,数据从一个网络到另一个网络就是靠路由来完成的。

引用一张图来解释路由(出处:https://www.jianshu.com/p/b55cf53e633a):

image.png
  1. 先说一个现象

当我们在前端页面
http://127.0.0.1:8010/#/manage/flowerManage
进行某个操作时,在调试窗口看到它访问了这样一个接口并且得到了响应:

http://127.0.0.1:8010/api/flower/proGather/list

试试直接在 Postman 这种接口测试工具里输入该接口,也得到了对应的 Response。

不过,在这个项目中,是在后端写了 /proGather/list 路径对应的视图函数,执行后返回处理结果。

疑问产生

后端是在 8089 端口启的服务,为什么这里使用 8010 端口也能访问呢?如果直接改成 8089 端口,可以访问到嘛?那我们再来试试在 Postman 中访问这个接口:

http://127.0.0.1:8089/api/flower/proGather/list

哈哈,出错啦。不过注意到没有,报了这样的错误:
raise NotFound()\nwerkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.

2. 为什么呢?

简单解释就是:前端服务通过路由,找到后端对应的视图函数,进行处理。这里又有一个 视图函数 的概念哎!本文后面会解释一下这个,你就先理解成是一个函数,运行之后给出了返回值作为 Response 给到前端就好了。

接下来看看后端为啥没有访问成功,这个是和我们视图函数的写法,以及配置息息相关。因此会放到后面“Vue-cli 配置文件”的部分详细解释清楚,先来说下针对这个项目,如何可以访问成功:

http://127.0.0.1:8089/flower/proGather/list

去掉路径里面的api,就可以了。

二、蓝图

概念大家都懂了哇?说一下怎么用。一个 WebApp 里面可以注册一个蓝图,当然也可以注册多个蓝图,下面的例子就是注册多个蓝图的情况。
create_app 的定义里面注册就好了,然后可以指定对应的url_prefix

__init__.py 文件的 create_app() 中,写了相关代码如下:

def create_app():
    app = Flask(__name__)
    app.config.from_object(BaseConfig)
    app.logger.addHandler(config_log())  # 初始化日志
    BaseConfig.init_app(app)
    moment.init_app(app)

    db.app = app
    db.create_all()

    login_manager.init_app(app)
    scheduler.start()  # 定时任务启动

    # 注册多个蓝图模块
    from app.admin import admin_module
    from app.flower import flower_module
    app.register_blueprint(admin_module, url_prefix='/admin')
    app.register_blueprint(flower_module, url_prefix='/flower')

    return app

那么再来看看蓝图模块是怎么写的呢?
目录结构是这个样子的:

app
    -admin
        __init__.py
        admin_manage.py
    -flower
        __init__.py
        flower_manage.py
    __init__.py

看下 admin 目录下的两个文件;flower 目录下也是类似的

app/admin/__init__.py

from flask import Blueprint
admin_module = Blueprint('admin_module', __name__)
from . import admin_manage

app/admin/admin_manage.py

from . import admin_module

@admin_module.route('/admin/add', methods=['POST'])
def add_user():
    pass

好啦,照葫芦画瓢,你也会注册蓝图模块了!

三、理解 Vue 的单页面应用

很久以前用 html 做过网站,所以一直有一个惯性思维,觉得点击网页上的超链接跳来跳去的,就一定是在不同的页面间切换。

所以一开始也按照这个思路来学习 Vue,看到别人案例中,有一个个的 Vue 文件,想当然的就理解成对应的页面了,后来才发现并非如此。

组件

Vue 中有一个很重要的概念就是组件!官网都是这么介绍组件的:

// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{count}} times.</button>'
})

<div id="components-demo">
  <button-counter></button-counter>
</div>

new Vue({ el: '#components-demo' })

组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

你可以将组件进行任意次数的复用。

对了单页面应用简称 SPA。罗里吧嗦一大堆,那么它和单页面应用之间有什么联系?

首先,我们定义组件;
然后,我们定义路由规则;
接着,我们访问路径,就会跳转到相应的组件上去。

所以,我们并不是在页面间跳转,而是通过路由规则,在组件之间进行跳转!

四、Vue-cli 配置文件

在这个项目里,前端使用了 Vue-cli 脚手架。
如果你也直接用 Vue-cli 创建一个 vue 项目的话,可以在 config 目录下看到好几个.js 文件,就是配置文件啦。
如果你是网上找来的项目,可能会略有不同,不过内容上都是类似的。

这次我们就来看看 index.js 文件里的部分内容吧,由于把配置详解一遍很花时间,所以我们就聚焦一点,来揭开第一部分接口调用路径的谜底!
当然,有些项目的配置不一定保持了初始的名字,总之是含有 module.exports = { 这行代码的配置文件就对了!

我这里是这样配置的:

module.exports = {
    lintOnSave: true,
    productionSourceMap: false,
    chainWebpack: (config) => {
        config.resolve.alias
            .set('~', resolve('src'))
    },
    devServer: {
        host: '127.0.0.1',
        port: 8010,
        proxy: {
            '/api/': {
                target: 'http://127.0.0.1:8089',
                changeOrigin: true,
                pathRewrite: {
                    '^/api': ''
                }
            }
        }
    },
    css: {
        loaderOptions: {
            css: {
                localIdentName: process.env.node_env == 'production' ? "[hash]" : '[path]_[name]_[local]_[hash:base64:5]',
                camelCase: 'only'
            },
            sass: {}
        },
    }
};

首先看下这个devServer.proxy.changeOrigin,它是为了解决跨域问题的一个设置。
changeOrigin: true, // 开启代理:在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题

那什么是跨域?

浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域,如下例子:
域名
主域名不同 http://www.baidu.com/index.html –>http://www.sina.com/test.js
子域名不同 http://www.666.baidu.com/index.html –>http://www.555.baidu.com/test.js
域名和域名 ip http://www.baidu.com/index.html –>http://180.149.132.47/test.js
端口
http://www.baidu.com:8080/index.html–> http://www.baidu.com:8081/test.js
协议
http://www.baidu.com:8080/index.html–> https://www.baidu.com:8080/test.js
备注
1、端口和协议的不同,只能通过后台来解决
2、localhost 和 127.0.0.1 虽然都指向本机,但也属于跨域

综上,我目前的设置前后端的端口不一样,也算是跨域了,因此就要设置成 true 啦!

接着看下这段:

 proxy: {
            '/api/': {
                target: 'http://127.0.0.1:8089',
                changeOrigin: true,
                pathRewrite: {'^/api': ''}
            }
      }

vue-cli 的这个设置来自于其使用的插件 http-proxy-middleware,github 地址如下:
https://github.com/chimurai/http-proxy-middleware
官网文档中有这么一句:

proxy('/api', {...}) - matches paths starting with /api

这就说明,当我们的路径中含有 /api 的时候,就会匹配到这里对应的配置啦。

那么 pathRewrite 是干啥的呢?文档解释如下:
option.pathRewrite: object/function, rewrite target's url path. Object-keys will be used as RegExp to match paths.
并且给出示例:

// rewrite path
pathRewrite: {'^/old/api' : '/new/api'}

// remove path
pathRewrite: {'^/remove/api' : ''}

// add base path
pathRewrite: {'^/' : '/basepath/'}

// custom rewriting
pathRewrite: function (path, req) { return path.replace('/api', '/base/api') }

而我们的配置中有写pathRewrite: {'^/api': ''},因此 proxy 时要把路径中的 /api 去掉。
OK,那么整体来解释一下 devServer 这段配置的意思!

devServer: {
        host: '127.0.0.1',
        port: 8010,
        proxy: {
            '/api/': {
                target: 'http://127.0.0.1:8089',
                changeOrigin: true,
                pathRewrite: {
                    '^/api': ''
                }
            }
        }
    }

首先,127.0.0.1:8010过来的请求,如果路径中含有 /api,就会被路由到127.0.0.1:8089 上去,并且把路径中的 /api 去掉。
也就是说,当我们在前端访问了一个这样的接口:

http://127.0.0.1:8010/api/flower/proGather/list

实际上对应的是后端这个接口:

http://127.0.0.1:8089/flower/proGather/list

再来回到开头的问题上来,为什么使用 postman 访问 http://127.0.0.1:8089/api/flower/proGather/list 会报 404 的错误?这下你就清楚了吧!

那么再来问一个问题,后端的接口路径为什么是这样的呢,那就要来聊聊视图函数啦。

五、路由与视图函数

在《Flask Web 开发》一书中,对于 URL 如何映射到 Python 函数,有这样的一段解释:

客户端(例如 Web 浏览器)把请求发送给 Web 服务器,Web 服务器再把请求发送给 Flask 程序实例。程序实例需要知道对每个 URL 请求运行哪些代码,所以保存了一个 URL 到 Python 函数的映射关系。处理 URL 和函数之间关系的程序称为路由。

在 Flask 程序中定义路由的最简便方式,是使用程序实例提供的 app.route 修饰器,把修饰的函数注册为路由。

再来看我们的项目代码。还记得刚才说的目录结构么?

app
    -admin
        __init__.py
        admin_manage.py
    -flower
        __init__.py
        flower_manage.py
    __init__.py

看一下 flower_manage.py 这个文件:

from . import flower_module

@flower_module.route('/proGather/list')
def get_list():
    pass

看来真正访问的是 get_list() 这个函数呢!可是我们在修饰器里只写了相对路径 /proGather/list 呀!
再来复习一下我们在 create_app() 函数里面注册蓝图的时候是怎么写的:

   from app.flower import flower_module
   app.register_blueprint(flower_module, url_prefix='/flower')

所以 flower_module 对应着 url_prefix='/flower',在拼上后端服务启动时的端口号,可不就是http://127.0.0.1:8089/flower/proGather/list 了么!

六、小结

本文差不多就是这样了,如果还觉得看得不够清楚,下次我写一个示例项目传到 github 上再解说吧。

作者:cynthia 猫
链接:https://www.jianshu.com/p/b72548f04592
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

正文完
 0
(未见秋)
版权声明:本站原创文章,由 (未见秋) 于2021-11-29发表,共计6119字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)