技术学习报告
因为我在团队中负责后端和后台管理系统,所以下面分两部分来进行报告。
后端技术
这次的项目中,后端使用nginx+python flask+mysql&redis作为技术栈。因为时间关系,原本想要在服务器上添加jenkins自动化部署以及Docker容器部署的功能都没有实现。
Nginx
在服务器上的Nginx使用,主要还是利用其做反向代理以及https认证。反向代理是Nginx很常用的功能,这里我就不详细说,主要是部署api.leo-lee.cn作为Restful后端的域名,manage.leo-lee.cn作为后台管理系统的访问域名。
而https认证,我使用了Let’s Encrypt提供的免费证书。使用它们提供的软件certbot来生成ssl证书,如下指令。-d参数后面接的是你的网站的地址,-w参数后面是证书生成的位置。
sudo certbot certonly --webroot -w /usr/share/nginx/html -d myapp.your-domain.com
然后给nginx配置好证书位置,就能使域名拥有https功能,如下面例子。
# HTTP - only for obtaining cert
server {
listen 80;
server_name myapp.your-domain.com;
location /.well-known {
root /usr/share/nginx/html;
allow all;
}
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS - serve nodejs app
server {
listen 443 ssl;
server_name myapp.your-domain.com;
ssl_certificate /etc/letsencrypt/live/myapp.your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myapp.your-domain.com/privkey.pem;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://localhost:3000/;
proxy_ssl_session_reuse off;
proxy_set_header Host $http_host;
proxy_cache_bypass $http_upgrade;
proxy_redirect off;
}
}
不过体验下来,其实感觉用nginx来配https不算体验特别好,因为如果你原先的服务是http的,利用nginx来处理https并proxy的话,那么原先服务里可能生成的url是http的,并写入到数据库中。那么返回的数据里就很可能出现http链接了。
最好的话,如果后端框架本身就可以使用https功能,就在程序里设置好,nginx仅作为反向代理服务器。利用nginx作为https加密,也只是为那些老旧的http服务程序,做一个权宜之计。
Flask
服务器框架主要使用python的flask轻量服务器,然后使用flask-restful作为Restful API的快速开发,具体一个资源Resource是按照下面的方式组织。
class CategorySingle(Resource):
def get(self, restaurant_id, category_id):
category = Category.find_by_id(category_id)
return {
'category': category.to_json()
}, 200
@jwt_required
def put(self, restaurant_id, category_id):
current_user = User.find_by_username(get_jwt_identity())
restaurant = Restaurant.find_by_id(restaurant_id)
category = Category.find_by_id(category_id)
# TODO: change category priority
if restaurant.owner_id == current_user.id:
return {
'message': 'Change category %s priority success.' % category.name
}
else:
return {
'message': 'This restaurant is not yours.'
}, 403
@jwt_required
def delete(self, restaurant_id, category_id):
current_user = User.find_by_username(get_jwt_identity())
restaurant = Restaurant.find_by_id(restaurant_id)
category = Category.find_by_id(category_id)
if restaurant.owner_id == current_user.id:
try:
db.session.delete(category)
db.session.commit()
return {
'message': 'Delete category %s success.' % category.name
}, 200
except:
db.session.rollback()
return {
'message': 'Something went wrong.'
}, 500
else:
return {
'message': 'This restaurant is not yours.'
}, 403
然后在路由的blueprint上把该资源定义到指定的路径即可,如下。
# category routes
api.add_resource(category_resources.CategorySellerAll, '/seller/restaurants/<int:restaurant_id>/categories')
api.add_resource(category_resources.CategorySingle, '/api/restaurants/<int:restaurant_id>/categories/<int:category_id>')
而且,在后端服务器上,我使用了jwt作为认证的token,在用户登录或者注册后,服务器将会返回access_token和refresh_token。其中access_token是授权口令,利用这个口令可以访问有权限要求的接口。而refresh_token是刷新口令,因为access_token的有效时间是2小时,当失效了,就要使用refresh_token来重新获取access_token。需要在flask中进行如下设置。
JWT_SECRET_KEY = os.environ.get('JWT_SECRET_KEY') or 'jwt-secret-string'
JWT_BLACKLIST_ENABLED = True
JWT_BLACKLIST_CHECKED = ['access', 'refresh']
JWT_ACCESS_TOKEN_EXPIRES = timedelta(hours=2)
JWT_REFRESH_TOKEN_EXPIRES = timedelta(days=1)
Redis
在后端服务器中,redis主要是用于用户logout时,对access_token和refresh_token加入黑名单并设置过期时间,还有利用redis来存储验证码的key和value,以及微信服务器的access_token(因为创建小程序码要用)。
后台管理系统
后台管理系统主要以nodejs+webpack+vue作为技术栈,并使用element-ui作为样式。使用axios来进行promise式的网络请求。具体是参考vue-manage-system这个项目作为框架的。
Vue
vue中,主要是使用vue-router作为路由配置工具,配置方法如下。
export default new Router({
routes: [
{
path: '/',
redirect: '/dashboard'
},
{
path: '/',
component: resolve => require(['../components/common/Home.vue'], resolve),
meta: { title: '自述文件' },
children:[
{
path: '/dashboard',
component: resolve => require(['../components/page/Dashboard.vue'], resolve),
meta: { title: '系统首页' }
},
{
path: '/restaurants',
component: resolve => require(['../components/page/Restaurants.vue'], resolve),
meta: { title: '餐馆列表'}
}
]
}
]
});
为了充分利用vue的组件属性,将一个单页应用(SPA)的框架分成Header,Sidebar,Tags,Home部分,在Home部分留下router-view标签,那么每个不同的页面就能通过router设置在这个标签里面加载对应的组件。具体代码我就不放上来了。
在使用axios的过程中,最主要的问题是部署时的配置问题。一开始以为程序错误,原来是devserver没有配置好,应该要按如下配置。
module.exports = {
baseUrl: '/',
productionSourceMap: false,
devServer: {
proxy: {
'/api': {
target: 'https://api.leo-lee.cn',
changeOrigin: true,
pathRewrite: {
'/api': ''
}
}
},
disableHostCheck: true
}
};
这里注意,如果不开disableHostCheck这个功能,就会使得配置了https的应用访问失败。