一文掌握Python Flask:HTTP微服务开发从入门到部署
Flask是一个轻量级的Python Web框架,以其"微内核"设计哲学闻名于世。所谓"微"并非指功能简单,而是指核心简洁、高度可扩展——Flask只提供最基础的Web服务能力,其他所有功能都可通过丰富的扩展生态系统按需添加。这种设计让开发者能够从几行代码的简单应用开始,逐步构建出复杂的企业级系统。
一、依赖库的安装与配置
1. 环境准备
首先确保已安装Python(建议版本3.7+),然后通过pip安装Flask依赖:
# 安装 Flask pip install flask # 如果系统中有多个 Python 版本,可能需要使用 pip3 pip3 install flask 2. 验证安装
安装完成后,创建一个简单的应用来验证 Flask 是否正确安装并正常工作:
# main.py# 导入Flask框架,Flask是一个轻量级的Python Web框架 from flask import Flask # 创建一个Flask应用程序实例 # __name__ 参数让Flask知道在哪里查找模板和静态文件等资源 app = Flask(__name__)# 使用装饰器定义路由,将URL路径'/'与下面的函数绑定 # methods参数显式指定只允许GET请求(GET是默认值,这里显式声明更清晰) @app.route('/', methods=['GET'])defhello():""" 处理根路径GET请求的处理函数 当用户访问网站根路径时,此函数会被调用 """# 返回响应内容,浏览器将显示此字符串 return"Flask安装成功!"if __name__ =='__main__':# 运行Flask应用程序 # debug=True 表示开启调试模式: # 1. 代码修改后服务器会自动重启 # 2. 发生错误时浏览器会显示详细错误信息 # 3. 仅用于开发环境,生产环境应设置为False app.run(debug=True)运行上述代码后访问 http://127.0.0.1:5000,看到"Flask安装成功!"即表示安装正确。

二、基础HTTP接口实现
掌握 Flask 基础安装后,将学习如何实现常用的 HTTP 接口。涵盖 GET 和 POST 两种最常用的 HTTP 方法。
1. GET 接口
GET 请求用于从服务器获取数据,是最常用的 HTTP 方法。Flask 提供了简洁的路由装饰器语法来处理不同类型的 GET 请求。
1.1 不带参数的 GET 接口
以下举例,演示了最基本的路由定义,不接收任何参数,返回固定的 JSON 数据:
# main.py# 导入Flask框架及其必要的组件 # Flask: 主框架类,用于创建Web应用实例 # jsonify: 将Python字典转换为JSON格式的HTTP响应 # request: 用于处理HTTP请求对象,可获取请求参数、头部等信息 from flask import Flask, jsonify, request # 创建Flask应用实例 # __name__ 表示当前模块名,Flask会根据它确定应用根目录,以便查找模板、静态文件等资源 app = Flask(__name__)# GET /api/hello @app.route('/api/hello', methods=['GET'])defhello_world():""" 最简单的GET接口示例:不接收任何参数,返回固定的欢迎消息。常用于接口连通性测试。 """# 使用jsonify函数创建JSON格式的HTTP响应 # 返回一个包含多条信息的字典 # 浏览器会自动将JSON数据格式化显示 return jsonify({'message':'Hello, World!',# 主要消息内容 'method':'GET',# 请求方法,方便客户端识别 'status':'success'# 操作状态,常见值:success/error })if __name__ =='__main__':# 参数说明: # debug=True: 启用调试模式 # 优点:1. 代码修改后自动重启服务 # 2. 出错时显示详细错误信息 # 3. 显示交互式调试器(仅本地访问) # 注意:生产环境必须设为False # port=5000: 指定服务运行的端口号 app.run(debug=True, port=5000)使用示例:
- 访问
http://127.0.0.1:5000/api/hello

1.2 带路径和查询参数的 GET 接口
实际开发中,GET 请求常需要接收参数。Flask 支持两种主要的参数传递方式:路径参数和查询参数。
# main.py# 导入Flask框架及其必要的组件 # Flask: 主框架类,用于创建Web应用实例 # jsonify: 将Python字典转换为JSON格式的HTTP响应 # request: 用于处理HTTP请求对象,可获取请求参数、头部等信息 from flask import Flask, jsonify, request # 创建Flask应用实例 # __name__ 表示当前模块名,Flask会根据它确定应用根目录,以便查找模板、静态文件等资源 app = Flask(__name__)# GET /api/user/zhangsan?age=25 @app.route('/api/user/<username>', methods=['GET'])defget_user(username):""" 1. 路径参数:username来自URL路径本身 2. 查询参数:age来自URL问号后的查询字符串 """# 从请求的查询字符串中获取'age'参数 # request.args 是一个特殊的字典,包含所有查询参数 # get() 方法参数说明: # 第一个参数'age': 要获取的参数名 # 示例URL: /api/user/zhangsan?age=25 age = request.args.get('age', default=18,type=int)# 返回JSON格式的响应,包含用户信息 # 使用f-string格式化字符串,动态插入变量值 return jsonify({'username': username,# 从路径获取的用户名 'age': age,# 从查询参数获取的年龄 'message':f'用户 {username} 的信息',# 包含用户名的描述信息 'method':'GET',# 请求方法,方便客户端识别 'status':'success'# 操作状态,常见值:success/error })if __name__ =='__main__':# 参数说明: # debug=True: 启用调试模式 # 优点:1. 代码修改后自动重启服务 # 2. 出错时显示详细错误信息 # 3. 显示交互式调试器(仅本地访问) # 注意:生产环境必须设为False # port=5000: 指定服务运行的端口号 app.run(debug=True, port=5000)访问 http://127.0.0.1:5000/api/user/zhangsan?age=25

2. POST接口
POST 请求用于向服务器提交数据,通常用于创建新资源。
2.1 POST 接口处理 JSON 数据
以下举例展示如何处理 JSON 格式的 POST 请求。
# main.pyfrom flask import Flask, jsonify, request app = Flask(__name__)# POST http://127.0.0.1:5000/api/user # Headers:Content-Type: application/json # Body:{"username": "李四", "email": "[email protected]"}@app.route('/api/user', methods=['POST'])defcreate_user():""" POST请求特点: 1. 请求参数在请求体中,不在URL中显示 2. 通常用于创建、修改资源 3. 可以传输JSON、表单、文件等多种格式数据 """# 获取JSON格式的请求数据 # request.get_json() 方法: # 1. 解析请求体中的JSON数据 # 2. 自动转换为Python字典/列表 # 3. 如果请求头中Content-Type不是application/json,可设置force=True强制解析 data = request.get_json()ifnot data:# 返回错误响应 # 200: 成功 # 201: 创建成功 # 400: 客户端请求错误 # 404: 资源不存在 # 500: 服务器内部错误 return jsonify({'error':'没有提供数据'}),400# 从解析后的JSON数据中获取特定字段 username = data.get('username')# 获取用户名 email = data.get('email')# 获取邮箱 ifnot username ornot email:# 字段验证失败,返回400错误 return jsonify({'error':'用户名和邮箱是必填项'}),400# 创建用户数据(模拟数据库操作) # 在实际应用中,这里应该: # 1. 连接数据库 # 2. 检查用户名/邮箱是否已存在 # 3. 将数据插入数据库 # 4. 获取数据库生成的自增ID user_data ={'id':1,# 模拟数据库自增ID,实际应从数据库获取 'username': username,# 用户输入的用户名 'email': email,# 用户输入的邮箱 'created_at':'2024-01-01'# 创建时间,实际应从数据库获取或使用当前时间 }# 返回成功响应 # HTTP状态码 201: Created,表示资源创建成功 # 与200的区别:201明确表示创建了新的资源 return jsonify({'message':'用户创建成功',# 操作成功消息 'user': user_data,# 创建的完整用户信息 'method':'POST',# 请求方法,便于客户端识别 'status':'success'# 操作状态 }),201if __name__ =='__main__':# 参数说明: # debug=True: 启用调试模式 # 优点:1. 代码修改后自动重启服务 # 2. 出错时显示详细错误信息 # 3. 显示交互式调试器(仅本地访问) # 注意:生产环境必须设为False # port=5000: 指定服务运行的端口号 app.run(debug=True, port=5000)使用 Postman 测试:
POST http://127.0.0.1:5000/api/user Headers:Content-Type: application/json Body:{"username":"李四", "email":"[email protected]"}
2.2 POST 接口处理表单请求
除了 JSON 格式,表单提交是另一种常见的 POST 请求方式,常用于文本表单提交和文件上传。
# main.pyfrom flask import Flask, jsonify, request app = Flask(__name__)# 提交纯文本表单:# POST /api/form/user HTTP/1.1# Content-Type: application/x-www-form-urlencoded# username=lisi&[email protected]@app.route('/api/form/user', methods=['POST'])defcreate_user_form():""" 通用表单处理接口,支持两种格式 """# 获取Content-Type content_type = request.headers.get('Content-Type','').lower()# 检查是否支持的表单格式 is_form_urlencoded ='application/x-www-form-urlencoded'in content_type is_form_data ='multipart/form-data'in content_type ifnot(is_form_urlencoded or is_form_data):return jsonify({'error':'不支持的Content-Type','supported_types':['application/x-www-form-urlencoded','multipart/form-data']}),415""" POST表单请求特点: 1. 数据通过表单字段(form data)传递,而不是JSON 2. Content-Type通常为application/x-www-form-urlencoded或multipart/form-data 3. 适合HTML表单提交、文件上传等场景 4. 数据在请求体中,格式为key1=value1&key2=value2 """# 获取表单数据(两种格式都可以通过request.form获取) username = request.form.get('username') email = request.form.get('email')# 验证必填字段ifnot username or username.strip()=='':return jsonify({'error':'用户名是必填项'}),400ifnot email or email.strip()=='':return jsonify({'error':'邮箱是必填项'}),400# 处理文件上传(仅form-data格式) uploaded_files ={}if is_form_data:for filename,filein request.files.items():iffileandfile.filename:# 这里可以保存文件file.save(f'uploads/{file.filename}') uploaded_files[filename]={'filename':file.filename,'content_type':file.content_type,'size':len(file.read())}file.seek(0)# 重置文件指针# 创建用户数据(模拟数据库操作) user_data ={'id':1,'username': username.strip(),'email': email.strip(),'created_at':'2024-01-01','format':'form-data'if is_form_data else'x-www-form-urlencoded','uploaded_files': uploaded_files }# 返回成功响应:201 Created状态码return jsonify({'message':'用户创建成功','user': user_data,'method':'POST','status':'success'}),201if __name__ =='__main__':# 参数说明: # debug=True: 启用调试模式 # 优点:1. 代码修改后自动重启服务 # 2. 出错时显示详细错误信息 # 3. 显示交互式调试器(仅本地访问) # 注意:生产环境必须设为False # port=5000: 指定服务运行的端口号 app.run(debug=True, port=5000)使用 Postman 测试:
POST http://127.0.0.1:5000/api/form/user Headers:Content-Type: application/x-www-form-urlencoded Body:username=lisi&[email protected] 
使用 Postman 测试:
POST http://127.0.0.1:5000/api/form/user Content-Type: multipart/form-data;boundary=4235013262151947840 ----4235013262151947840 Content-Disposition: form-data;name="username" lisi ----4235013262151947840 Content-Disposition: form-data;name="email" [email protected] ----4235013262151947840 Content-Disposition: form-data;name="";filename="WX20240906-100536.png" Content-Type: image/png WX20240906-100536.png字节流数据 ----4235013262151947840 
三、数据库集成
现代Web应用开发中,数据库是不可或缺的核心组件。Flask作为一款轻量级Web框架,虽然自身不包含数据库功能,但通过其出色的扩展机制,可以轻松集成多种数据库系统。数据库集成主要涉及两个方面:数据模型的建立和数据的持久化存储。
1. 数据库配置与模型定义
Flask支持多种数据库系统,包括:
- SQLite:轻量级文件数据库,适合小型应用和快速原型开发
- MySQL:成熟的关系型数据库,适合中型到大型应用
本示例使用SQLite,因为它无需单独安装数据库服务器,且数据存储在单一文件中,便于开发和测试。
from flask import Flask, jsonify, request from flask_sqlalchemy import SQLAlchemy from datetime import datetime # 创建Flask应用实例 # __name__ 表示当前模块名,Flask会根据它确定应用根目录,以便查找模板、静态文件等资源 flaskApp = Flask(__name__)# 设置SQLite数据库的URI,数据库文件将保存在项目根目录下的users.db文件中 # 'sqlite:///' 表示使用SQLite数据库,'users.db'是数据库文件名 flaskApp.config['SQLALCHEMY_DATABASE_URI']='sqlite:///users.db'# 关闭SQLAlchemy的修改追踪功能:设置为False可以节省内存,避免性能警告 flaskApp.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False# 创建SQLAlchemy实例,将其绑定到Flask应用:这个实例将用于所有数据库操作 sqliteDb = SQLAlchemy(flaskApp)# 定义数据模型(User类):继承自db.Model,表示这是一个数据库模型 classUser(sqliteDb.Model):# 定义id字段:整型,主键,自动递增 id= sqliteDb.Column(sqliteDb.Integer, primary_key=True)# 定义username字段:字符串类型,最大长度80字符 # unique=True 表示用户名必须唯一 # nullable=False 表示该字段不能为空 username = sqliteDb.Column(sqliteDb.String(80), unique=True, nullable=False)# 定义email字段:字符串类型,最大长度120字符 # unique=True 表示邮箱必须唯一 # nullable=False 表示该字段不能为空 email = sqliteDb.Column(sqliteDb.String(120), unique=True, nullable=False)# 定义created_at字段:日期时间类型 # default=datetime.utcnow 表示默认值为当前UTC时间 # 当创建新用户记录时,如果未指定创建时间,会自动使用当前时间 created_at = sqliteDb.Column(sqliteDb.DateTime, default=datetime.utcnow)# 定义实例方法,将User对象转换为字典格式 # 用于API响应,便于序列化为JSON格式 defto_dict(self):return{'id': self.id,'username': self.username,'email': self.email,'created_at': self.created_at.strftime('%Y-%m-%d %H:%M:%S')}# 创建数据库表:使用应用上下文管理器,确保在正确的应用上下文中执行数据库操作 with flaskApp.app_context():# 根据定义的数据模型创建所有数据库表:如果表已存在,不会重复创建 sqliteDb.create_all()2. 创建用户
Create, Read, Update, Delete是任何数据驱动应用的基本操作。创建(Create)操作允许系统添加新数据记录,是实现数据持久化的第一步。
# 创建新资源的接口 # 与获取列表的路径相同,但使用POST方法 # RESTful设计:同一路径不同方法执行不同操作 @flaskApp.route('/api/create/user', methods=['POST'])defcreate_user():""" 创建新用户 RESTful风格:POST /api/users 创建新资源 请求体应包含JSON格式的用户数据 """# 获取JSON格式的请求数据 # request.get_json() 解析请求体中的JSON,返回Python字典 data = request.get_json()# 验证必要字段 # 定义必须提供的字段列表 required_fields =['username','email']# 遍历每个必需字段 for field in required_fields:# 检查字段是否存在且值不为空(去除空格后) # data[field] 直接访问字典,如果field不存在会引发KeyError # 更好的写法是:if field not in data or not data.get(field, '').strip() if field notin data ornot data[field].strip():# 返回400 Bad Request错误 # 提供具体的错误信息,指出哪个字段缺失 return jsonify({'error':f'{field}是必填项'}),400# 检查用户名是否已存在(唯一性验证) # User.query.filter_by(username=data['username']) 创建查询条件 # .first() 执行查询并返回第一个结果,没有结果则返回None if User.query.filter_by(username=data['username']).first():# 返回409 Conflict错误 # 409表示请求与服务器当前状态冲突(用户名已存在) return jsonify({'error':'用户名已存在'}),409# 检查邮箱是否已存在(唯一性验证) if User.query.filter_by(email=data['email']).first():# 同样返回409冲突错误 return jsonify({'error':'邮箱已存在'}),409# 创建新用户对象实例 # User 是SQLAlchemy模型类 # 通过关键字参数传递字段值 new_user = User( username=data['username'],# 从请求数据获取用户名 email=data['email']# 从请求数据获取邮箱 )# 将新用户对象添加到数据库会话 # sqliteDb.session.add() 将对象标记为"待添加" sqliteDb.session.add(new_user)# 提交会话,将更改保存到数据库 # 执行INSERT语句 sqliteDb.session.commit()# 返回201 Created状态码 # 201表示资源创建成功,通常包含新创建的资源信息 return jsonify({'message':'用户创建成功',# 成功消息 'user': new_user.to_dict()# 返回新用户的完整信息 }),201if __name__ =='__main__':# 参数说明: # debug=True: 启用调试模式 # 优点:1. 代码修改后自动重启服务 # 2. 出错时显示详细错误信息 # 3. 显示交互式调试器(仅本地访问) # 注意:生产环境必须设为False # port=5000: 指定服务运行的端口号 flaskApp.run(debug=True, port=5000)使用 Postman 测试:

3. 返回所有用户
当用户数量较多时,直接返回所有用户可能导致性能问题。实际应用中应考虑:
# 使用装饰器定义路由,将URL路径与处理函数绑定 # '/api/users' 是接口的URL路径 # methods=['GET'] 指定此路由只响应GET请求 # GET请求用于获取资源列表,不会修改服务器状态 @flaskApp.route('/api/users', methods=['GET'])defget_all_users():""" 获取所有用户信息 RESTful风格:GET /api/users 获取资源列表 无需参数,返回用户数组 """# 执行数据库查询,获取User表中的所有记录 # User.query.all() 返回一个包含所有用户对象的列表 # 如果表中没有用户,返回空列表 [] users = User.query.all() # 返回JSON格式的响应 # 使用列表推导式将每个用户对象转换为字典 # user.to_dict() 是User模型类中定义的方法,将对象序列化为字典 return jsonify({'users':[user.to_dict()for user in users],# 用户列表 'count':len(users)# 用户总数,便于前端分页统计 })if __name__ =='__main__':# 参数说明: # debug=True: 启用调试模式 # 优点:1. 代码修改后自动重启服务 # 2. 出错时显示详细错误信息 # 3. 显示交互式调试器(仅本地访问) # 注意:生产环境必须设为False # port=5000: 指定服务运行的端口号 flaskApp.run(debug=True, port=5000)使用 Postman 测试:

4. 返回单个用户
RESTful API通过URL路径参数定位具体资源。这种方式:
- 语义清晰,符合REST设计原则
- 便于缓存和代理处理
- 支持层级化资源访问
# 定义带路径参数的路由 # '<int:user_id>' 是路径参数,int: 指定参数类型为整数 # 访问时URL中的数字部分会被捕获并传递给user_id参数 # 例如:/api/users/123 中的123会作为user_id参数 @flaskApp.route('/api/users/<int:user_id>', methods=['GET'])defget_user(user_id):""" 获取单个用户详细信息 RESTful风格:GET /api/users/{id} 获取指定资源 user_id: 用户ID,从URL路径中获取 """# 根据主键(id)查询用户 # User.query.get(user_id) 相当于 SELECT * FROM user WHERE id = user_id # 如果找不到用户,返回None user = User.query.get(user_id)# 检查用户是否存在 # 如果user是None,表示数据库中不存在该ID的用户 if user isNone:# 返回404 Not Found错误 # 404状态码表示请求的资源不存在 return jsonify({'error':'用户不存在'}),404# 用户存在,返回其信息 # 调用to_dict()方法将用户对象转换为字典 return jsonify(user.to_dict())if __name__ =='__main__':# 参数说明: # debug=True: 启用调试模式 # 优点:1. 代码修改后自动重启服务 # 2. 出错时显示详细错误信息 # 3. 显示交互式调试器(仅本地访问) # 注意:生产环境必须设为False # port=5000: 指定服务运行的端口号 flaskApp.run(debug=True, port=5000)使用 Postman 测试:

5. 搜索用户
搜索是用户界面的核心功能之一,举例如下:
# 搜索用户接口 # 使用GET方法,通过查询参数传递搜索条件 # RESTful风格:GET /api/users/search?username=xxx&email=xxx @flaskApp.route('/api/users/search', methods=['GET'])defsearch_users():""" 搜索用户 通过查询参数过滤用户列表 支持模糊搜索 """# 从URL查询字符串获取搜索参数 # request.args 包含所有查询参数 # get() 方法:第一个参数是参数名,第二个参数是默认值 username = request.args.get('username','')# 用户名搜索关键词 email = request.args.get('email','')# 邮箱搜索关键词 # 创建基础查询 # User.query 返回一个查询对象,可以链式调用过滤方法 query = User.query # 如果提供了用户名搜索条件 if username:# 添加用户名过滤条件 # contains() 方法实现模糊搜索,SQL中的LIKE '%username%' query = query.filter(User.username.contains(username))# 如果提供了邮箱搜索条件 if email:# 添加邮箱过滤条件 query = query.filter(User.email.contains(email))# 执行查询,获取结果列表 # query.all() 执行构建好的查询 users = query.all()# 返回搜索结果 return jsonify({'users':[user.to_dict()for user in users],# 搜索结果列表 'count':len(users),# 结果数量 'search_params':{# 返回搜索参数,便于前端确认 'username': username,'email': email }})if __name__ =='__main__':# 参数说明: # debug=True: 启用调试模式 # 优点:1. 代码修改后自动重启服务 # 2. 出错时显示详细错误信息 # 3. 显示交互式调试器(仅本地访问) # 注意:生产环境必须设为False # port=5000: 指定服务运行的端口号 flaskApp.run(debug=True, port=5000)使用 Postman 测试:

5. 更新用户信息
以下示例实现的是PUT语义更新客户端提供的字段。
# 更新现有资源的接口 # 使用PUT方法,需要指定要更新的资源ID # RESTful设计:PUT用于完全替换资源 @flaskApp.route('/api/users/<int:user_id>', methods=['PUT'])defupdate_user(user_id):""" 更新用户信息 RESTful风格:PUT /api/users/{id} 更新指定资源 PUT通常用于完全替换资源,PATCH用于部分更新 """# 先查询要更新的用户是否存在 user = User.query.get(user_id)if user isNone:return jsonify({'error':'用户不存在'}),404# 获取请求中的更新数据 data = request.get_json()# 更新用户名(如果提供了新用户名) # 检查'username'字段是否存在且值不为空 if'username'in data and data['username']:# 检查新用户名是否与其他用户冲突(排除当前用户自己) # User.query.filter() 创建复杂查询条件 # User.username == data['username']: 用户名等于新用户名 # User.id != user_id: 排除当前用户自己 # .first(): 获取第一个匹配的用户 existing_user = User.query.filter( User.username == data['username'],# 用户名相等 User.id!= user_id # ID不等于当前用户ID ).first()# 如果找到其他用户使用这个用户名 if existing_user:return jsonify({'error':'用户名已被使用'}),409# 更新用户名 user.username = data['username']# 更新邮箱(如果提供了新邮箱) if'email'in data and data['email']:# 检查新邮箱是否与其他用户冲突 existing_user = User.query.filter( User.email == data['email'],# 邮箱相等 User.id!= user_id # ID不等于当前用户ID ).first()if existing_user:return jsonify({'error':'邮箱已被使用'}),409# 更新邮箱 user.email = data['email']# 提交更改到数据库 # 执行UPDATE语句 sqliteDb.session.commit()# 返回更新后的用户信息 return jsonify({'message':'用户更新成功','user': user.to_dict()# 返回更新后的用户信息 })if __name__ =='__main__':# 参数说明: # debug=True: 启用调试模式 # 优点:1. 代码修改后自动重启服务 # 2. 出错时显示详细错误信息 # 3. 显示交互式调试器(仅本地访问) # 注意:生产环境必须设为False # port=5000: 指定服务运行的端口号 flaskApp.run(debug=True, port=5000)使用 Postman 测试:

6. 删除用户
删除操作需要谨慎处理:
# 删除资源的接口 # 使用DELETE方法 # RESTful设计:DELETE用于删除资源 @flaskApp.route('/api/users/<int:user_id>', methods=['DELETE'])defdelete_user(user_id):""" 删除用户 RESTful风格:DELETE /api/users/{id} 删除指定资源 删除后资源将不可恢复 """# 查询要删除的用户 user = User.query.get(user_id)if user isNone:return jsonify({'error':'用户不存在'}),404# 从数据库会话中标记用户为待删除 sqliteDb.session.delete(user)# 提交更改,执行DELETE语句 sqliteDb.session.commit()# 返回200 OK状态码 # 204 No Content也可以,但这里返回了消息体 return jsonify({'message':'用户删除成功'}),200if __name__ =='__main__':# 参数说明: # debug=True: 启用调试模式 # 优点:1. 代码修改后自动重启服务 # 2. 出错时显示详细错误信息 # 3. 显示交互式调试器(仅本地访问) # 注意:生产环境必须设为False # port=5000: 指定服务运行的端口号 flaskApp.run(debug=True, port=5000)