我正在使用Flask创建一个用于移动平台的API,但我也希望应用程序本身能够消化API以呈现Web内容。我想知道在Flask中访问API资源方法的最佳方法是什么?例如,如果我将以下类添加为资源:
class FooAPI(Resource):
def __init__(self):
# Do some things
super(FooAPI, self).__init__()
def post(self, id):
#return something
def get(self):
#return something
api = Api(app)
api.add_resource(FooAPI, '/api/foo', endpoint = 'foo')
然后在我想要的控制器中:
@app.route("/bar")
def bar():
#Get return value from post() in FooAPI
如何从FooAPI获取post()的返回值?我可以通过api变量以某种方式做到吗?或者我是否必须在控制器中创建FooAPI的实例?似乎必须有一个简单的方法来做到这一点,我只是不理解......
答案 0 :(得分:12)
您的应用程序使用API的显而易见的方法是像任何其他客户端一样调用它。应用程序同时充当服务器和客户端的事实无关紧要,客户端部分可以将请求放入localhost
,服务器部分将以与获取外部请求相同的方式获取它们。要生成HTTP请求,您可以使用标准库中的requests或urllib2。
但是虽然上面的方法会很好用但对我来说似乎有些过分。在我看来,更好的方法是以常规应用程序和API都可以调用的方式公开应用程序的常用功能。例如,您可以拥有一个名为FooLib
的包实现所有共享逻辑,然后FooAPI
成为FooLib
周围的瘦包装,FooAPI
和{{1}调用FooApp
来完成任务。
答案 1 :(得分:1)
我设法实现了这一点,有时API变得丑陋,在我的情况下,我需要递归调用函数,因为应用程序具有极其递归的性质(树)。递归函数本身非常昂贵,递归的HTTP请求将是一个内存和cpu浪费的世界。
所以这是片段,检查第三个for循环:
class IntentAPI(Resource):
def get(self, id):
patterns = [pattern.dict() for pattern in Pattern.query.filter(Pattern.intent_id == id)]
responses = [response.dict() for response in Response.query.filter(Response.intent_id == id)]
return jsonify ( { 'patterns' : patterns, 'responses' : responses } )
def delete(self, id):
for pattern in Pattern.query.filter(Pattern.intent_id == id):
db.session.delete(pattern)
for response in Response.query.filter(Response.intent_id == id):
db.session.delete(response)
for intent in Intent.query.filter(Intent.context == Intent.query.get(id).set_context):
self.delete(intent.id) #or IntentAPI.delete(self, intent.id)
db.session.delete(Intent.query.get(id))
db.session.commit()
return jsonify( { 'result': True } )
答案 2 :(得分:1)
另一种方法是将应用程序和API都放在同一个Flask(-RESTful)实例中。然后,您可以让应用在内部调用API方法/函数(不使用HTTP)。让我们考虑一个管理服务器上文件的简单应用程序:
# API. Returns filename/filesize-pairs of all files in 'path'
@app.route('/api/files/',methods=['GET'])
def get_files():
files=[{'name':x,'size':sys.getsizeof(os.path.join(path,x))} for x in os.listdir(path)]
return jsonify(files)
# app. Gets all files from the API, uses the API data to render a template for the user
@app.route('/app/files/',methods=['GET'])
def app_get_files():
response=get_files() # you may verify the status code here before continuing
return render_template('files.html',files=response.get_json())
由于Flask的请求对象为global,因此您可以将所有请求(从API到应用程序再向后)推送所有请求,而无需将其包括在函数调用中。例如,对于处理文件上传的应用程序资源,您可以简单地调用:
@app.route('/app/files/post',methods=['POST'])
def app_post_file():
response=post_file()
flash('Your file was uploaded succesfully') # if status_code==200
return render_template('home.html')
关联的API资源为:
@app.route('/api/files/',methods=['POST'])
def post_file():
file=request.files['file']
....
....
return jsonify({'some info about the file upload'})
但是,对于大量的应用程序数据,包装/解开JSON的开销使Miguel的第二个解决方案更可取。
在您的情况下,您想在控制器中调用此方法:
response=FooAPI().post(id)