目录
6、处理 application/json 类型请求体的 POST 请求
先创建一个项目的文件夹,然后在该文件夹下初始化一个虚拟环境。虚拟环境建设好后,文件夹下将有一个 venv 的目录。
- # python 3版本 创建虚拟环境:
- py -3 -m venv venv
-
- # 激活虚拟环境
- venv\Scripts\activate
-
- # 安装 flask
- pip install flask
依次创建 flaskr、flaskr/static、flaskr/templates、tests目录:
flaskr目录:包含应用代码和文件的 Python 包;
/static:存放静态文件,如图片等;
/templates:可以用来存放 html 文件,用于服务端渲染;
test 目录:用来存放测试类型的文件;
- from flask import Flask
- from flask import render_template
- from flask_cors import CORS # pip install flask_cors
-
- # 创建该类的实例
- app = Flask(__name__)
- CORS(app, resources={r'/*': {'origins' :'*'}}) # 同源策略解决方法
-
-
- # 使用app.route()进行装饰的函数称为路由处理函数
- # 使用装饰器设定匹配的URL,如果命中,则执行下面的函数;默认是 get 请求
- @app.route('/')
- def default_page():
- # 返回指定的页面,是项目下 templates 下的文件
- return render_template('index.html')
-
- if __name__ == '__main__':
- # 运行该服务,监听的端口为 9090,host=‘0.0.0.0’表示接受公开访问
- app.run(port=9090, host='0.0.0.0')
- # templates/index.js
-
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Titletitle>
- <style>
- h1{ color: brown; }
- style>
- head>
- <body>
- <h1>Hello, welcome to the first page.h1>
- body>
- html>
改造 index.html 文件,让请求和结果展示在当前界面完成:
- # templates/index.js
-
- <body>
- <h1>Hello, welcome to the first page.h1>
- <ul>
- <li>
- <button onclick="get_request('get_datetime', '.datetime')">click to get datetimebutton>
- <p class="datetime">p>
- li>
- ul>
- <script>
- function get_request(path, renderElement, query='', callback){
- const xhr = new XMLHttpRequest()
-
- xhr.open('GET', `http://192.168.145.135:9090/${path}${query ? `?${query}` : ''}`)
-
- xhr.onreadystatechange = () => {
- if(xhr.readyState === 4 && xhr.status){
- document.querySelector(renderElement).innerText = xhr.response
- callback ? callback(xhr.response) : ''
- }
- }
-
- xhr.send()
- }
- script>
- body>
- # 规定匹配的路径为 /get_time 或 /get_time?
- @app.route('/get_datetime')
- def get_datetime():
- return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
/templates/index.html 中新增的内容:
- <li>
- <input class="name" type="text" placeholder="please input you name">
- <select class="gender" name="" id="">
- <option value="man">manoption>
- <option value="woman">womanoption>
- select>
- <button onclick="request_args()">click to get datetimebutton>
- <p class="name-gender">p>
- li>
-
- <script>
- function request_args(){
- let name = document.querySelector('.name').value
- let gender = document.querySelector('.gender').value
- get_request('get_with_args', '.name-gender', `name=${name}&gender=${gender}`)
- }
- script>
- from flask import Flask,request
-
- @app.route('/get_with_args')
- def get_with_args():
- args = request.args
-
- # 各参数可以从 request 实例上去获取
- # print(args['name'], args['gender'])
-
- name = args['name']
- gender = args['gender']
- return f'你好,帅气的{name}先生' if gender == 'man' else f'你好,美丽的{name}小姐'
关于 request 更多内容可以参考: https://flask.net.cn/api.html#incoming-request-data
html 文件新增的测试内容:
- <li>
- <input class="nickname" type="text" placeholder="input nickname">
- <input class="age" type="number" placeholder="input age">
- <button onclick="sendParams()">click to send paramsbutton>
- <p class="nickname-and-age">p>
- li>
-
- <script>
- const sendParams = () => {
- let nickname = document.querySelector('.nickname').value
- let age = document.querySelector('.age').value
- get_request(`test-params/${nickname}/${age}`, '.nickname-and-age', '', (resdata)=>{
- let strong = document.createElement('strong')
- let obj = JSON.parse(resdata)
- strong.innerText = `response data: nickname: ${obj.nickname}; age: ${obj.age}`
- document.querySelector('.nickname-and-age').append(strong)
- })
- }
- script>
- # 两个 params 参数都必须传, 预定义 nickname 参数为字符串,age 为整型
- @app.route('/test-params/
/' ) - def test_params(nickname, age):
- return {'nickname':nickname,'age':age}
index.html 文件中新增的内容:
- <li>
- <input class="username" type="text">
- <input class="passwd" type="password">
- <button class="post-btn">click to postbutton>
- <p class="post-res">p>
- li>
-
- <script>
- document.querySelector('.post-btn').addEventListener('click',()=>{
- fetch('http://localhost:9090/test-post',{
- method:'post',
- body:JSON.stringify({
- username:document.querySelector('.username').value,
- passwd:document.querySelector('.passwd').value
- }),
- headers:{
- 'Content-Type':'application/json',
- 'auth':'abcdefg---6789'
- }
- }).then(res => res.text()).then(data=>{
- document.querySelector('.post-res').innerText = data
- })
- })
- script>
- @app.route('/test-post', methods=['post'])
- def test_post():
- str = (request.data).decode()
- print(str)
- obj = json.loads(str)
- print(obj['username'])
-
- print(request.json['username'])
-
- # print(request.get_data()) # 与 request.data 一致,是字符串类型
- # print(request.json) # 是dict类型
- return 'ok'
index.js 中添加的内容:
- <li>
- <input type="text" placeholder="your address" class="_address">
- <input type="text" placeholder="your name" class="_name">
- <button class="to-new-page">click to new page with databutton>
- li>
-
- <script>
- document.querySelector('.to-new-page').addEventListener('click', ()=>{
- address = document.querySelector('._address').value
- name = document.querySelector('._name').value
-
- # 在新的窗口中访问
- window.open(`http://localhost:9090/new-page?address=${address}&name=${name}`)
- })
- script>
新创建两个 html:
- # new-page.html 当有 name 时返回的页面,可以没有address
-
- <body>
- {% if address %}
- <h1>Hi {{name}}, we know your address is {{address}}.h1>
- {% else %}
- <h1>Hello {{name}}!h1>
- {% endif %}
- <img src="https://tse3-mm.cn.bing.net/th/id/OIP-C.bVb769JBdzVZYuksxZ2Y-AHaEo?pid=ImgDet&rs=1" alt="">
- body>
- # 404.html
-
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>404title>
- <style>
- body,html{
- margin: 0;
- padding: 0;
- }
- .outer{
- width: 100vw;
- height: 100vh;
- display: flex;
- justify-content: center;
- align-items: center;
- }
- .num{
- width: 100px;
- height: 100px;
- font-size: 5rem;
- font-weight: 900;
- text-shadow: 3px 3px 3px #aaa;
- }
- .num:nth-child(2n){ color: brown; }
- .num:nth-child(2n + 1){ color: chocolate; }
- .num:nth-child(4){ width: auto; }
- style>
- head>
- <body>
- <div class="outer">
- <div class="num">4div>
- <div class="num">0div>
- <div class="num">4div>
- <div class="num">no founddiv>
- div>
- body>
- # 可以直接访问
- @app.route('/new-page')
- def new_page():
- # 设置 address 和 name 都不是必传参数
- address = request.args['address'] if request.args else ''
- name = request.args['name'] if request.args else ''
- if name: # 往模板页面传递两个参数
- return render_template('another.html', address=address, name=name)
- else:
- # 如果没有名字,则返回 404 的页面
- return render_template('404.html')
index.html 中新增的内容:
- <li>
- <input type="file" accept=".jpg,.png,.gif" id="file">
- <button class="upload-file" onclick="uploadFile()">upload filebutton>
- <p class="upload-file-res">p>
- li>
-
- <script>
- function uploadFile(){
- let files = document.querySelector('#file').files
-
- const formdata = new FormData()
- formdata.append('file', files[0])
- formdata.append('time', new Date().getTime())
-
- fetch('http://localhost:9090/upload-file',{
- method:'post',
- body:formdata
- }).then(res=>res.text()).then(data=>{
- console.log(data)
- })
- }
- script>
- @app.route('/upload-file', methods=['post'])
- def upload_file():
- if 'file' not in request.files:
- return 'upload fail'
-
- file = request.files['file']
-
- if file.filename == '':
- return 'filename no found'
-
- file.save(f'./{file.filename}') # 保存图片到本地
-
- return 'ok'
关于 flask 文件上传的更安全的处理:上传文件_Flask中文网
每一个使用 app.route() 进行装饰的路由处理函数都需要有返回值,是服务端返回给客户端的响应体内容。该返回值可以是一个字符串,也可以是一个元组(包含状态码、响应体等的信息)等的形式。
可以使用 make_response() 来构造响应内容。
- from flask import request, make_response
-
-
- @app.route('/')
- def website():
- ip = request.remote_addr # 获取用户的ip
- agent = request.user_agent # 获取用户访问时的设备信息(浏览器信息)
- print('ip:',ip)
- print('agent:',agent)
-
- # 设置内容安全策略 report-uri /CSP-feedback 将违反安全策略的情况使用post请求发送到当前项目的 /CSP-feedback 路由处理函数中进行处理
- csp_header = {
- 'Content-Security-Policy': "default-src 'self' 'unsafe-inline';script-src 'self';report-uri /CSP-feedback",
- 'X-Content-Type-Options': 'nosniff',
- 'X-Frame-Options': 'DENY',
- 'X-XSS-Protection': '1; mode=block'
- }
- temp = render_template('index.html')
-
- res = make_response(temp, 200) # 设置响应的html内容和状态码
- res.headers.extend(csp_header) # 将内容安全策略添加到响应头
-
- return res
-
-
- @app.route('/CSP-feedback',methods=['post'])
- def CSP_feedback():
- data = request.data.decode() # 获取响应体的内容
- ip = request.remote_addr
- print(data, ip)
- return {}
-
-
- @app.route('/test')
- def hello_world():
- response = make_response('Hello, World!') # 设置响应内容
- response.status_code = 200 # 单独设置状态码
- response.headers['Content-Type'] = 'text/plain' # 设置响应体类型
- return response
-
-
访问时 控制台显示出违反安全策略的详情内容:
- {
- "csp-report":{
- "blocked-uri":"inline",
- "column-number":78,
- "disposition":"enforce",
- "document-uri":"http://127.0.0.1:9090/",
- "effective-directive":"script-src-elem",
- "line-number":249,
- "original-policy":"default-src 'self' 'unsafe-inline'; script-src 'self'; report-uri http://127.0.0.1:9090/CSP-feedback",
- "referrer":"",
- "source-file":"moz-extension",
- "status-code":200,
- "violated-directive":"script-src-elem"
- }
- }
"blocked-uri":被CSP阻止的资源URI。"inline"意味着CSP阻止了在HTML文档中直接插入内联脚本(即
标签)。
"column-number"、"line-number":在源代码中违规的列号和行号。
"disposition":表示处理的方式。enforce 表示强制执行CSP策略,阻止该违规行为的执行。
"effective-directive"、"violated-directive":正在被触发的CSP指令 和 违反的CSP指令。script-src-elem 要求 script 的来源必须符合要求
安装 pymysql:
pip install pymysql
- import pymysql
-
- # 连接指定数据库
- conn = pymysql.connect(host='localhost', user='root',
- password='abcdef',database='python_study',
- charset='utf8')
-
- # 定义一个游标对象,靠游标对象来执行sql 以及获取 结果
- #cursor = conn.cursor() # 默认是元组形式的结果
-
- # 将游标对象定义为 字典 类型,进而更方便地获取数据
- from pymysql.cursors import DictCursor
- cursor = conn.cursor(DictCursor) # 创建游标,定义返回的数据类型是字典
查询语句的执行和结果获取:
- # 创建并执行 执行sql语句
- sql = 'select * from student'
- cursor.execute(sql)
-
- # 获取执行的结果集 所有数据
- res = cursor.fetchall()
- print(res)
-
- # res = cursor.fetchmany(3) # 获取前三条结果
关于更新类的操作(增、删、改):
- # 更新一条数据
- sql = "update student set age = 9 where num = '1000'"
- cursor.execute(sql)
- conn.commit() # 显式提交更新操作
-
-
-
- # 新增一条数据,注意格式化的sql语句,如果有引号的要加上引号
- name = '许九'
- age = 10
- gender = '男'
- num = '1006'
- sql = f"insert into student values ('{name}','{num}','{gender}',{age});"
- cursor.execute(sql)
- conn.commit()
最后需要关闭连接:
- # 关闭连接
- conn.close()
如果 连接的时候报错:RuntimeError: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
该错误指出需要 'cryptography' 包来处理 'sha256_password' 或 'caching_sha2_password' 的认证方式。
这可能是因为 Python 环境中缺少了 'cryptography' 包。这个包是一个 Python 的加密库,它提供了许多加密算法的实现,包括 'sha256_password' 和 'caching_sha2_password' 这两种 MySQL 的密码加密方式。
直接安装这个包然后再运行程序即可:
pip install cryptography
参考:
[1] flask获取post请求参数_flask post参数_阿常呓语的博客-CSDN博客
[2] 快速上手_Flask中文网