JSON是http请求和响应使用的传输格式, 那么Web服务就应该实现以下两个功能:
- 把资源转换成json数据——发送给客户端
- 把json数据转换成资源——把客户端发送过来的json数据转换成资源存储在数据库中
一. 把资源转换成json数据
1) app/models.py
class Post(db.Model):
#...
def to_json(self):
json_post = {
'url': url_for('api.get_post', id = self.id, _external=True),
'body': self.body,
'body_html': self.body_html,
'timestamp': self.timestamp,
'author': url_for('api.get_user', id=self.author_id, _external=True),
'comments': url_for('api.get_post_comments', self.id, _external=True),
'comment_count': self.comments.count()
}
return json_post
#_external参数是为了生成完整的url, 在客户端只能使用完整的url
#上面的代码还说明了生成json数据时可以使用虚构的属性(ps:comment_count)
class User(db.Model):
#...
def to_json(self):
json_user = {
'url': url_for('api.get_user', id=self.id, _external=True),
'username': self.username,
'member_since': self.member_since,
'last_seen': self.last_seen,
'posts': url_for('api.get_user_posts', self.id, _external=True),
'followed_posts': url_for('api.get_user_followed_posts', self.id, _external=True),
'post_count': self.posts.count()
}
return json_user
#为了保护隐私, 用户的某些属性没有加入响应, 比如email和role等
#这说明提供给客户端的资源没有必要和数据库模型内部完全一致
二. 把json转换成资源
1)app/models.py
from app.exceptions import ValidationError
class Post(db.Model):
#...
@staticmethod
def from_json(json_post):
body = json_post.get('body')
if body is None or body == '':
raise ValidationError('post dose not have a body')
return Post(body=body)
#我们创建Post实例时只用了json中的body字段
#不需要body_html字段, because我们之前为数据库的Post表的body字段设置了事件监听, 一旦实例的body属性被修改就会自动修改body_html
#不需要timestamp字段, because timestamp有默认值, 即创建时间
#不需要author, because author的值不是客户端确定的, author的值是通过认证的用户
#不需要url, because url的值也不是客户端确定的, 是数据库分配id后决定的
当body值不存在的时候, 引发ValidationError, 由调用函数的上层函数处理这个错误。
为了不再视图函数中捕获该错误, 我们在api蓝本全局中处理该错误:
2) app/api__1_0/errors.py
@api.errorhandler(ValidationError)
der validation_error(e):
return bad_request(e.args[0])
#只要在api注册的路由中遇到ValidationError的错误, 就会由该函数处理返回响应。
这样的话视图函数就会简洁一些:
3)app/api_1_0/posts.py
@api.route('/posts', method=['POST'])
def new_post():
post = Post.from_json(request.json)
post.author = g.current_user
db.session.add(post)
db.session.commit()
return jsonify(post.to_json())
#当用户编辑完文章点击提交按钮时, 客户端对文章内容进行json编码, 并把内容提交到响应url
#由于该url是api注册的, 要先进行认证, 认证通过后, g.current_user存储当前用户
#执行该视图函数, 先从请求对象中获取json_post, 转换成资源post
#post.author就是g.current_user