用flask或flask-restful开发接口时,返回给前端的json数据中若有中文,在postman的raw里或浏览器直接访问,中文会显示成unicode编码之后的形式,下面是使之显示成中文的方法。
情况一
若没用Flask-RESTful,只用flask,针对视图函数里下面三种返回的方法,只要创建app后加一句配置,app.config['JSON_AS_ASCII']=False,则postman的raw数据或浏览器直接访问,都显示中文。参考链接1,参考链接2
- return {'msg':'状态'}
- return jsonify(msg='状态')
- 先定义字典dic={'msg':'状态'},再return json.dumps(dic)
如果不加配置代码,可以在返回时加参数,如return json.dumps(dic,ensure_ascii=False),也显示中文。返回值还含有中文时,默认为ASCII编码,中文为Unicode编码,参考链接3
情况二
若用Flask-RESTful,类视图的post()函数(只是举例,get()或其他也行)中有
data={
'message':'新闻发表成功',
'status':200,
'news':marshal(news,news_detail_fields)
}
marshal()内的news是News类的对象,一个news对象就是一条新闻,数据库中有多条新闻,模型代码
class BaseModel(db.Model):
__abstract__=True # 作为抽象类,不能单独作为模型出现,只能作父类
id=db.Column(db.Integer,primary_key=True,autoincrement=True)
timestamp=db.Column(db.DateTime,default=datetime.utcnow)
class NewsType(BaseModel):
__tablename__='news_type' # 不写这句和父类中的__abstract__,生成的表名是base_model
type=db.Column(db.String(50),nullable=False)
class News(BaseModel):
__tablename__='news'
title=db.Column(db.String(100),nullable=False)
content=db.Column(db.Text,nullable=False)
type_id=db.Column(db.Integer,db.ForeignKey('news_type.id'))
user_id=db.Column(db.Integer, db.ForeignKey('user.id'))
is_deleted=db.Column(db.Boolean,default=False)
comments=db.relationship('NewsComment',backref='news')
class NewsComment(BaseModel):
__tablename__='news_comment'
content=db.Column(db.String(256),nullable=False)
star_times=db.Column(db.Integer,default=0) # 点赞量
news_id=db.Column(db.Integer,db.ForeignKey('news.id')) # 被评论的新闻的id
user_id=db.Column(db.Integer,db.ForeignKey('user.id')) # 发表评论的人的id
replies=db.relationship('Reply',backref='comment')
class User(BaseModel):
__tablename__='user'
username=db.Column(db.String(50),unique=True,nullable=True)
password=db.Column(db.String(128)) # 短信登录时,若当前手机号未注册,则自动注册。这时由于是短信登录,所以没有密码,所以创建用户时可以没有密码
phone=db.Column(db.String(11),unique=True,nullable=True)
portrait_file_name=db.Column(db.String(64))
is_deleted=db.Column(db.Boolean,default=False)
news=db.relationship('News',backref='author') # 根据新闻查作者
comments=db.relationship('NewsComment',backref='user')
replies=db.relationship('Reply',backref='user')
class Reply(BaseModel): # 对评论的回复
__tablename__='reply'
content=db.Column(db.String(256),nullable=False)
star_times=db.Column(db.Integer,default=0) # 点赞量
comment_id=db.Column(db.Integer,db.ForeignKey('news_comment.id')) # 被回复的评论的id
user_id=db.Column(db.Integer,db.ForeignKey('user.id')) # 发表回复的人的id
向数据库手动添加一些新闻再测试。
然后post()函数返回了data,有多种返回方式,我将对应的结果放在后面的表格里。如果复制下面的代码去测试,请自行导入相关库、创建api及其他对象。postman里选POST,地址http://127.0.0.1:5000/news,Body的form-data里,KEY有type_id、title、content,再填上对应的VALUE,然后提交。如果懒得POST,在数据库里有数据的前提下,自己写个GET的接口也可以测试,如获取所有的新闻类型。
add_news_parser=reqparse.RequestParser()
add_news_parser.add_argument('title',type=str,help='请输入新闻标题',required=True,location='form') # 接收要发布的新闻的标题
add_news_parser.add_argument('content',type=str,help='请输入新闻内容',required=True,location='form') # 接收要发布的新闻的内容
add_news_parser.add_argument('type_id',type=int,help='请选择新闻类型',required=True,location='form') # 接收要发布的新闻的类型的id
reply_fields={ # 对评论的回复的格式
'user':AuthorName(attribute='user'),
'content':fields.String,
'datetime':fields.DateTime(attribute='timestamp'),
'star_times':fields.Integer
}
comment_fields={ # 评论的格式
'user':AuthorName(attribute='user'),
'content':fields.String,
'datetime':fields.DateTime(attribute='timestamp'),
'star_times':fields.Integer,
'replies':fields.List(fields.Nested(reply_fields)) # 对评论的回复
}
news_detail_fields={ # 新闻详情的格式,即点击某条新闻后展示的完整内容
'id':fields.Integer,
'title':fields.String,
'content':fields.String, # 数据库中是Text类型,fields中无Text类型,可用String代替
'datetime':fields.DateTime(attribute='timestamp'),
'author':AuthorName(attribute='author'),
'comments':fields.List(fields.Nested(comment_fields)) # comments里是NewsComment对象,不能直接放在fields.List()里,fields.Nested()支持嵌套形式的json,里面传入字典,会把NewsComment对象转成前面传入的字典的格式,然后放在List里。from https://flask-restful.readthedocs.io/en/latest/fields.html#list-field和https://flask-restful.readthedocs.io/en/latest/fields.html#advanced-nested-field
}
class NewsResource(Resource): # 新闻的类视图
def post(self): # 发表新闻
args=add_news_parser.parse_args()
title=args.get('title')
content=args.get('content')
type_id=args.get('type_id')
news=News(title=title,content=content,type_id=type_id,user_id=g.user.id)
db.session.add(news)
db.session.commit()
data={
'message':'新闻发表成功',
'status':200,
'news':marshal(news,news_detail_fields)
}
return data
# return jsonify(message='新闻发表成功',status=200,news=marshal(news,news_detail_fields))
# return json.dumps(data)
# return json.dumps(data,ensure_ascii=False)
api.add_resource(NewsResource,'/news')
将app.config['JSON_AS_ASCII']=False称为配置代码1,将app.config.update(RESTFUL_JSON=dict(ensure_ascii=False))称为配置代码2。
json是通过import json导入的,不是from flask import json,因从flask导入的json在json.dumps()里输参数ensure_ascii=False时没自动补全。
创建app后不加配置代码 | 创建app后加配置代码1 | 创建app后加配置代码2 | |
return data | unicode | unicode | 中文 |
return jsonify(message='新闻发表成功',status=200,news=marshal(news,news_detail_fields)) | unicode | 中文 | unicode |
return json.dumps(data) | unicode且有很多\ | unicode且有很多\ | unicode且有很多\ |
return json.dumps(data,ensure_ascii=False) | unicode且有很多\ | unicode且有很多\ | 中文且有很多\ |
如果你的返回值是简单的纯json,而不像我那样里面有对象,返回值的数据编码已测试与上表一致。因我的对象用marshal()或@marshal_with()序列化过了,返回值已经是真正的json数据。flask-restful接口返回的数据只能是纯json形式,里面不能有对象,否则无法返回。
json.dumps()和jsonify()都是将字典转为字符串,但响应头不同,json.dumps()返回的是Content-Type:text/html,jsonify()返回的是Content-Type:application/json。参考链接4。可能因为json.dumps()不是真正的json形式,或是其他原因,导致代码中json.dumps()只返回unicode编码的数据,而jsonify()在加了那句配置代码的前提下能返回正常显示中文的数据。
接口返回的中文无法显示的原因
flask是支持中文的,官方的方法是添加app.config['JSON_AS_ASCII']=False。所以可以解决情况一。官方文档1,官方文档2。
但flask-restful使用的json是Python标准库中的模块(官方文档3),导致flask官方的方法失效,而标准库的json要想显示中文,就必须json.dumps(data,ensure_ascii=False)。flask-restful的源码中,是想读取你的用户配置(如图箭头),从而达到控制 json.dumps()。源码内没有手动配置的话,默认就是ensure_ascii为True,因这里它的dumped = dumps(data, **settings) + "\n"就是反向映射(图片未截全,自行去源码里看)。
所以若用flask-restful,除了添加那句配置代码2,也可以直接修改源码(修改后不用再在创建app后添加那句配置代码,图中我已修改源码)
- 在pycharm里按ctrl点flask_restful进入一个__init__.py文件
- 再点from flask_restful.representations.json import output_json中的json或output_json
- 在settings = current_app.config.get('RESTFUL_JSON', {})的下一行添加settings['ensure_ascii'] = False
app.config.update(RESTFUL_JSON=dict(ensure_ascii=False))是通过app.config指定flask_restful取消ensure_ascii。