好久不更新,更新就大活,蘑菇APP开发前置一
基于flask框架的掌上商城开发,移动端app与后端服务器相结合,此篇为前置内容准备。
1.flask框架
因为项目整体是基于flask这款轻量级框架搭建的,所以先简易介绍一下flask框架,Flask诞生于2010年,是Armin ronacher(人名)用 Python 语言基于 Werkzeug 工具箱编写的轻量级Web开发框架。
flask本身仅相当于一个内核,其主要功能都由第三方提供扩展功能。(例如邮箱功能,用户认证等部分)。
其WSGI工具箱采用Werkzeug(路由模块),模板引擎采用jinja2(django有自己的引擎模板,但是flask作为一款轻量级框架怎么可能有这些东西),这两个也是flask的核心。
1.flask常用扩展
Flask-SQLalchemy:操作数据库,ORM;
Flask-script:终端脚本工具,脚手架;
Flask-migrate:管理迁移数据库;
Flask-Session:Session存储方式指定;
Flask-WTF:表单;
Flask-Mail:邮件;
Flask-Bable:提供国际化和本地化支持,翻译;
Flask-Login:认证用户状态;
Flask-OpenID:认证, OAuth;
Flask-RESTful:开发REST API的工具;
Flask JSON-RPC: 开发rpc远程服务[过程]调用
Flask-Bootstrap:集成前端Twitter Bootstrap框架
Flask-Moment:本地化日期和时间
Flask-Admin:简单而可扩展的管理接口的框架
总而言之我们所需要的大部分功能,flask的第三方扩展库都为我们准备好了,那么我们就仅仅需要解压使用。
2.安装与第一个flask项目
就像我们使用django框架一样,推荐大家使用虚拟环境来进行项目。
mkvirtualenv flask -p python3 #创建虚拟环境 virtualenv demo #切换到所创建的相应名为demo的虚拟环境下
pip install flask==0.12.5 #我们选择的是更加贴近实战开发的版本
2.创建flask项目:
与django不同的是,我们在创建flask项目的时候目录结构以及大部分自动化的东西我们都需要手动创建。
from flask import Flask #导入模块 app = Flask(__name__) #创建应用 @app.route("/") #最基础的路由创建方式 def index(): #基础的视图模式 return "hello" if __name__ =="__main__": #这个想必不用多说,运行项目 app.run(ip,port) #通过传参可以用来指定端口和ip号
这大概应该就是各位见过最简单的flask项目了
我们在使用不同配置的时候是需要进行人为导入的,接下来介绍如何导入文件
... class Config(): '''配置类''' DEBUG = True #最基础的配置文件导入,debug模式下我们修改代码后自动重启,默认 #是关闭的 app.config.from_object(Config) #通过这种方式将配置加载到应用中 ...
'''创建路由的时候名称必须全局唯一,不能出现重复,否则会报错''' @app.route("/") #最基础的路由创建方式 def index(): #基础的视图模式 return "hello"
路由传参数
@app.route("/user/<user_id>") #通过这种方式我们可以向视图中传递参数 @app.route("/user/<string:user_id>") #通过这种方式我们传递参数的时候就有了限制
转换器名称 | 描述 |
---|---|
string | 默认类型,接受不带斜杠的任何文本 |
int | 接受正整数 |
float | 接受正浮点值 |
path | 接收string 但也接受斜线 |
uuid | 接受UUID(通用唯一识别码)字符串 xxxx-xxxx-xxxxx-xxxxx |
路由限定请求方式
from flask import Flask,request #flask本身没有默认防范跨域攻击的,但是我们有强大的第三方扩展库 ... @app.route(rule = "/add",methods = ["post"])
正则路由限制:
这里确实值得我们单个拿出来进行书写,当我们对我们路由携带参数有一定规则上的限制的时候我们该如何去做呢???
首先我们要导入转换器基类:在flask中,所有的路由匹配规则都是使用转换器进行的;
然后我们需要自定义转换器:就行我们一直提到的,flask几乎自己什么都做不了,但却阻挡不了开发者们,没有的话我们自己动手做;
之后添加转换器到默认的转换器字典中;
最后使用它吧,实现我们自定义的匹配规则。
from werkzeug.routing import BaseConverter #导入转换器基类 class RegexConverter(BaseConverter): '''继承并拓展转换器奥''' def __init__(self,map,*args): super().__init__(map) #我们只是在原基础上进行拓展所以之前的也需要继承下来 self.regex = "1[3-9]\d{9}" #self.regex = args[0] 我们也可以将正则写在路由中 app.url_map.coverters["re"] = RegexConverter #添加到默认转换器 @app.route(rule='/user/<re:mobile>') #使用转换器 #@app.route(rule='/user/<re("\w+@\w+\.\w+"):email>')
5.Http的请求和相应
作为一款web开发框架,哪怕再轻量级request也是不可获取的一个模块
属性 | 说明 | 类型 |
---|---|---|
data | 记录请求体的数据,并转换为字符串 只要是通过其他属性无法识别转换的请求体数据 最终都是保留到data属性中 | bytes类型 |
form | 记录请求中的表单数据 | MultiDict |
args | 记录请求中的查询字符串 | MultiDict |
cookies | 记录请求中的cookie信息 | Dict |
headers | 记录请求中的请求头 | EnvironHeaders |
method | 记录请求使用的HTTP方法 | GET/POST |
url | 记录请求的URL地址 | string |
files | 记录请求上传的文件列表 | * |
json | 记录ajax请求的json数据 | json |
from flask import Flask,request # 初始化 app = Flask(import_name=__name__) # 编写路由视图 @app.route(rule='/') def index(): return "<h1>hello world!</h1>" """== 获取查询字符串 ==""" @app.route(rule="/args",methods=["post","get"]) def args(): print(request.args) # 获取查询字符串 """ 请求地址: http://127.0.0.1:5000/args?name=xiaoming&password=123&lve=swimming&lve=shopping 打印效果: ImmutableMultiDict([('name', 'xiaoming'), ('password', '123')]) ImmutableMultiDict是一个由flask封装的字典类,在字典的基础上,提供了一些其他的方法而已。 格式: ImmutableMultiDict([('键', '值'), ('键', '值')]) 字典本身不支持同名键的,ImmutableMultiDict类解决了键同名问题 操作ImmutableMultiDict,完全可以操作字典操作,同时还提供了get,getlist方法,获取指定键的1个值或多个值 """ print(request.args["name"]) # xiaoming print(request.args.get("name")) # xiaoming print(request.args.getlist("lve")) # ['swimming', 'shopping'] # 把ImmutableMultiDict转换成普通字典 print(request.args.to_dict(flat=False)) # {'name': ['xiaoming'], 'password': ['123'], 'lve': ['swimming', 'shopping']} print(request.args.to_dict(flat=True)) # {'name': 'xiaoming', 'password': '123', 'lve': 'swimming'} return "ok" """== 获取请求体数据 ==""" @app.route(rule="/data",methods=["post","put","patch"]) def data(): """接受客户端发送过来的请求体数据,是request.json,request.form,request.files等无法接受的数据,全部会保留到这里""" print(request.data) # # 接受表单提交的数据 print(request.form) # ImmutableMultiDict([('username', 'root'), ('password', '123456')]) # 接受ajax或其他客户端提交过来的json数据 print(request.json) # {'username': 'root', 'password': '123456'} # 接受上传文件 avatar = request.files["avatar"] # ImmutableMultiDict([('avatar', <FileStorage: '123.jpg' ('image/jpeg')>)]) print(avatar) # <FileStorage: '123.jpg' ('image/jpeg')> # 获取请求头信息 print( request.headers ) # 获取全部的而请求头信息 print( request.headers.get("Host") ) # 获取自定义请求头 print( request.headers.get("company") ) # oldboy print( request.headers["company"] ) # oldboy # 本次请求的url地址 print( request.url) # http://127.0.0.1:5000/data print( request.path ) # /data return "ok" # 声明和加载配置 class Config(): DEBUG = True app.config.from_object(Config) if __name__ == '__main__': # 运行flask app.run(host="0.0.0.0")
6.响应
flask支持两种响应方式,数据响应:默认响应HTML文本,也可以返回json格式;页面重定向url_for。响应的时候,flask也支持自定义http响应状态码
#响应HTML文本 ... @app.route("/") def index(): return "<html文本>" #响应json from flask import Flask, request, jsonify # jsonify 就是json里面的jsonify @app.route("/") def index(): # 也可以响应json格式代码 data = [ {"id":1,"username":"liulaoshi","age":18}, {"id":2,"username":"liulaoshi","age":17}, {"id":3,"username":"liulaoshi","age":16}, {"id":4,"username":"liulaoshi","age":15}, ] return jsonify(data) #重定向到外网 @app.route("/user") def user(): # 页面跳转 redirect函数就是response对象的页面跳转的封装 # Location: http://www.baidu.com return redirect("http://www.baidu.com") #重定向到自己的视图函数 @app.route('/demo4') def demo4(): # 使用 url_for 生成指定视图函数所对应的 url return redirect( url_for(endpoint="user") )
我们之前提到过flask可以自定义状态码和响应头,我们可以很方便的返回自定义的状态码
@app.route('/demo') def demo(): return '状态码 666' #400 from flask import make_response @app.route("/rep") def index(): response = make_response("ok") response.headers['name'] = "良心会痛么" return response
7.http会话控制
会话指的是客户端和服务端一次完整的交互过程,开始在用户通过浏览器第一次访问服务端网站开始,结束是在关闭浏览器是与服务端断开。
所谓会话控制就是在客户端与服务端之间进行多次http请求响应之间记录跟踪和识别用户信息。
无状态:一次用户请求时,浏览器服务器无法知道之前用户的行为,每次多事新的请求
原因:我们总说无状态,无状态五五开...那么原因是什么呢?我们使用的大多说框架虽然请求和相应是封装为更高级的方法像是wsgi等,但是底层都是socket套接字,一次请求后服务器将请求结果发送回浏览器之后会关闭当前的socket连接,下一次连接的时候就又是一次新的连接了。
有时我们需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等
实现状态保持主要有两种方式:
-
在客户端存储信息使用
Cookie,token[jwt,oauth]
-
在服务器端存储信息使用
Session
8.Cookie
Cookie是由服务器端生成,发送给客户端浏览器,浏览器会将Cookie的key/value保存,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookie的key/value可以由服务器端自己定义。
使用场景: 登录状态, 浏览历史, 网站足迹,购物车 [不登录也可以使用购物车]
Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用
Cookie基于域名安全,不同域名的Cookie是不能互相访问的
如访问luffy.com时向浏览器中写了Cookie信息,使用同一浏览器访问baidu.com时,无法访问到luffy.com写的Cookie信息
浏览器的同源策略针对cookie也有限制作用.
当浏览器请求某网站时,会将本网站下所有Cookie信息提交给服务器,所以在request中可以读取Cookie信息
设置cookie
设置cookie需要通过flask的Response响应对象来进行设置,由响应对象会提供了方法set_cookie给我们可以快速设置cookie信息。
from flask imoprt Flask,make_response @app.route('/set_cookie') def set_cookie(): resp = make_response('this is to set cookie') resp.set_cookie('username', 'xiaoming', max_age=3600) return resp
9.session
对于敏感、重要的信息,建议要存储在服务器端,不能存储在浏览器中,如用户名、余额、等级、验证码等信息
在服务器端进行状态保持的方案就是Session
Session依赖于Cookie,session的ID一般默认通过cookie来保存到客户端。
flask中的session需要加密,所以使用session之前必须配置SECRET_KEY选项,否则报错.
session的有效期默认是会话期,会话结束了,session就废弃了。
如果将来希望session的生命周期延长,可以通过修改cookie中的sessionID来完成配置。
设置session
from flask import session @app.route('/set_session') def set_session(): session['username'] = 'xiaoming' return 'ok!' #也可以用session.get来获取
后文继续