如何理解wsgi, Werkzeug, flask之間的關(guān)系
Flask是一個(gè)基于Python開(kāi)發(fā)并且依賴(lài)jinja2模板和Werkzeug WSGI服務(wù)的一個(gè)微型框架,對(duì)于Werkzeug朵耕,它只是工具包缠局,其用于接收http請(qǐng)求并對(duì)請(qǐng)求進(jìn)行預(yù)處理泥兰,然后觸發(fā)Flask框架框杜,開(kāi)發(fā)人員基于Flask框架提供的功能對(duì)請(qǐng)求進(jìn)行相應(yīng)的處理,并返回給用戶(hù),如果要返回給用戶(hù)復(fù)雜的內(nèi)容時(shí)障本,需要借助jinja2模板來(lái)實(shí)現(xiàn)對(duì)模板的處理。將模板和數(shù)據(jù)進(jìn)行渲染响鹃,將渲染后的字符串返回給用戶(hù)瀏覽器驾霜。
快速入門(mén)
最小應(yīng)用代碼實(shí)現(xiàn)
from flask import Flask 這個(gè)類(lèi)的實(shí)例是我們的 WSGI 應(yīng)用程序
app = Flask(__name__) 創(chuàng)建一個(gè)該類(lèi)的實(shí)例,第一個(gè)參數(shù)是應(yīng)用模塊或者包的名稱(chēng)买置。
app.debug = True 如果你啟用了調(diào)試支持粪糙,服務(wù)器會(huì)在代碼修改后自動(dòng)重新載入,并在發(fā)生錯(cuò)誤時(shí)提供一個(gè)相當(dāng)有用的調(diào)試器忿项。
@app.route('/') 使用 route() 裝飾器告訴 Flask 什么樣的URL 能觸發(fā)我們的函數(shù)猜旬。
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
啟用調(diào)試模式
app.debug = True 如果你啟用了調(diào)試支持,服務(wù)器會(huì)在代碼修改后自動(dòng)重新載入倦卖,并在發(fā)生錯(cuò)誤時(shí)提供一個(gè)相當(dāng)有用的調(diào)試器洒擦。
或者: app.run(debug=True)
路由
Flask 的 URL 規(guī)則基于 Werkzeug 的路由模塊。這個(gè)模塊背后的思想是基于 Apache 以及更早的 HTTP 服務(wù)器主張的先例怕膛,保證優(yōu)雅且唯一的 URL熟嫩。
app.route() 裝飾器把一個(gè)函數(shù)綁定到對(duì)應(yīng)的 URL 上。
@app.route('/')
def index():
return 'Index Page'
@app.route('/hello')
def hello():
return 'Hello World'
變量規(guī)則
要給 URL 添加變量部分褐捻,你可以把這些特殊的字段標(biāo)記為 <variable_name> 掸茅, 這個(gè)部分將會(huì)作為命名參數(shù)傳遞到你的函數(shù)椅邓。 規(guī)則可以用 <converter:variable_name> 指定一個(gè)可選的轉(zhuǎn)換器。
@app.route('/user/<username>')
def show_user_profile(username):
# show the user profile for that user
return 'User %s' % username
@app.route('/post/<int:post_id>')
def show_post(post_id):
# show the post with the given id, the id is an integer
return 'Post %d' % post_id
構(gòu)造 URL 好處:它允許你一次性修改 URL昧狮, 而不是到處邊找邊改景馁。
你可以用 url_for() 來(lái)給指定的函數(shù)構(gòu)造 URL。 它接受函數(shù)名作為第一個(gè)參數(shù)逗鸣,也接受對(duì)應(yīng) URL 規(guī)則的變量部分的命名參數(shù)合住。未知變量部分會(huì)添加到 URL 末尾作為查詢(xún)參數(shù)。
>>> with app.test_request_context():
... print url_for('index')
... print url_for('login')
... print url_for('login', next='/')
... print url_for('profile', username='John Doe')
...
/
/login
/login?next=/
/user/John%20Doe
HTTP 方法
默認(rèn)情況下撒璧,路由只回應(yīng) GET 請(qǐng)求透葛,但是通過(guò) route() 裝飾器傳遞 methods 參數(shù)可以改變這個(gè)行為。
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
do_the_login()
else:
show_the_login_form()
靜態(tài)文件
給靜態(tài)文件生成地址
在你的包中或是模塊的所在目錄中創(chuàng)建一個(gè)名為 static 的文件夾卿樱,在應(yīng)用中使用 /static 即可訪問(wèn)僚害。
給靜態(tài)文件生成 URL ,使用特殊的 'static' 端點(diǎn)名:
url_for('static', filename='style.css')
這個(gè)文件應(yīng)該存儲(chǔ)在文件系統(tǒng)上的 static/style.css
模板渲染
可以使用 render_template() 方法來(lái)渲染模板 代碼實(shí)現(xiàn)
from flask import render_template
@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
return render_template('hello.html', name=name)
模板文件
<!doctype html>
<title>Hello from Flask</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello World!</h1>
{% endif %}
請(qǐng)求對(duì)象
當(dāng)前請(qǐng)求的 HTTP 方法可通過(guò) method 屬性來(lái)訪問(wèn)繁调。通過(guò):attr:~flask.request.form 屬性來(lái)訪問(wèn)表單數(shù)據(jù)( POST 或 PUT 請(qǐng)求提交的數(shù)據(jù))
你可以通過(guò) args 屬性來(lái)訪問(wèn) URL 中提交的參數(shù) ( ?key=value ):
searchword = request.args.get('q', '')
@app.route('/login', methods=['POST', 'GET'])
def login():
error = None
if request.method == 'POST': 通過(guò)method方法獲取請(qǐng)求方法
if valid_login(request.form['username'], 通過(guò)request.form 方法獲取表單數(shù)據(jù)
request.form['password']):
return log_the_user_in(request.form['username'])
else:
error = 'Invalid username/password'
# the code below is executed if the request method
# was GET or the credentials were invalid
return render_template('login.html', error=error)
文件上傳
用 Flask 處理文件上傳很簡(jiǎn)單萨蚕。只要確保你沒(méi)忘記在 HTML 表單中設(shè)置 enctype="multipart/form-data" 屬性,不然你的瀏覽器根本不會(huì)發(fā)送文件蹄胰。
from flask import request
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['the_file'] 獲取文件
f.save('/var/www/uploads/uploaded_file.txt') 保存文件
...
from flask import request
from werkzeug import secure_filename
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['the_file'] 獲取文件
f.save('/var/www/uploads/' + secure_filename(f.filename)) 安全獲取文件的名稱(chēng)
Cookie
設(shè)置cookie
from flask import make_response
@app.route('/')
def index():
resp = make_response(render_template(...))
resp.set_cookie('username', 'the username')
return resp
讀取cookie
from flask import request
@app.route('/')
def index():
username = request.cookies.get('username')
# use cookies.get(key) instead of cookies[key] to not get a
# KeyError if the cookie is missing.
重定向和錯(cuò)誤
from flask import abort, redirect, url_for
@app.route('/')
def index():
return redirect(url_for('login')) 重定向
@app.route('/login')
def login():
abort(401) 錯(cuò)誤
this_is_never_executed()
定制錯(cuò)誤頁(yè)面
from flask import render_template
@app.errorhandler(404)
def page_not_found(error):
return render_template('page_not_found.html'), 404
flask響應(yīng)方式三種
如果返回的是一個(gè)合法的響應(yīng)對(duì)象岳遥,它會(huì)從視圖直接返回。
如果返回的是一個(gè)字符串烤送,響應(yīng)對(duì)象會(huì)用字符串?dāng)?shù)據(jù)和默認(rèn)參數(shù)創(chuàng)建。
如果返回的是一個(gè)元組糠悯,且元組中的元素可以提供額外的信息帮坚。這樣的元組必須是 (response, status, headers) 的形式,且至少包含一個(gè)元素互艾。 status 值會(huì)覆蓋狀態(tài)代碼试和, headers 可以是一個(gè)列表或字典,作為額外的消息標(biāo)頭值纫普。
如果上述條件均不滿(mǎn)足阅悍, Flask 會(huì)假設(shè)返回值是一個(gè)合法的 WSGI 應(yīng)用程序,并轉(zhuǎn)換為一個(gè)請(qǐng)求對(duì)象昨稼。
@app.errorhandler(404)
def not_found(error):
resp = make_response(render_template('error.html'), 404)
resp.headers['X-Something'] = 'A value'
return resp
會(huì)話session
在 Cookies 的基礎(chǔ)上實(shí)現(xiàn)的节视,并且對(duì) Cookies 進(jìn)行密鑰簽名
from flask import Flask, session, redirect, url_for, escape, request
app = Flask(__name__)
@app.route('/')
def index():
if 'username' in session:
return 'Logged in as %s' % escape(session['username'])
return 'You are not logged in'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
session['username'] = request.form['username'] 登錄設(shè)置session值
return redirect(url_for('index'))
return '''
<form action="" method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
'''
@app.route('/logout')
def logout():
# remove the username from the session if it's there
session.pop('username', None) 退出登錄,刪除session值假栓,去掉登錄狀態(tài)
return redirect(url_for('index'))
# set the secret key. keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' session簽名隨機(jī)密碼
消息閃現(xiàn)
消息日志記錄
日志記錄格式
app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')
代碼實(shí)現(xiàn)
import logging
from flask import Flask
app = Flask(__name__)
app.debug = True
@app.route('/')
def hello_world():
try:
j = 1/0
except:
app.logger.debug("message_info") 記錄日志
print "haha"
return 'Hello World!'
if __name__ == '__main__':
handler = logging.FileHandler('flask.log', encoding='UTF-8')
handler.setLevel(logging.DEBUG)
logging_format = logging.Formatter(
'%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s')
handler.setFormatter(logging_format)
app.logger.addHandler(handler)
app.run('localhost',9999)
可插撥視圖
主要用途:可定制寻行,可插撥的視圖來(lái)替代部分實(shí)現(xiàn)
第一步 :創(chuàng)建一個(gè)flask.views.View 的子類(lèi),并且引入dispatch_request()方法
第二步:通過(guò)使用as_view()方法匾荆,把類(lèi)轉(zhuǎn)換成實(shí)際視圖函數(shù)
from flask.views import View
class ShowUsers(View):
def dispatch_request(self):
users = User.query.all()
return render_template('users.html', objects=users)
app.add_url_rule('/users/', view_func=ShowUsers.as_view('show_users')) 傳遞給函數(shù)的 字符串是最終視圖的名稱(chēng)
也可以基于http方法調(diào)度:
from flask.views import MethodView
class UserAPI(MethodView):
def get(self):
users = User.query.all()
...
def post(self):
user = User.from_form_data(request.form)
...
app.add_url_rule('/users/', view_func=UserAPI.as_view('users'))
藍(lán)圖
基本概念:藍(lán)圖被注冊(cè)到應(yīng)用之后拌蜘。所要執(zhí)行的操作的集合杆烁,F(xiàn)lask 會(huì)把藍(lán)圖和視圖函數(shù)關(guān)聯(lián)起來(lái),并生成兩個(gè)端點(diǎn)之前的 URL 。
藍(lán)圖blueprint简卧,實(shí)現(xiàn)應(yīng)用模塊化兔魂,使用藍(lán)圖讓?xiě)?yīng)用層次清晰,開(kāi)發(fā)者可以更容易開(kāi)發(fā)举娩,和維護(hù)項(xiàng)目
藍(lán)圖析校,通常作用于相同的URL地址前綴,他們可以放在同一個(gè)模塊中晓铆。
藍(lán)圖可以極大簡(jiǎn)化應(yīng)用并為擴(kuò)展提供集中的注冊(cè)入口勺良。
blueprint對(duì)象和flask應(yīng)用對(duì)象的工作方式類(lèi)似,但不是一個(gè)真正的應(yīng)用骄噪。它更像一個(gè)用于構(gòu)建和擴(kuò)展應(yīng)用的藍(lán)圖
為什么使用藍(lán)圖:
- 把一個(gè)應(yīng)用分解為一套藍(lán)圖尚困。這是針對(duì)大型應(yīng)用的理想方案:一個(gè)項(xiàng)目可以實(shí)例化一個(gè) 應(yīng)用,初始化多個(gè)擴(kuò)展,并注冊(cè)許多藍(lán)圖。
- 在一個(gè)應(yīng)用的 URL 前綴和(或)子域上注冊(cè)一個(gè)藍(lán)圖链蕊。 URL 前綴和(或)子域的參數(shù) 成為藍(lán)圖中所有視圖的通用視圖參數(shù)(缺省情況下)事甜。
- 使用不同的 URL 規(guī)則在應(yīng)用中多次注冊(cè)藍(lán)圖。
- 通過(guò)藍(lán)圖提供模板過(guò)濾器滔韵、靜態(tài)文件逻谦、模板和其他工具。藍(lán)圖不必執(zhí)行應(yīng)用或視圖 函數(shù)陪蜻。
- 藍(lán)圖的缺點(diǎn)是一旦應(yīng)用被創(chuàng)建后,只有銷(xiāo)毀整個(gè)應(yīng)用對(duì)象才能注銷(xiāo)藍(lán)圖邦马。
藍(lán)圖實(shí)例:show.py
from flask import Blueprint, render_template, abort
from jinja2 import TemplateNotFound
simple_page = Blueprint('simple_page', __name__, template_folder='templates')
@simple_page.route('/', defaults={'page': 'index'}) 使用此裝飾器時(shí),藍(lán)圖會(huì)記錄下所登記的show函數(shù)
@simple_page.route('/<page>')
def show(page):
try:
return render_template('pages/%s.html' % page)
except TemplateNotFound:
abort(404)
當(dāng)以后注冊(cè)藍(lán)圖時(shí)宴卖,這個(gè)函數(shù)會(huì)被注冊(cè)到應(yīng)用中滋将,它會(huì)把 構(gòu)建 Blueprint 時(shí)所使用的名稱(chēng)(在本例為 simple_pa ge )作為函數(shù)端點(diǎn) 的前綴。
可以這樣注冊(cè)藍(lán)圖:
# app.py
from flask import Flask
from user.simple_page import simple_page
app = Flask(__name__)
app.register_blueprint(simple_page)
注冊(cè)后形成的url--視圖映射規(guī)則:
[<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
<Rule '/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
<Rule '/' (HEAD, OPTIONS, GET) -> simple_page.show>]
藍(lán)圖還可以掛接到不同的位置:
app.register_blueprint(simple_page, url_prefix='/pages')
這樣就會(huì)形成如下規(guī)則:
[<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
<Rule '/pages/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
<Rule '/pages/' (HEAD, OPTIONS, GET) -> simple_page.show>]
總之,你可以多次注冊(cè)藍(lán)圖,但是不一定每個(gè)藍(lán)圖都能正確響應(yīng)症昏。是否能夠多次注冊(cè)實(shí)際 上取決于你的藍(lán)圖是如 何編寫(xiě)的,是否能根據(jù)不同的位置做出正確的響應(yīng)随闽。
藍(lán)圖資源
靜態(tài)文件:
藍(lán)圖的第三個(gè)參數(shù)是 static_folder 。
這個(gè)參數(shù)用以指定藍(lán)圖的靜態(tài)文件所在的文件夾,它可以是一個(gè)絕對(duì)路徑也 可以是相對(duì)路徑肝谭。:
admin = Blueprint('admin', __name__, static_folder='static')
模板:
使用藍(lán)圖來(lái)暴露模板,那么可以使用 Blueprint 的 template_folder 參數(shù):
admin = Blueprint('admin', __name__, template_folder='templates') 分絕對(duì)和相對(duì)
理解上下文(上下文是環(huán)境的一個(gè)快照掘宪,是一個(gè)用來(lái)保存狀態(tài)的對(duì)象)
flask分應(yīng)用上下文和請(qǐng)求上下文
current_app = LocalProxy(_find_app) 應(yīng)用上下文,當(dāng)前app實(shí)例對(duì)象
g = LocalProxy(partial(_lookup_app_object, 'g')) 應(yīng)用上下文攘烛,用作零時(shí)存儲(chǔ)的對(duì)象
每個(gè)請(qǐng)求的g都是獨(dú)立的魏滚,并且在整個(gè)請(qǐng)求內(nèi)都是可訪問(wèn)修改的。
對(duì)g對(duì)象調(diào)用過(guò)程如下:
訪問(wèn)g-->從當(dāng)前線程的應(yīng)用上下文棧頂獲取應(yīng)用上下文-->取出其中的g對(duì)象-->進(jìn)行操作坟漱。
所以可以通過(guò)一個(gè)g對(duì)象而讓所有線程互不干擾的訪問(wèn)自己的g栏赴。
request = LocalProxy(partial(_lookup_req_object, 'request')) 請(qǐng)求上下文,封裝了客戶(hù)端發(fā)出的http請(qǐng)求
session = LocalProxy(partial(_lookup_req_object, 'session')) 請(qǐng)求上下文,存儲(chǔ)用戶(hù)對(duì)話
本地線程
Thread Local 希望不同的線程對(duì)于內(nèi)容的修改只在線程內(nèi)發(fā)揮作用须眷,線程間互不影響
原理:threading.current_thread().dict里添加一個(gè)包含對(duì)象mydata的id的值竖瘾,保存不同線程的狀態(tài)
Werkzeug Local
Werkzeug 使用自定義storage保存不同線程下的狀態(tài)
? 提供釋放本地線程的release_local方法
? 獲取get_ident函數(shù),獲取線程或者協(xié)程標(biāo)識(shí)符
Werkzeug 兩種數(shù)據(jù)結(jié)構(gòu)
LocalStack : 基于Werkzeug.local.local 實(shí)現(xiàn)的棧結(jié)構(gòu)花颗,可以將對(duì)象推入和彈出捕传,可以快速拿到棧頂對(duì)象。
LocalProxy:標(biāo)準(zhǔn)代理模式扩劝,這個(gè)函數(shù)執(zhí)行之后就是通過(guò)LocalStack實(shí)例化的棧頂?shù)臈m攲?duì)象庸论,對(duì)于LocalProxy對(duì)象的操作都會(huì)轉(zhuǎn)發(fā)到這個(gè)棧頂對(duì)象。
理解flask.request
源碼globles.py 文件
from functools import partial
from werkzeug.local import LocalStack, LocalProxy
def _lookup_req_object(name):
top = _request_ctx_stack.top _request_ctx_stack是底層一個(gè)請(qǐng)求上下文棧結(jié)構(gòu)
if top is None:
raise RuntimeError(_request_ctx_err_msg)
return getattr(top, name)
request = LocalProxy(partial(_lookup_req_object, 'request'))
請(qǐng)求上下文request流程如下:
1棒呛,請(qǐng)求產(chǎn)生
2键科,請(qǐng)求過(guò)程中向 _request_ctx_stack推入請(qǐng)求上下文對(duì)象邑茄,這個(gè)請(qǐng)求上下文會(huì)變成棧頂帆啃,request就會(huì)成為這個(gè)請(qǐng)求上下文擒悬,也就包含了這次請(qǐng)求相關(guān)的信息和數(shù)據(jù)。
3趋观,在視圖函數(shù)使用request就可以使用request.args.get('name')
flask擴(kuò)展生態(tài)
- Flask擴(kuò)展系列(一)–Restful
- Flask擴(kuò)展系列(二)–Mail
- Flask擴(kuò)展系列(三)–國(guó)際化I18N和本地化L10N
- Flask擴(kuò)展系列(四)–SQLAlchemy
- Flask擴(kuò)展系列(五)–MongoDB
- Flask擴(kuò)展系列(六)–緩存
- Flask擴(kuò)展系列(七)–表單
- Flask擴(kuò)展系列(八)–用戶(hù)會(huì)話管理
- Flask擴(kuò)展系列(九)–HTTP認(rèn)證
- Flask擴(kuò)展系列–自定義擴(kuò)展
Flask-Script 添加運(yùn)行服務(wù)器扛禽,設(shè)置數(shù)據(jù)庫(kù),定制shell等功能的命令皱坛。
Flask-WTF 處理web表單
Flask-Sqlalchemy 關(guān)系型數(shù)據(jù)庫(kù)擴(kuò)展
Flask-RESTful 實(shí)現(xiàn)一個(gè)Restful的請(qǐng)求
Flask-Mail 發(fā)送郵件
Flask-Babel 國(guó)際化I18N和本地化L10N
Flask-PyMongo 使用MongoDB數(shù)據(jù)庫(kù)
Flask-Cache 使用緩存
Flask-Login 登錄管理
Flask-HTTPAuth 認(rèn)證
Flask-Admin 是一個(gè)簡(jiǎn)單易用的Flask擴(kuò)展编曼,讓你可以很方便并快速地為Flask應(yīng)用程序增加管理界面。
Flask-DebugToolbar 內(nèi)置調(diào)試工具
Flask-Migrate 數(shù)據(jù)庫(kù)遷移框架
Flask-Security 提供角色管理剩辟,權(quán)限管理掐场,用戶(hù)登錄,郵箱驗(yàn)證贩猎,密碼重置熊户,密碼加密
flask 鉤子函數(shù)使用方法
before_first_request
注冊(cè)一個(gè)函數(shù),在處理第一個(gè)請(qǐng)求之前運(yùn)行,只運(yùn)行一次融欧,后面不再運(yùn)行
示例:
@app.before_first_request
def bf_first_request():
g.string = 'before_first_request'
before_request
注冊(cè)一個(gè)函數(shù),在處理每次請(qǐng)求之前運(yùn)行.每次都運(yùn)行
@app.before_request和@app.teardown_request的組合可以用于打開(kāi)一個(gè)資源和關(guān)閉一個(gè)資源敏弃,
@app.before_request
def bf_request():
g.string = 'before_request'
after_request
注冊(cè)一個(gè)函數(shù),在每次請(qǐng)求之后運(yùn)行.注冊(cè)的函數(shù)至少需要含有一個(gè)參數(shù),這個(gè)參數(shù)實(shí)際上為服務(wù)器的響應(yīng),且函數(shù)中需要返回這個(gè)響應(yīng)參數(shù).
裝飾器修飾的函數(shù)接收一個(gè)響應(yīng)對(duì)象卦羡,我們拿到了這個(gè)響應(yīng)對(duì)象后可以對(duì)內(nèi)容進(jìn)行修改(雖然很少見(jiàn))還可以在上面追加COOKIE進(jìn)行返回噪馏。
@app.after_request
def af_request(param):
return param 接收這個(gè)參數(shù),然后要返回
param是服務(wù)器的響應(yīng)
示例:
{'_on_close': [], 'response': ['Hello World!'], 'headers': Headers([('Content-Type', u'text/html; charset=utf-8'), ('Content-Length', u'12')]), '_status_code': 200, '_status': '200 OK', 'direct_passthrough': False}
teardown_request
會(huì)在每個(gè)請(qǐng)求后執(zhí)行绿饵,請(qǐng)求發(fā)生異常時(shí)也會(huì)被調(diào)用欠肾,處理函數(shù)必須傳入?yún)?shù)接收異常信息
@app.teardown_request修飾的函數(shù)在非調(diào)試模式下才能在出現(xiàn)異常時(shí)被執(zhí)行
@app.teardown_request
def teardown_request(error):
print("請(qǐng)求之后調(diào)用")
app.errorhandler
裝飾器用于錯(cuò)誤處理
傳入裝飾器的值為一錯(cuò)誤狀態(tài)碼,修飾的函數(shù)必須要有一個(gè)參數(shù)用于接收錯(cuò)誤拟赊,為了測(cè)試500的效果必須關(guān)閉調(diào)試模式刺桃!
@app.errorhandler(404)
def notFound(error):
return "404"
@app.errorhandler(500)
def notFound(error):
return "500"