如果有几个原因可以让你爱上flask这个极其灵活的库,我想蓝图绝对应该算上一个,部署蓝图以后,你会发现整个程序结构非常清晰,模块之间相互不影响。蓝图对restful api的最明显效果就是版本控制;而对整个项目来说,总要有后台管理系统吧,总要有web管理吧,但这些东西不能全部放到view.py。不单单是这样,如果你是一个经验丰富的程序员,你应该知道,一个程序最好只有一个入口点,从这个入口点进去,全是单向的,就像一棵树一样,入口点就在树根,然后蔓延到树干,树枝。树枝和树枝之间最好不要太多交集,也就是我们通常所说的 低耦合。

  好了,说了这么多,我们举个简单例子看看。假设你的一款app已经上架,现在公司要开发新的版本,新版本不但要增加新的接口,可能还要修改以前老的接口。但如果修改老的接口,那老版本的用户又不能正常访问了,要是用户更新不及时,是不是这些接口就不能访问了?如果我需要这个接口可以维持一阵子,过一段时间后,再停用,该如何实现呢?

  下面这个截图是我原先的项目结构。

  

  如果要增加接口和修改接口,只能在view.py里面做,我这个只是举例,在真实的项目中,一个中等app,最少有上百个接口,如何快速找到接口,定位问题,是一门很好的学问。

  首先,先在app下增加一个文件夹,api_1_0作为版本1.0存放地方,是我们的一个树枝,把之前的view.py迁移到里面去。结构如下:

  

  我们之前总是在view.py里面运行app,这个肯定不行,不能把入口点放在某一个树枝吧。要把整个app拉出来,在上一级来启动项目,那么就再增加一个run.py文件,专门来运行整个项目,整个run.py代码如下

# coding:utf-8
from flask import Flask
from config import Conf
import redis
from qiniu import Auth, put_file, etag, urlsafe_base64_encodedef create_app():app = Flask(__name__)app.config.from_object(Conf)app.secret_key = app.config['SECRET_KEY']app.redis = redis.Redis(host=app.config['REDIS_HOST'], port=app.config['REDIS_PORT'],db=app.config['REDIS_DB'], password=app.config['REDIS_PASSWORD'])app.q = Auth(access_key=app.config['QINIU_ACCESS_KEY'], secret_key=app.config['QINIU_SECRET_KEY'])app.bucket_name = app.config['BUCKET_NAME']app.debug = app.config['DEBUG']return appif __name__ == '__main__':app = create_app()app.run(debug=app.debug, host='0.0.0.0', port=5001)

这边就是整个项目的总起点,是整个树的根(其实这边还可以再把run.py放在外面,这边接下来会继续讲)。

恩,看起来好了,但我们还是没有看到蓝图。好了,这边就说到了,之前view.py里面全部用的app.route,之前没问题,因为它本身就是根,可是你现在已经不是根了,那就要变换一下,在api_1_0/__init__.py里,添加如下代码:

# coding:utf-8
from flask import Blueprintapi = Blueprint('api', __name__)from . import view

这边解释一下,首先定义一个蓝图,这个不用讲了,你用之前,肯定要定义一下吧,蓝图名字就叫api。下面一行代码有点意思,from . import view, 这个代码,我发现用蓝图的人,很多都忘写了。如果你是一个python老手,你应该知道,导入python文件,其实就是运行导入的那个文件。在这边,你导入一次,就是就是告诉上级,这边还有个view.py文件,里面覆盖了一些接口。整个根就知道,哦,如果有接口访问这,我就指向你这边。好好理解上面的粗体字。

好了,树枝这边已经定义了,那树根那边也应该重新写一下。一个request到树根那,树根根据url来指向树枝,树根怎么指向呢?代码如下:

def create_app():app = Flask(__name__)app.config.from_object(Conf)app.secret_key = app.config['SECRET_KEY']app.redis = redis.Redis(host=app.config['REDIS_HOST'], port=app.config['REDIS_PORT'],db=app.config['REDIS_DB'], password=app.config['REDIS_PASSWORD'])app.q = Auth(access_key=app.config['QINIU_ACCESS_KEY'], secret_key=app.config['QINIU_SECRET_KEY'])app.bucket_name = app.config['BUCKET_NAME']app.debug = app.config['DEBUG']from .app_1_0 import api as api_1_0_blueprintapp.register_blueprint(api_1_0_blueprint, url_prefix='/api/v1000')return app

看到增加的2行代码没有,这个就是树根指向树枝的代码。

好了,我们现在整个流程应该清楚了,请求从树根进来,树根根据蓝图指向某一个树枝,这个树枝具体处理请求,返回。

那自然,最后一段树枝具体处理,也要改变咯。

原先view.py代码改变一下,如下:

# coding:utf-8
from flask import Flask, request, jsonify, g, render_template, redirect, url_for, session, current_app
from app.model import User, db_session, MaintainRecord
import hashlib
import time
import uuid
from app.util import message_validate
import random
from functools import wrapsfrom . import apidef login_check(f):@wraps(f)def decorator(*args, **kwargs):token = request.headers.get('token')if not token:return jsonify({'code': 0, 'message': '需要验证'})phone_number = current_app.redis.get('token:%s' % token)if not phone_number or token != current_app.redis.hget('user:%s' % phone_number, 'token'):return jsonify({'code': 2, 'message': '验证信息错误'})return f(*args, **kwargs)return decorator@api.before_request
def before_request():token = request.headers.get('token')phone_number = current_app.redis.get('token:%s' % token)if phone_number:g.current_user = User.query.filter_by(phone_number=phone_number).first()g.token = tokenreturn@api.route('/login', methods=['POST'])
def login():phone_number = request.get_json().get('phone_number')password = request.get_json().get('password')user = User.query.filter_by(phone_number=phone_number).first()if not user:return jsonify({'code': 0, 'message': '没有此用户'})if user.password != password:return jsonify({'code': 0, 'message': '密码错误'})m = hashlib.md5()m.update(phone_number)m.update(password)m.update(str(int(time.time())))token = m.hexdigest()pipeline = current_app.redis.pipeline()pipeline.hmset('user:%s' % user.phone_number, {'token': token, 'nickname': user.nickname, 'app_online': 1})pipeline.set('token:%s' % token, user.phone_number)pipeline.expire('token:%s' % token, 3600*24*30)pipeline.execute()return jsonify({'code': 1, 'message': '成功登录', 'nickname': user.nickname, 'token': token})@api.route('/user')
@login_check
def user():user = g.current_usernickname = current_app.redis.hget('user:%s' % user.phone_number, 'nickname')return jsonify({'code': 1, 'nickname': nickname, 'phone_number': user.phone_number})@api.route('/logout')
@login_check
def logout():user = g.current_userpipeline = current_app.redis.pipeline()pipeline.delete('token:%s' % g.token)pipeline.hmset('user:%s' % user.phone_number, {'app_online': 0})pipeline.execute()return jsonify({'code': 1, 'message': '成功注销'})@api.route('/get-qiniu-token')
def get_qiniu_token():key = uuid.uuid4()token = current_app.q.upload_token(current_app.bucket_name, key, 3600)return jsonify({'code': 1, 'key': key, 'token': token})@api.route('/set-head-picture', methods=['POST'])
@login_check
def set_head_picture():head_picture = request.get_json().get('head_picture')user = g.current_useruser.head_picture = head_picturetry:db_session.commit()except Exception as e:print edb_session.rollback()return jsonify({'code': 0, 'message': '未能成功上传'})current_app.redis.hset('user:%s' % user.phone_number, 'head_picture', head_picture)return jsonify({'code': 1, 'message': '成功上传'})@api.route('/register-step-1', methods=['POST'])
def register_step_1():"""接受phone_number,发送短信"""phone_number = request.get_json().get('phone_number')user = User.query.filter_by(phone_number=phone_number).first()if user:return jsonify({'code': 0, 'message': '该用户已经存在,注册失败'})validate_number = str(random.randint(100000, 1000000))result, err_message = message_validate(phone_number, validate_number)if not result:return jsonify({'code': 0, 'message': err_message})pipeline = current_app.redis.pipeline()pipeline.set('validate:%s' % phone_number, validate_number)pipeline.expire('validate:%s' % phone_number, 60)pipeline.execute()return jsonify({'code': 1, 'message': '发送成功'})@api.route('/register-step-2', methods=['POST'])
def register_step_2():"""验证短信接口"""phone_number = request.get_json().get('phone_number')validate_number = request.get_json().get('validate_number')validate_number_in_redis = current_app.redis.get('validate:%s' % phone_number)if validate_number != validate_number_in_redis:return jsonify({'code': 0, 'message': '验证没有通过'})pipe_line = current_app.redis.pipeline()pipe_line.set('is_validate:%s' % phone_number, '1')pipe_line.expire('is_validate:%s' % phone_number, 120)pipe_line.execute()return jsonify({'code': 1, 'message': '短信验证通过'})@api.route('/register-step-3', methods=['POST'])
def register_step_3():"""密码提交"""phone_number = request.get_json().get('phone_number')password = request.get_json().get('password')password_confirm = request.get_json().get('password_confirm')if len(password) < 7 or len(password) > 30:# 这边可以自己拓展条件return jsonify({'code': 0, 'message': '密码长度不符合要求'})if password != password_confirm:return jsonify({'code': 0, 'message': '密码和密码确认不一致'})is_validate = current_app.redis.get('is_validate:%s' % phone_number)if is_validate != '1':return jsonify({'code': 0, 'message': '验证码没有通过'})pipeline = current_app.redis.pipeline()pipeline.hset('register:%s' % phone_number, 'password', password)pipeline.expire('register:%s' % phone_number, 120)pipeline.execute()return jsonify({'code': 1, 'message': '提交密码成功'})@api.route('/register-step-4', methods=['POST'])
def register_step_4():"""基本资料提交"""phone_number = request.get_json().get('phone_number')nickname = request.get_json().get('nickname')is_validate = current_app.redis.get('is_validate:%s' % phone_number)if is_validate != '1':return jsonify({'code': 0, 'message': '验证码没有通过'})password = current_app.redis.hget('register:%s' % phone_number, 'password')new_user = User(phone_number=phone_number, password=password, nickname=nickname)db_session.add(new_user)try:db_session.commit()except Exception as e:print edb_session.rollback()return jsonify({'code': 0, 'message': '注册失败'})finally:current_app.redis.delete('is_validate:%s' % phone_number)current_app.redis.delete('register:%s' % phone_number)return jsonify({'code': 1, 'message': '注册成功'})@api.teardown_request
def handle_teardown_request(exception):db_session.remove()

  这边有好几个需要注意的点。

  第一,运行的代码取消掉了,因为统一从run.py来运行,作为入口点。

  第二,原先的app.route也全部改成api.route, api也从本地的__init__.py中导入。因为你现在代表树枝,不能代表整棵树了。

  第三,app.redis,可以用current_app.redis来代替,其实就是我在run.py中定义的一些变量,在整颗树中使用。

  

好了,我们运行一下python run.py试试看吧。这边需要注意的是,url也不一样了哦,client.py中,要用不同的url了。client.py运行代码如下:

if __name__ == '__main__':api = APITest('http://127.0.0.1:5001/api/v1000')u = api.login('13565208554', '123456')print uu1 = api.user()print u1api.logout()

python run.py返回的url,看看详细情况。

  

  整个运行情况问题,那么回到最上面提到的问题。老板看我们第一个版本做的非常稳定,要开发第二个版本,先不管新增的功能,老板觉得你上一个版本的login接口写的不好,直接明文把用户名和密码传输过去了,安全问题得不到保障。现在要求是login接口重写,其他不变,并且上个版本的接口暂时还不能停止。

  如果按照我们以前的想法,再增加一个不同的接口呗。嗯!方法不错,要是要修改20个接口呢?重写吗?怎么分得清哪个对应哪个版本?就算这次分清了,再更新5个版本,你还知道原来写的什么吗?

  好了,上面一堆废话就是为了衬托项目结构的重要性的。版本1.1,我们重新写一个树枝,这个树枝跟1.0的树枝没有任何交集。有了1.0,1.1就非常好写了。

  把整个api_1_0直接复制到api_1_1中。没错,你没看错,就是整个复制过去。

  然后把api_1_1中的__init__.py修改一下,代码如下:

# coding:utf-8
from flask import Blueprintapi = Blueprint('api1_1', __name__)from . import view

  其实就是把蓝图名字修改了一下。整个项目结构如下图:

  在run.py中,增加如下代码。

def create_app():app = Flask(__name__)app.config.from_object(Conf)app.secret_key = app.config['SECRET_KEY']app.redis = redis.Redis(host=app.config['REDIS_HOST'], port=app.config['REDIS_PORT'],db=app.config['REDIS_DB'], password=app.config['REDIS_PASSWORD'])app.q = Auth(access_key=app.config['QINIU_ACCESS_KEY'], secret_key=app.config['QINIU_SECRET_KEY'])app.bucket_name = app.config['BUCKET_NAME']app.debug = app.config['DEBUG']from app_1_0 import api as api_1_0_blueprintapp.register_blueprint(api_1_0_blueprint, url_prefix='/api/v1000')from api_1_1 import api as api_1_1_blueprintapp.register_blueprint(api_1_1_blueprint, url_prefix='/api/v1100')return app

好了,整个复制过程完工,看看实际情况吧。

我们先不修改登录接口,就看一下效果。client.py运行代码

if __name__ == '__main__':api = APITest('http://127.0.0.1:5001/api/v1000')u = api.login('13565208554', '123456')print uu1 = api.user()print u1api.logout()api = APITest('http://127.0.0.1:5001/api/v1100')u = api.login('13565208554', '123456')print uu1 = api.user()print u1api.logout()

看看ide调试结果吧。

是不是2个版本同时支持了?结构是不是非常非常清晰?

  结构调整到这,那就听老板的意思,开始写新的接口吧。老板说,上次登录接口不安全,要修改一下。一般来说,验证时,都是把用户名,和 密码+随机值+时间戳 的加密方式传过去,我们也萧规曹随吧,新版本的login接口代码如下: 

@api.route('/login', methods=['POST'])
def login():phone_number = request.get_json().get('phone_number')encryption_str = request.get_json().get('encryption_str')random_str = request.get_json().get('random_str')time_stamp = request.get_json().get('time_stamp')user = User.query.filter_by(phone_number=phone_number).first()if not user:return jsonify({'code': 0, 'message': '没有此用户'})password_in_sql = user.passwords = hashlib.sha256()s.update(password_in_sql)s.update(random_str)s.update(time_stamp)server_encryption_str = s.hexdigest()if server_encryption_str != encryption_str:return jsonify({'code': 0, 'message': '密码错误'})m = hashlib.md5()m.update(phone_number)m.update(user.password)m.update(str(int(time.time())))token = m.hexdigest()pipeline = current_app.redis.pipeline()pipeline.hmset('user:%s' % user.phone_number, {'token': token, 'nickname': user.nickname, 'app_online': 1})pipeline.set('token:%s' % token, user.phone_number)pipeline.expire('token:%s' % token, 3600*24*30)pipeline.execute()return jsonify({'code': 1, 'message': '成功登录', 'nickname': user.nickname, 'token': token})

代码稍微解释一下,encryption_str就是加密串,是由密码+随机值+时间戳用sha256加密的。传到服务器,服务器也这样加密一下,然后看看2者是不是一致。传输过程不涉及密码传输。就这么简单。

客户端就不能用之前的代码了,需要重写一下。代码如下:

# coding:utf-8
import requests
import json
from qiniu import put_file
import time
import random
import hashlibclass API_1_0(object):base_url = 'http://127.0.0.1:5001/api/v1000'def __init__(self):self.headers = {}self.token = Noneself.qiniu_token = Noneself.qiniu_key = Noneself.qiniu_base_url = 'http://7xk6rc.com1.z0.glb.clouddn.com/'def login(self, phone_number, password, path='/login'):payload = {'phone_number': phone_number, 'password': password}self.headers = {'content-type': 'application/json'}response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)response_data = json.loads(response.content)self.token = response_data.get('token')return response_datadef user(self, path='/user'):self.headers = {'token': self.token}response = requests.get(url=self.base_url + path, headers=self.headers)response_data = json.loads(response.content)return response_datadef logout(self, path='/logout'):self.headers = {'token': self.token}response = requests.get(url=self.base_url + path, headers=self.headers)response_data = json.loads(response.content)return response_datadef get_qiniu_token(self, path='/get-qiniu-token'):response = requests.get(url=self.base_url + path)response_data = json.loads(response.content)self.qiniu_token = response_data.get('token')self.qiniu_key = response_data.get('key')if self.qiniu_token and self.qiniu_key:print '成功获取qiniu_token和qiniu_key,分别为%s和%s' % (self.qiniu_token.encode('utf-8'), self.qiniu_key.encode('utf-8'))localfile = '/home/yudahai/PycharmProjects/blog01/app/my-test.png'ret, info = put_file(self.qiniu_token, self.qiniu_key, localfile)print info.status_codeif info.status_code == 200:print '上传成功'self.head_picture = self.qiniu_base_url + self.qiniu_keyprint '其url为:' + self.head_picture.encode('utf-8')else:print '上传失败'return response_datadef set_head_picture(self, path='/set-head-picture'):payload = {'head_picture': self.head_picture}self.headers = {'token': self.token, 'content-type': 'application/json'}response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)response_data = json.loads(response.content)print response_data.get('message')return response_datadef register_step_1(self, phone_number, path='/register-step-1'):payload = {'phone_number': phone_number}self.headers = {'content-type': 'application/json'}response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)response_data = json.loads(response.content)print response_data.get('code')return response_datadef register_step_2(self, phone_number, validate_number, path='/register-step-2'):payload = {'phone_number': phone_number, 'validate_number': validate_number}self.headers = {'content-type': 'application/json'}response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)response_data = json.loads(response.content)print response_data.get('code')return response_datadef register_step_3(self, phone_number, password, password_confirm, path='/register-step-3'):payload = {'phone_number': phone_number, 'password': password, 'password_confirm': password_confirm}self.headers = {'content-type': 'application/json'}response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)response_data = json.loads(response.content)print response_data.get('code')return response_datadef register_step_4(self, phone_number, nickname, path='/register-step-4'):payload = {'phone_number': phone_number, 'nickname': nickname}self.headers = {'content-type': 'application/json'}response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)response_data = json.loads(response.content)print response_data.get('code')return response_dataclass API_1_1(object):base_url = 'http://127.0.0.1:5001/api/v1100'def __init__(self):self.headers = {}self.token = Noneself.qiniu_token = Noneself.qiniu_key = Noneself.qiniu_base_url = 'http://7xk6rc.com1.z0.glb.clouddn.com/'def login(self, phone_number, password, path='/login'):random_str = str(random.randint(10000, 100000))time_stamp = str(int(time.time()))s = hashlib.sha256()s.update(password)s.update(random_str)s.update(time_stamp)encryption_str = s.hexdigest()payload = {'phone_number': phone_number, 'encryption_str': encryption_str, 'random_str': random_str, 'time_stamp': time_stamp}self.headers = {'content-type': 'application/json'}response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)response_data = json.loads(response.content)self.token = response_data.get('token')return response_datadef user(self, path='/user'):self.headers = {'token': self.token}response = requests.get(url=self.base_url + path, headers=self.headers)response_data = json.loads(response.content)return response_datadef logout(self, path='/logout'):self.headers = {'token': self.token}response = requests.get(url=self.base_url + path, headers=self.headers)response_data = json.loads(response.content)return response_datadef get_qiniu_token(self, path='/get-qiniu-token'):response = requests.get(url=self.base_url + path)response_data = json.loads(response.content)self.qiniu_token = response_data.get('token')self.qiniu_key = response_data.get('key')if self.qiniu_token and self.qiniu_key:print '成功获取qiniu_token和qiniu_key,分别为%s和%s' % (self.qiniu_token.encode('utf-8'), self.qiniu_key.encode('utf-8'))localfile = '/home/yudahai/PycharmProjects/blog01/app/my-test.png'ret, info = put_file(self.qiniu_token, self.qiniu_key, localfile)print info.status_codeif info.status_code == 200:print '上传成功'self.head_picture = self.qiniu_base_url + self.qiniu_keyprint '其url为:' + self.head_picture.encode('utf-8')else:print '上传失败'return response_datadef set_head_picture(self, path='/set-head-picture'):payload = {'head_picture': self.head_picture}self.headers = {'token': self.token, 'content-type': 'application/json'}response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)response_data = json.loads(response.content)print response_data.get('message')return response_datadef register_step_1(self, phone_number, path='/register-step-1'):payload = {'phone_number': phone_number}self.headers = {'content-type': 'application/json'}response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)response_data = json.loads(response.content)print response_data.get('code')return response_datadef register_step_2(self, phone_number, validate_number, path='/register-step-2'):payload = {'phone_number': phone_number, 'validate_number': validate_number}self.headers = {'content-type': 'application/json'}response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)response_data = json.loads(response.content)print response_data.get('code')return response_datadef register_step_3(self, phone_number, password, password_confirm, path='/register-step-3'):payload = {'phone_number': phone_number, 'password': password, 'password_confirm': password_confirm}self.headers = {'content-type': 'application/json'}response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)response_data = json.loads(response.content)print response_data.get('code')return response_datadef register_step_4(self, phone_number, nickname, path='/register-step-4'):payload = {'phone_number': phone_number, 'nickname': nickname}self.headers = {'content-type': 'application/json'}response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)response_data = json.loads(response.content)print response_data.get('code')return response_dataif __name__ == '__main__':api = API_1_0()u = api.login('13565208554', '123456')print uu1 = api.user()print u1api.logout()api = API_1_1()u = api.login('13565208554', '123456')print uu1 = api.user()print u1api.logout()

就是复制一遍,然后把base_url定义成不同,仅此而已。其实真是的ios, android也这样。当需要更新版本,他们复制之前的项目,改变一下base_url,然后修改需要修改的接口。这样2个版本同时支持的服务器端就写好了,是不是超级直观?直接运行一下吧。

{u'message': u'\u6210\u529f\u767b\u5f55', u'code': 1, u'nickname': u'\u6d4b\u8bd5\u7528\u62371', u'token': u'e5a74170d81feb106a066f18f9f2e966'}
{u'phone_number': u'13565208554', u'code': 1, u'nickname': u'\u6d4b\u8bd5\u7528\u62371'}
{u'message': u'\u6210\u529f\u767b\u5f55', u'code': 1, u'nickname': u'\u6d4b\u8bd5\u7528\u62371', u'token': u'e5a74170d81feb106a066f18f9f2e966'}
{u'phone_number': u'13565208554', u'code': 1, u'nickname': u'\u6d4b\u8bd5\u7528\u62371'}

返回的code都是1,两种方式登录都支持。老板要求都达到了,老板高兴,发奖金,我们也高兴(好吧,不做梦了)。

  好了,讲到这,大家应该对整个项目接口有非常清晰的了解了,以后再加接口或者更新版本,对你来说,都是不是问题了吧。

 

转载于:https://www.cnblogs.com/yueerwanwan0204/p/5522749.html

flask开发restful api系列(7)-蓝图与项目结构相关推荐

  1. flask开发restful api系列(5)-短信验证码

    我们现在开发app,注册用户的时候,不再像web一样,发送到个人邮箱了,毕竟个人邮箱在移动端填写验证都很麻烦,一般都采用短信验证码的方式.今天我们就讲讲这方面的内容. 首先,先找一个平台吧.我们公司找 ...

  2. vue安装Postcss_Flask和Vue.js构建全栈单页面web应用【通过Flask开发RESTful API】

    前言: 看了一些国外的关于介绍flask和vue的前后端分离的文章,但没看到比较通俗易懂,代码完善的,直到昨天看到一篇新出的文章,而且内容非常棒,所以翻译过来,供大家一起学习. 原文来自Develop ...

  3. python django开发api_基于Django框架开发Restful api

    在上篇文章中,我们讲解了restful api的主要概念,让大家有初步的了解.这一篇中,我们将通过python的Django框架,来学习开发restful 架构的接口. 一. 开发环境 开发语言:Py ...

  4. java接口构建英雄属性_Java开发学习心得(三):项目结构

    3 项目结构 经过前面一系列学习,差不多对Java的开发过程有了一定的了解,为了能保持一个良好的项目结构,考虑到接下来要进行开发,还需要学习一下Java的项目结构 下面以两个项目结构为参照 图1 图2 ...

  5. Python自动化开发 - RESTful API

    本节内容 1.  RESTful 简介 2.  RESTful 设计指南 3.  Django REST Framework 最佳实践 4.  理论拓展与开放平台 5.  API文档化与测试 一  R ...

  6. python flask高级编程之restful_python Flask实现restful api service

    一直在用node.js做后端,要逐步涉猎大数据范围,注定绕不过python,因此决定把一些成熟的东西用python来重写,一是开拓思路.通过比较来深入学习python:二是有目标,有动力,希望能持之以 ...

  7. php 开发restful api,用PHP创建RESTful API?

    如果您的服务支持所有CRUD操作,则始终建议实现RESTful接口.这样做并不是很难.我已经概述了下面的一些基础知识. RESTful服务只是做了一些事情: >它使用HTTP请求方法进行CRUD ...

  8. 007 使用SpringMVC开发restful API五--异常处理

    一:任务 1.任务 Spring Boot中默认的错误机制处理机制 自定义异常处理 二:Spring Boot中的默认错误处理机制 1.目前 浏览器访问的时候, restful 接口主要是根据状态码进 ...

  9. Flask restful api与blueprint结合实践

    所需依赖: Flask Flask-RESTful Python2.7 备注:flask-restful不能和flask的render_template模板结合使用,因为restfulapi的设计不是 ...

  10. 基于Flask开发企业级REST API应用(一)

    关于我 编程界的一名小小程序猿,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:github.com/hy ...

最新文章

  1. 3年测试经验的文艺青年,从京东测试团队浅谈纯功能测试人员该何去何从?
  2. 删除fedora多余内核:解决每次升级后旧内核还会存在的问题
  3. php过滤提交数据 防止sql注入***(8)
  4. python itertools模块位置_Python高效编程之itertools模块详解
  5. c语言在函数中只执行一次,请问大家,为什么我调用我定义的函数俩次,但是程序只执行一次...
  6. java 分享巧克力_[leetcode 双周赛 11] 1231 分享巧克力
  7. 绝不要在构造函数和析构过程中调用virtual函数
  8. 显示锁 java_第十三章:显示锁——Java并发编程实战
  9. anaconda新建环境在PyCharm执行import ssl失败
  10. python可用编程模块规模多大_哪些Python模块可用于编程竞赛?
  11. 域控服务器降级失败,降级域控制器时出错 - Windows Server | Microsoft Docs
  12. idea 查看jsp是否被引用_idea 查看jsp是否被引用_IntelliJ IDEA解析JSP中的Web路径
  13. Java XLSTransformer生成excel文件
  14. RLC电阻电感电容取值标准
  15. 【rmzt:进击的巨人炫彩主题】
  16. 用计算机弹平凡之路谱子,pen beat曲谱_penbeat平凡之路的谱子
  17. android 仿微信demo————登录功能实现(服务端)
  18. matlab爆内存电脑卡死,电脑卡死怎么办?电脑卡顿的原因和解决办法
  19. Kotlin中val和var的区别
  20. 西游记中的几位女妖怪

热门文章

  1. c++ static 关键字总结
  2. 彻底搞懂 MySQL 事务的隔离级别
  3. es 时间字段聚合_ES聚合命令
  4. 面试题--------3、string stringbuffer stringbuilder的区别
  5. java 富文本 xss_Jsoup 防止富文本 XSS 攻击
  6. 1.3使用command-line runners
  7. 【渝粤教育】国家开放大学2019年春季 2633轨道交通信号与通信系统 参考试题
  8. [渝粤教育] 西南科技大学 大学物理 在线考试复习资料
  9. Numpy系列(三)常用random随机函数汇总
  10. 手把手教你强化学习(十) 基于Stochastic Policy的深度强化学习方法