我正在为API编写python REST客户端。
API需要身份验证,我想在同一脚本上运行许多API客户端对象。
我当前的API代码如下:
class RestAPI:
def __init__(self, id):
self.id = id
self.fetch()
def fetch(self):
requests.get(self.url+self.id, auth=self.apikey)
class Purchase(RestAPI):
url = 'http://example.com/purchases/'
class Invoice(RestAPI):
url = 'http://example.com/invoices/'
...
我想这样使用API:
api_admin = Api('adminmytoken')
api_user = Api('usertoken')
…
amount = api_admin.Purchase(2).amount
api_user.Purchase(2).amount # raises because api_user is not authorized for this purchase
问题在于,每个对象需要根据我要使用的客户端知道它的 apikey 。
在我看来,这种模式对“类工厂”来说很像:所有RestAPI类都需要知道所提供的令牌。
如何干净地做到这一点而无需手动为每个模型提供令牌?
答案 0 :(得分:0)
我认为这里的问题是您的设计有些落后。继承可能不是这里的关键。我可能要做的是将api令牌作为User类的参数,然后传递给Rest接口上的实例级绑定:
class APIUser:
def __init__(self, id, api_key, **kwargs):
self._rest = Interface(id, api_key, **kwargs)
def purchase(self, some_arg):
# the interface itself does the actual legwork,
# and you are simply using APIUser to call functions with the interface
return self._rest.fetch('PURCHASE', some_arg)
class Interface:
methods = {
# call you want (class url)
'PURCHASE': (Purchase, 'https://myexample.com/purchases'),
'INVOICE': (Invoice, 'https://myexample.com/invoices'),
# add more methods here
}
def __init__(self, id, key):
self.id = id
self.key = key
self.session = requests.Session()
def _fetch(self, method, *args, **kwargs):
# do some methods to go get data
try:
# use the interface to look up your class objects
# which you may or may not need
_class, url = self.methods[method]
except KeyError as e:
raise ValueError(f"Got unsupported method, expected "
f"{'\n'.join(self.methods)}") from e
headers = kwargs.pop('headers', {})
# I'm not sure the actual interface here, maybe you call the
# url to get metadata to populate the class with first...
req = requests.Request(_class.http_method, url+self.id, auth=self.key, headers=headers).prepare()
resp = self.session.send(req)
# this will raise the 401 ahead of time
resp.raise_for_status()
# maybe your object uses metadata from the response
params = resp.json()
# return the business object only if the user should see it
return _class(*args, **kwargs, **params)
class Purchase:
http_method = 'GET'
def __init__(self, *args, **kwargs):
# do some setup here with your params passed by the json
# from the api
user = APIUser("token", "key") # this is my user session
some_purchase = user.purchase(2) # will raise a 401 Unauthorized error from the requests session
admin = APIUser("admintoken", "adminkey") # admin session
some_purchase = admin.purchase(2)
# returns a purchase object
some_purchase.amount
出于某些原因,您可能想采用这种方式:
通过分离出对象的实际含义,您仍然只需要将api键和令牌一次传递给User类。接口绑定在实例上,仍然可以在同一脚本中为多个用户提供灵活性。
您还将获得明确调用的模型。如果您尝试建立模型,则必须调用它,然后接口才能执行身份验证。您不再需要由业务对象强制执行身份验证