提交 421ba693 编写于 作者: checklate's avatar checklate

新增v1 API,并使用postman调用成功

上级 cf0f3368
......@@ -43,4 +43,7 @@ def create_app(config_name):
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/auth')
from .api import api as api_blueprint
app.register_blueprint(api_blueprint, url_prefix='/api/v1')
return app
from flask import Blueprint
api = Blueprint('api', __name__)
from . import authentication, posts, users, comments, errors
from flask import g, jsonify
from flask_httpauth import HTTPBasicAuth
from ..models import User
from . import api
from .errors import unauthorized, forbidden
auth = HTTPBasicAuth()
@auth.verify_password
def verify_password(username_or_token, password):
if username_or_token == '':
return False
if password == '':
g.current_user = User.verify_auth_token(token=username_or_token)
g.token_used = True
return g.current_user is not None
user = User.query.filter_by(username=username_or_token).first()
if not user:
return False
g.current_user = user
g.token_used = False
return user.verify_password(password)
@auth.error_handler
def auth_error():
return unauthorized('Invalid credentials')
@api.before_request
@auth.login_required
def before_request():
pass
# if not g.current_user.is_anonymous:
# return forbidden('Unconfirmed account')
@api.route('/tokens/', methods=['POST'])
def get_token():
if g.token_used:
return unauthorized('Invalid credentials')
return jsonify({'token': g.current_user.generate_auth_token()})
from flask import jsonify, request, g, url_for, current_app
from .. import db
from ..models import Post, Permission, Comment
from . import api
from .decorators import permission_required
@api.route('/comments/')
def get_comments():
page = request.args.get('page', 1, type=int)
pagination = Comment.query.order_by(Comment.timestamp.desc()).paginate(
page=page, per_page=current_app.config['FLASKY_COMMENTS_PER_PAGE'],
error_out=False)
comments = pagination.items
prev = None
if pagination.has_prev:
prev = url_for('api.get_comments', page=page-1)
next = None
if pagination.has_next:
next = url_for('api.get_comments', page=page+1)
return jsonify({
'comments': [comment.to_json() for comment in comments],
'prev': prev,
'next': next,
'count': pagination.total
})
@api.route('/comments/<int:id>')
def get_comment(id):
comment = Comment.query.get_or_404(id)
return jsonify(comment.to_json())
@api.route('/posts/<int:id>/comments/')
def get_post_comments(id):
post = Post.query.get_or_404(id)
page = request.args.get('page', 1, type=int)
pagination = post.comments.order_by(Comment.timestamp.asc()).paginate(
page=page, per_page=current_app.config['FLASKY_COMMENTS_PER_PAGE'],
error_out=False)
comments = pagination.items
prev = None
if pagination.has_prev:
prev = url_for('api.get_post_comments', id=id, page=page-1)
next = None
if pagination.has_next:
next = url_for('api.get_post_comments', id=id, page=page+1)
return jsonify({
'comments': [comment.to_json() for comment in comments],
'prev': prev,
'next': next,
'count': pagination.total
})
@api.route('/posts/<int:id>/comments/', methods=['POST'])
@permission_required(Permission.COMMENT)
def new_post_comment(id):
post = Post.query.get_or_404(id)
comment = Comment.from_json(request.json)
comment.author = g.current_user
comment.post = post
db.session.add(comment)
db.session.commit()
return jsonify(comment.to_json()), 201, \
{'Location': url_for('api.get_comment', id=comment.id)}
from functools import wraps
from flask import g
from .errors import forbidden
def permission_required(permission):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not g.current_user.can(permission):
return forbidden('Insufficient permissions')
return f(*args, **kwargs)
return decorated_function
return decorator
from flask import jsonify
from app.exceptions import ValidationError
from . import api
def bad_request(message):
response = jsonify({'error': 'bad request', 'message': message})
response.status_code = 400
return response
def unauthorized(message):
response = jsonify({'error': 'unauthorized', 'message': message})
response.status_code = 401
return response
def forbidden(message):
response = jsonify({'error': 'forbidden', 'message': message})
response.status_code = 403
return response
@api.errorhandler(ValidationError)
def validation_error(e):
return bad_request(e.args[0])
from flask import jsonify, request, g, url_for, current_app
from .. import db
from ..models import Post, Permission
from . import api
from .decorators import permission_required
from .errors import forbidden
@api.route('/posts/')
def get_posts():
page = request.args.get('page', 1, type=int)
pagination = Post.query.paginate(
page=page, per_page=current_app.config['FLASKY_POSTS_PER_PAGE'],
error_out=False)
posts = pagination.items
prev = None
if pagination.has_prev:
prev = url_for('api.get_posts', page=page-1)
next = None
if pagination.has_next:
next = url_for('api.get_posts', page=page+1)
return jsonify({
'posts': [post.to_json() for post in posts],
'prev': prev,
'next': next,
'count': pagination.total
})
@api.route('/posts/<int:id>')
def get_post(id):
post = Post.query.get_or_404(id)
return jsonify(post.to_json())
@api.route('/posts/', methods=['POST'])
@permission_required(Permission.WRITE)
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()), 201, \
{'Location': url_for('api.get_post', id=post.id)}
@api.route('/posts/<int:id>', methods=['PUT'])
@permission_required(Permission.WRITE)
def edit_post(id):
post = Post.query.get_or_404(id)
if g.current_user != post.author and \
not g.current_user.can(Permission.ADMIN):
return forbidden('Insufficient permissions')
post.body = request.json.get('body', post.body)
db.session.add(post)
db.session.commit()
return jsonify(post.to_json())
from flask import jsonify, request, current_app, url_for
from . import api
from ..models import User, Post
@api.route('/users/<int:id>')
def get_user(id):
user = User.query.get_or_404(id)
return jsonify(user.to_json())
@api.route('/users/<int:id>/posts/')
def get_user_posts(id):
user = User.query.get_or_404(id)
page = request.args.get('page', 1, type=int)
pagination = user.posts.order_by(Post.timestamp.desc()).paginate(
per_page=current_app.config['FLASKY_POSTS_PER_PAGE'],
error_out=False)
posts = pagination.items
prev = None
if pagination.has_prev:
prev = url_for('api.get_user_posts', id=id, page=page-1)
next = None
if pagination.has_next:
next = url_for('api.get_user_posts', id=id, page=page+1)
return jsonify({
'posts': [post.to_json() for post in posts],
'prev': prev,
'next': next,
'count': pagination.total
})
@api.route('/users/<int:id>/timeline/')
def get_user_followed_posts(id):
user = User.query.get_or_404(id)
page = request.args.get('page', 1, type=int)
pagination = user.followed_posts.order_by(Post.timestamp.desc()).paginate(
page=page, per_page=current_app.config['FLASKY_POSTS_PER_PAGE'],
error_out=False)
posts = pagination.items
prev = None
if pagination.has_prev:
prev = url_for('api.get_user_followed_posts', id=id, page=page-1)
next = None
if pagination.has_next:
next = url_for('api.get_user_followed_posts', id=id, page=page+1)
return jsonify({
'posts': [post.to_json() for post in posts],
'prev': prev,
'next': next,
'count': pagination.total
})
class ValidationError(ValueError):
pass
......@@ -19,7 +19,7 @@ from datetime import datetime
import bleach
from flask import current_app, request, url_for
from itsdangerous import Serializer
from authlib.jose import jwt, JoseError
from markdown import markdown
from wtforms import ValidationError
......@@ -114,6 +114,7 @@ class Post(db.Model):
'body': self.body,
'body_html': self.body_html,
'timestamp': self.timestamp,
'update_timestamp': self.update_timestamp,
'author_url': url_for('api.get_user', id=self.author_id),
'comments_url': url_for('api.get_post_comments', id=self.id),
'comment_count': self.comments.count()
......@@ -128,6 +129,9 @@ class Post(db.Model):
return Post(body=body)
db.event.listen(Post.body, 'set', Post.on_changed_body)
class Follow(db.Model):
__tablename__ = 'follows'
follower_id = db.Column(db.Integer, db.ForeignKey('users.id'),
......@@ -229,17 +233,38 @@ class User(UserMixin, db.Model):
return Post.query.join(Follow, Follow.followed_id == Post.author_id) \
.filter(Follow.follower_id == self.id)
def generate_auth_token(self, expiration):
s = Serializer(current_app.config['SECRET_KEY'])
return s.dumps({'id': self.id}).decode('utf-8')
# def generate_auth_token(self, expiration):
# s = Serializer(current_app.config['SECRET_KEY'])
# return s.dumps({'id': self.id})
#
# @staticmethod
# def verify_auth_token(token):
# s = Serializer(current_app.config['SECRET_KEY'])
# try:
# data = s.loads(token)
# except:
# return None
# return User.query.get(data['id'])
def generate_auth_token(self, **kwargs):
"""生成用于邮箱验证的JWT(json web token)"""
# 签名算法
header = {'alg': 'HS256'}
# 待签名的数据负载
data = {'id': self.id}
data.update(**kwargs)
token = jwt.encode(header=header, payload=data, key=current_app.config['SECRET_KEY'])
return token.decode()
@staticmethod
def verify_auth_token(token):
s = Serializer(current_app.config['SECRET_KEY'])
"""用于验证用户注册和用户修改密码或邮箱的token, 并完成相应的确认操作"""
try:
data = s.loads(token)
except:
return None
data = jwt.decode(token, current_app.config['SECRET_KEY'])
except JoseError:
return False
return User.query.get(data['id'])
def to_json(self):
......@@ -285,6 +310,24 @@ class Comment(db.Model):
markdown(value, output_format='html'),
tags=allowed_tags, strip=True))
def to_json(self):
json_comment = {
'url': url_for('api.get_comment', id=self.id),
'post_url': url_for('api.get_post', id=self.post_id),
'body': self.body,
'body_html': self.body_html,
'timestamp': self.timestamp,
'author_url': url_for('api.get_user', id=self.author_id),
}
return json_comment
@staticmethod
def from_json(json_comment):
body = json_comment.get('body')
if body is None or body == '':
raise ValidationError('comment does not have a body')
return Comment(body=body)
db.event.listen(Comment.body, 'set', Comment.on_changed_body)
......@@ -292,6 +335,3 @@ db.event.listen(Comment.body, 'set', Comment.on_changed_body)
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
db.event.listen(Post.body, 'set', Post.on_changed_body)
......@@ -10,7 +10,7 @@ import os
import sys
import click
from app import create_app, db
from app.models import User, Role
from app.models import User, Role, Follow, Permission, Post, Comment
from flask_migrate import Migrate
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
......@@ -19,5 +19,6 @@ migrate = Migrate(app, db)
@app.shell_context_processor
def make_shell_context():
return dict(db=db, User=User, Role=Role)
return dict(db=db, User=User, Follow=Follow, Role=Role,
Permission=Permission, Post=Post, Comment=Comment)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册