Django基础七之Ajax
内容概要:
contentType:
contentType是在请求头中规定(告诉服务器)请求体的编码类型(即消息的格式,而请求体的编码类型和服务端接收到的数据的编码类型是一样的,request.body使用相同的类型解码为字节),常见的类型共有3种:
1, application/x-www-form-urlencoded
网络传输默认的消息格式,不用显式指定,键值对的 ==>user=yuan&age=22,后端对应这个格式来解析获取数据
2, multipart/form-data
传输大数据或者文件会使用一种消息格式,将文件或者消息数据分段
3, application/json
==> contenttype:'json',简写一个json,它也能识别是application/json类型.application/json 这个 Content-Type 作为响应头大家肯定不陌生。实际上,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。
传输json字符串,
form表单不支持发json类型的contentType格式的数据,而ajax什么格式都可以发。在ajax中contentType有两种写法(可以写在headers外面或里面)。
$.ajax({ url:"{% url 'home' %}", type:'post', //写法1:直接以键值对的形式写在ajax中 contentType:'json', //注意写法:驼峰样式,单词之间没有-连接 headers:{ //写法2:以键值对的形式写在headers中 // contentType:'json', }, })
注意:
request.POST 就是按照 request.body 解析的数据,但是只有当 contentType=urlencoded的时候才会取值去解析(这个解析是Django帮我们做的).
1,contentType=json 时候打印的request.body和 request.POST:
2,contentType=urlencoded时候打印的request.body和 request.POST:
CSRFToken:
post传输方式要验证CSRF,有两种方式:
1,,通过input标签获取这个值 ==>放在请求数据中,注意键名一定要是这个
1 上面的任意位置写上{% csrf_token %} 2 3 data:{csrfmiddlewaretoken:$("input[name='csrfmiddlewaretoken']").val()},
2,通过那个cookie 获取值 ==>放在请求头中,注意键名一定要是这个
1 headers:{ //注意:这里是复数 2 // contentType:'json', 3 'X-CSRFToken':$.cookie('csrftoken') 4 },
注意:
1,在发送json格式的时候(contentType:'json',),只能通过cookie来发送这个csrftoken(是在请求头中发送),写在data中没有效果
2,将CSRF验证添加到数据部分(请求体中),真正验证的时候是先扫描请求头再扫描请求体去寻找CSRF验证,但是要注意,在请求头和请求体中的键是不同的
""" 小结: 字符串(Django这里) -------> 首先要清楚,请求 响应 <-------- 字符串 小结: 1,contentType是在请求头中规定(告诉服务器)请求体的编码类型 2,请求的数据类型是json的(即设置contentType='json'),django这边解析不了(Django从request.body中解析数据放到request.POST或者request.GET中),
数据还放在request.body里面(json字符串形式的字节类型,如b'{"name":"liang","psw":"222"}') 3,响应的数据类型: 3.1,使用HttpResponse, 第一个参数只能是字符串,默认没有传输content_type,前端需要反序列化: HttpResponse(msg),前端(JS)就是接收的普通的字符串, HttpResponse(json.dumps(msg),msg可以是任意的数据类型,前端(JS)是接收的json类型的字符串,需要反序列化成自己的数据类型 HttpResponse(json.dumps(msg),content_type='application/json'),msg可以是任意的数据类型,前端(JS)是接收的json类型的字符串,
不需要反序列化就自动加工成自己的数据类型 3.2,使用JsonResponse, 默认第一个参数只能是字典,默认传输content_type=json,前端不需要反序列化: JsonResponse(msg) JsonResponse([1,2,3],safe=False) # 加了safe=False为第二个参数,第一个参数就可以是任意的数据类型了 """
form表单上传文件:
操作步骤:
1,enctype="multipart/form-data"
2,<input type="file" name="photo"> # 这里的name是后端request.FILES.get('name')取值的依据
3,method="post" 不要忘记 {% csrf_token %}
核心代码:
name = request.POST.get('name')# 普通数据 photo = request.FILES.get('photo') # 文件 path = os.path.join(settings.BASE_DIR,'statics','files',photo.name) # 正确的路径表示方式,就是从项目的所在路径往下面拼接 with open(path,'wb') as f: # 方式一:读文件 # for line in photo: # f.write(line) # 方式二: for line in photo.chunks(): # 对象(photo)调用实例方法(chunks) f.write(line)
ajax
ajax特点:
异步交互:当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应!
局部刷新:整个过程中页面没有刷新,只是刷新页面中的局部位置而已!
使用ajax上传文件:
操作步骤(注意:取到用户选择的文件对象的方法):
1,要新建一个 FormData 类型的对象, var formdata = new FormData();
2,要使用 $('.file')[0].files[0] 手动获取上传到浏览器上面的文件,
使用 formdata.append('photo',$('.file')[0].files[0]); 将文件进去,进行传输
3,post传输方式要验证CSRF,方式有两种:
方法一:在请求头中
headers:{'X-CSRFToken':$.cookie('csrftoken')}
方法二:在请求数据中
上面的任意位置写上{% csrf_token %},
使用 formdata.append('csrfmiddlewaretoken',$('input[name=csrfmiddlewaretoken]').val()); 添加到数据中一起传输.
核心代码:
$('#btn1').click(function () { var formdata = new FormData(); formdata.append('username','alex'); //request.POST.get('username') formdata.append('photo',$('.file')[0].files[0]); //request.FILES.get('photo') //方法一:将CSRF验证添加到数据部分(请求体中) formdata.append('csrfmiddlewaretoken',$('input[name=csrfmiddlewaretoken]').val()); $.ajax({ url:"{% url 'upload' %}", type:'post', //方法二:在请求头中加 csrftoken 验证 // headers:{'X-CSRFToken':$.cookie('csrftoken')}, data:formdata, //下面两句也要 processData:false, //不预处理数据 contentType:false, //不设置内容类型 success:function (res) { console.log(res) } }) })
一 Ajax相关
1.简介
AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,现在更多使用json数据)。
- 同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
- 异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。
Ajax的特点:
异步交互: 当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应!
局部刷新: 整个过程中页面没有刷新,只是刷新页面中的局部位置而已!
AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)
我们以前知道的前端向后端发送数据的方式有:
GET:地址栏、a标签、Form表单
POST:Form表单
那么现在我们在学习一种:那就是ajax
ajax:也是前端向后端发送数据的一种方式
2.示例
写一个登陆认证页面,登陆失败不刷新页面,提示用户登陆失败,登陆成功自动跳转到网站首页。
{% load static %} <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --> <title>login页面</title> </head> <body> {#使用ajax的时候就不用使用form提交数据了,input标签中也不用必须带着name属性了#} {#<input type="hidden" name="csrfmiddlewaretoken" value="BQ5cJCg2GEbRXfVAUe1maIV0mqq3YV86Ta8TznSSquHzBLhQGMFGn2cTDUYOnlS3">#} {% csrf_token %} 用户名: <input type="text"> 密码: <input type="password"> <button>提交</button> <span style="color: red;"></span> <script src={% static "js/jquery.js" %}></script> <script> {#将ajax嵌套到点击事件中,当点击按钮时候,触发事件响应#} $('button').click( function () { var name = $(':text').val(); var password = $(':password').val(); var csrf_token = $("input[name='csrfmiddlewaretoken']").val(); $.ajax({ {#注意:下面每一个语句的结尾要加逗号,除了data可以依据需要不写(不需要传输数据就可以不用写)其他几个都是必须要有的字段#} url:{% url 'login' %}, {#要加引号#} type:"post", {#这个是请求数据,当不需要传输数据的时候,就可以不用写这个.数据的值是JS的自定义对象数据类型,键可以不用加引号,键是后端的views取数据的依据, 除了csrfmiddlewaretoken的名字是固定的,其他的键的名字都自己随意#} data:{name:name,password:password,csrfmiddlewaretoken:csrf_token}, success:function (response) { {#js里面的json反序列化#} {#通过 HttpResponse 发送来的只有序列化的字符串需要反序列化才能转化成js的对象(才能调用各种方法取值)来使用#} {#response = JSON.parse(response);#} if (response.status) { {#通过字典下标的方式取值,自定义对象的时候,键要带引号才行#} {#location.href = response['url_path'] #} location.href = response.url_path }else { $('span').text('用户名或密码错误,请重新输入'); console.log('失败') } } }) } ) </script> </body> </html>
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --> <title>home</title> </head> <body> <h1>欢迎来到梁山</h1> </body> </html>
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^home/', views.home,name='home'),
url(r'^login/', views.login,name='login'),
]
rt * from django.urls import reverse import json from django.http import JsonResponse # Create your views here. def home(request): return render(request, 'home.html') def login(request): if request.method == 'GET': return render(request, 'login.html') else: # POST请求是提交数据, name = request.POST.get('name') # 取数据的依据是对应HTML中ajax中的data的值的键 psw = request.POST.get('password') record = UserInfo.objects.filter(name=name, psw=psw) msg = {'status': 0, 'url_path': 0} if record.exists(): # 能匹配到记录的话,跳转到主页 msg['status'] = 1 msg['url_path'] = reverse('home') # return HttpResponse(json.dumps(msg)) # 使用 HttpResponse 只能发送字符串,需要将字典等非字符串的数据类型通过json 序列化为字符串 # return HttpResponse(json.dumps(msg),content_type='application/json') # 第而给参数传入content_type='application/json', js那边不用反序列化就能使用了(其实是JS那边将接收到的返回值反序列化之后才传给了response) # return JsonResponse(msg) # 直接传输字典,传输之前不用序列化,js接收到之后也不用反序列化(内部会自动将反序列化之后的值传给response) return JsonResponse(msg,safe=False) # JsonResponse默认只能传输字典,加第二个参数safe=False才能传输其他的数据类型
3.示例 content-Type = 'json'
{% load static %} <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --> <title>home</title> </head> <body> {#{% csrf_token %}#} <input type="text" class="i1"> <input type="password" class="i2"> <button class="btn1">提交</button> </body> <script src="{% static 'js/jquery.js' %}"></script> <script src="{% static 'js/jquery.cookie.js' %}"></script> <script> $('.btn1').click(function () { $.ajax({ url:"{% url 'home' %}", type:'post', contentType:'json', //注意写法:驼峰样式,单词之间没有-连接 headers:{ //注意:这里是复数 // contentType:'json', 'X-CSRFToken':$.cookie('csrftoken') }, //data:{name:$('.i1').val(),psw:$('.i2').val(),csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val()}, //如果我们发送的是json数据格式的数据,那么csrf_token就不能直接写在data里面了,没有效果,必须通过csrf的方式3的形式来写,写在headers(请求头,可以写一些自定制的请求头)里面,注意,其实contentType也是headers里面的一部分,写在里面外面都可以 data:JSON.stringify({name:$('.i1').val(),psw:$('.i2').val()}), //上面指定了 contentType:'json',则这里的数据需要序列化成json字符串 success:function (response) { console.log(response) } }) }) </script> </html>
urlpatterns = [
url(r'^home/', views.home,name='home'),
]
def home(request): if request.method == 'GET': return render(request, 'home.html') else: print(request.POST) # 不用解析就是字典???错, json类型的contentType请求,Django解析不了,数据还放在request.body里面. print(request.body) # b'{"name":"liang","psw":"222"}' """ 字符串(Django这里) -------> 首先要清楚,请求 响应 <-------- 字符串 小结: 请求的数据类型是json的(即设置contentType='json'),django这边解析不了,数据还放在request.body里面(json字符串形式的字节类型,如b'{"name":"liang","psw":"222"}') 响应的数据类型: 使用HttpResponse, 第一个参数只能是字符串,默认没有传输content_type,前端需要反序列化: HttpResponse(msg),前端(JS)就是接收的普通的字符串, HttpResponse(json.dumps(msg),msg可以是任意的数据类型,前端(JS)是接收的json类型的字符串,需要反序列化成自己的数据类型 HttpResponse(json.dumps(msg),content_type='application/json'),msg可以是任意的数据类型,前端(JS)是接收的json类型的字符串,不需要反序列化就自动加工成自己的数据类型 使用JsonResponse, 默认第一个参数只能是字典,默认传输content_type=json,前端不需要反序列化: JsonResponse(msg) JsonResponse([1,2,3],safe=False) # 加了safe=False为第二个参数,第一个参数就可以是任意的数据类型了 """ return HttpResponse('OK')
4.示例 文件上传
form表单的文件上传
{% load static %} <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --> <title>upload</title> <!-- Bootstrap //引入的路径也不要忘记在最外层加引号--> <link href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}" rel="stylesheet"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> {# 面板#} <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">欢迎来到用户中心</h3> </div> <div class="panel-body"> <form action="{% url 'upload' %}" method="post" enctype="multipart/form-data"> {% csrf_token %} <div class="form-group"> <label for="exampleInputEmail1">用户名 </label> <input type="text" class="form-control" id="exampleInputEmail1" placeholder="用户名" name="name"> </div> <div class="form-group"> <label for="exampleInputPassword1">昵称</label> <input type="text" class="form-control" id="exampleInputPassword1" placeholder="昵称" name="nickname"> </div> <div class="form-group"> <label for="exampleInputFile">头像</label> <input type="file" id="exampleInputFile" name="photo"> <p class="help-block" style="font-size: 12px">请选择一张图片作为你的头像 </p> </div> <button type="submit" class="btn btn-success">提交 </button> </form> </div> </div> </div> </div> </div> <script src="{% static "js/jquery.js" %}"></script> </body> </html> // form表单上传文件: // 1,enctype="multipart/form-data" // 2,<input type="file" name="photo"> # 这里的name是后端 request.FILES.get('name')取值的依据 // 3,method="post" 不要忘记 {% csrf_token %}
urlpatterns = [
url(r'^upload/', views.upload,name='upload'),
]
def upload(request): if request.method == 'GET': return render(request,'upload2.html') else: name = request.POST.get('name') nickname = request.POST.get('nickname') photo = request.FILES.get('photo') # 拿到上传的文件对象,就是InMemoryUploadedFile对象 # print(request.POST) # <QueryDict: {'csrfmiddlewaretoken': ['77Dt4yxcQxEpDlWaLAwbqd6QdPcTtdnXprGaUj92Ana7hRiqx8avDxnJujKESD7U'], 'name': ['liang'], 'nickname': ['海绵宝宝']}> # print(type(photo)) # <class 'django.core.files.uploadedfile.InMemoryUploadedFile'> # print(request.FILES) # <MultiValueDict: {'photo': [<InMemoryUploadedFile: 1.jpg (image/jpeg)>]}> # path = os.path.join(reverse('static'),'files',photo.name) # 错误,reverse找不到static路径,static只是用来标识静态文件的路径的,而不是用来反向解析路径的 # path = os.path.join(settings.STATICFILES_DIRS,'statics','files',photo.name) # 错误,STATICFILES_DIRS是个列表,需要的第一个参数是字符串, from django.core.files.uploadedfile import InMemoryUploadedFile path = os.path.join(settings.BASE_DIR,'statics','files',photo.name) # 正确的路径表示方式,就是从项目的所在路径往下面拼接 with open(path,'wb') as f: # 方式一:读文件 # for line in photo: # f.write(line) # 方式二: for line in photo.chunks(): # 对象(photo)调用实例方法(chunks) f.write(line) return HttpResponse('OK')
Ajax的文件上传
upload.html,内容如下:
{% load static %} <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --> <title>ajax upload files</title> <!-- Bootstrap --> <link href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}" rel="stylesheet"> </head> <body> <div style="margin-left: 100px; margin-top: 100px;"> <input type="file" class="file"> <button id="btn1" class="btn-success">提交</button> </div> {% csrf_token %} <script src="{% static 'js/jquery.js' %}"></script> <script src="{% static 'js/jquery.cookie.js' %}"></script> <script> $('#btn1').click(function () { var formdata = new FormData(); //注意:声明一个变量一定要使用var //append类似于列表,只是添加的数据是键值对,第一个参数是键,第二个参数是值,中间使用逗号隔开 formdata.append('username','alex'); //request.POST.get('username')普通数据,依然通过request.POST去取 formdata.append('photo',$('.file')[0].files[0]); // $('.file')[0].file[0]是DOM方法拿到你在浏览器上面选中的文件(那个文件是已经发送到浏览器上面了) //方法一:将CSRF验证添加到数据部分(请求体中),真正验证的时候是先扫描请求头再扫描请求体去寻找CSRF验证,但是要注意,在请求头和请求体中的键是不同的 //request.FILES.get('photo') 文件通过request.FILES去取 formdata.append('csrfmiddlewaretoken',$('input[name=csrfmiddlewaretoken]').val()); $.ajax({ url:"{% url 'upload' %}", type:'post', // post请求必须要加csrf验证(两种方法,一个是在请求头中加,一个在数据中加) //方法二:在请求头中加 csrftoken 验证 // headers:{ // 'X-CSRFToken':$.cookie('csrftoken') //前面的X-CSRFToken可以全部大写,后面的必须是csrftoken,而不能加下划线 // }, data:formdata, // ajax上传文件使用 FormData 数据类型,而且一定要配套下面两个语句一起使用 processData:false, //不预处理数据 contentType:false, //不设置内容类型 success:function (res) { console.log(res) } }) }) </script> </body> </html> // 使用ajax上传文件: // 1,要新建一个 FormData 类型的对象, var formdata = new FormData(); // 2,要使用 $('.file')[0].files[0] 手动获取上传到浏览器上面的文件, 使用 formdata.append('photo',$('.file')[0].files[0]); 将文件进去,进行传输 // 3,post传输方式要验证CSRF,方式有两种: // 方法一:在请求头中 // headers:{'X-CSRFToken':$.cookie('csrftoken')} // 方法二:在请求数据中 // 上面的任意位置写上{% csrf_token %},使用 //formdata.append('csrfmiddlewaretoken',$('input[name=csrfmiddlewaretoken]').val()); 添加到数据中一起传输.
注意:Ajax的特性就是局部刷新,最终总是还在当前页面,这个例子的表现就是最后post提交数据的时候,并不会跳转到OK界面,而是异步提交,局部刷新(偷偷的进行请求和响应)
urls.py 和 views.py 同form表单上传文件的写法.
二 关于json
说起json,我们大家都了解,就是python中的json模块,那么json模块具体是什么呢?那我们现在详细的来说明一下
json简介
1、json(Javascript Obiect Notation,JS对象标记)是一种轻量级的数据交换格式。
它基于 ECMAScript (w3c制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。
简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
JSON 是轻量级的文本数据交换格式
JSON 独立于语言 *
JSON 具有自我描述性,更易理解
* JSON 使用 JavaScript 语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。
2、json其实是从js中拿出来的一个对象,也可以说json是js的一个子集。
需要知道的:json的格式来源于js的格式
1、js支持单引号,也支持双引号,也可以没有引号
/在js中吧{}这样的类型叫做对象,js中没有字典一说
data = {
'name':'haiyan',
"name":"haiyan",
name:"haiyan"
} //js对象默认会把自己的键当成字符串处理,所以可以加引号也可以不加
2、json的格式:
1、json只认双引号的
2、json一定是一个字符串
3、下面我们看看哪些是合格的字符串,那些不是?
合格的json对象:
["one", "two", "three"] { "one": 1, "two": 2, "three": 3 } #这就是一个json的object类型,符合json的标准格式,就可以通过dumps来进行序列化 {"names": ["张三", "李四"] } [ { "name": "张三"}, {"name": "李四"} ]
不合格的json对象:
{ name: "张三", 'age': 32 } // 属性名必须使用双引号 [32, 64, 128, 0xFFF] // 不能使用十六进制值 { "name": "张三", "age": undefined } // 不能使用undefined { "name": "张三", "birthday": new Date('Fri, 26 Aug 2011 07:13:10 GMT'), "getName": function() {return this.name;} // 不能使用函数和日期对象 }
看一下普通字符串和json字符串,在进行反序列化的时候的区别:
import json # s = "{'name':'chao','age':18}" #普通字符串,没加引号的没问题,加了引号的,必须是双引号才能使用json.loads()。 s = '{"name":"chao","age":18}' #json字符串,里面必须是双引号 ret = json.loads(s) print(ret) print(ret['name'])
python中的序列化(dumps)与反序列化(loads)
import json i = 10 s = "dsfdsf" l = [11,22,33] dic = {"name":"haiyna","age":22} b = True # #吧基本数据类型转换成字符串的形式 print(json.dumps(i),type(json.dumps(i))) #10 <class 'str'> print(json.dumps(s),type(json.dumps(s))) #"dsfdsf" <class 'str'> print(json.dumps(l),type(json.dumps(l))) #[11, 22, 33] <class 'str'> print(json.dumps(dic),type(json.dumps(dic))) #{"name": "haiyna", "age": 22} <class 'str'> print(json.dumps(b),type(json.dumps(b))) #true <class 'str'> # ===============json反序列化============= d = {"a":1,"b":"fdgfd"} data = json.dumps(d) print(data,type(data)) f = open("a.txt","w") f.write(data) #注意这会写进去的字符串时双引号的格式 f.close() # ===============json序列化============= f = open("a.txt","r") datat = f.read() print(datat,type(datat)) #{"a": 1, "b": "fdgfd"} <class 'str'> data = json.loads(datat) print(data,type(data)) #{'a': 1, 'b': 'fdgfd'} <class 'dict'>
JS中的序列化(stringify)与反序列化(parse)
<script> //===========js中的json反序列化=========== s = '{"name":1}'; var data = JSON.parse(s); console.log(data); console.log(typeof data); //object //===========js中的json的序列化======= s2={'name':'yuan'}; console.log(JSON.stringify(s2),typeof JSON.stringify(s2)) //string </script>
CSRFToken
未找到释义