Python3 "Class factory" - 例如:API(token)。我的类()



我正在为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 的所有类都需要知道提供的令牌。

如何在不手动向每个模型提供令牌的情况下干净地做到这一点?

我认为这里的问题是你的设计有点倒退。继承可能不是这里的关键。我可能会做的是将 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

您可能想这样做有几个原因:

  1. 如果不允许查看对象,则无法取回对象
  2. 现在,rest接口可以控制谁可以看到什么,并且隐式地绑定到用户对象本身,而不需要其他每个类都知道发生了什么。
  3. 您可以在一个地方更改网址(如果需要(
  4. 您的业务对象只是业务对象,它们不需要执行任何其他操作

通过分离出对象的实际内容,您仍然只需要将 api 密钥和令牌传递给 User 类一次。接口绑定在实例上,仍可在同一脚本中灵活地容纳多个用户。

您还可以获得显式调用的模型。如果您尝试采用模型,则必须调用它,这时接口可以强制执行您的身份验证。您不再需要由业务对象强制执行身份验证

最新更新