请求和响应的不同类

时间:2015-10-09 07:12:14

标签: python google-app-engine google-cloud-endpoints endpoints-proto-datastore

我刚刚开始使用Proto Datastore创建API。我想知道是否可以为我的请求和响应设置不同的类,前提是它们都是从EndpointsModel继承的。例如,

class my_request(EndpointsModel):
    #attributes

class my_response(EndpointsModel):
    #different set of attributes

@endpoints.api(name='Blah', version='v1')
    class testAPI(remote.Service):

    @my_request.method(name='myAPImethod', path='blah',
                      http_method='POST')
    def myAPImethod(self, req):
        #do something
        resp = my_response()
        return resp

这看起来不会起作用。那么有人可以告诉我如何创建这样的方法。我能想到的唯一另一种方法是恢复原始的protorpc方法,并指定请求和响应类型作为装饰器的一部分。有没有办法使用proto-datastore实现这一目标? 提前谢谢。

2 个答案:

答案 0 :(得分:0)

您可以应用一种输入/输出字段filtering using request_fields= and response_fields= in your .method()装饰器,但不能指定不同的输入和输出消息类。

如果需要使用不同的对象,则必须使用标准端点和protoRPC类和装饰器。

答案 1 :(得分:0)

在你的例子中,你有一个非常简单的布局,并没有真正满足于问题,或者恕我直言,我认为你实际上想要问的是什么。

我自己正在开发一个更复杂的端点API,但是使用两个不同的模型来为我的API调用创建请求和响应对象;但是,它们在另一个中嵌套一个(或多个)。我发现提示引导我使用应该在App Engine日志中用作警告的方法:

Method csapi.user.XXXXX specifies path parameters but you are not using a ResourceContainer. This will fail in future releases; please switch to using ResourceContainer as soon as possible.

注意; ResourceContainers非常棘手,需要您从protorpc导入的messages.Message分类。有关ResourceContainers的说明,请参阅this StackOverflow答案。

使用您的示例作为基础,我们需要构建ResourceContainers,它复制模型中我们希望返回给被调用者的内容。

import endpoints
from protorpc import messages
from protorpc import message_types
from protorpc import remote
from google.appengine.ext import ndb
from google.appengine.ext.ndb import msgprop
from endpoints_proto_datastore.ndb import EndpointsAliasProperty
from endpoints_proto_datastore.ndb import EndpointsModel

class MySubscriptionResponseMessage(messages.Message):
    id = messages.IntegerField(1)
    accountType = messages.EnumField(Account_Type, 2, required=True)
    isActive = messages.BooleanField(3, required=True, default=False)
    thisAcctEmail = messages.StringField(4)
    subscriberSince = message_types.DateTimeField(5)

class MyUserResponseMessage(messages.Message):
    id = messages.IntegerField(1)
    subscriptions = messages.MessageField('SubscriptionReadResponseMessage', 2, repeated=True)

class MySubscription(EndpointsModel):
    accountType = msgprop.EnumProperty(AccountType, choices=set([AccountType.POTENTIAL_LEAD, AccountType.BASIC, AccountType.ADVANCED, AccountType.PREMIUM]), required=True, default=AccountType.POTENTIAL_LEAD)
    isActive = ndb.BooleanProperty(required=True, indexed=True)
    thisAcctId = ndb.StringProperty(repeated=False, indexed=True, required=True)
    subscriberSince = ndb.DateTimeProperty(auto_now_add=True)

class MyUser(EndpointsModel):
    subscription_key = ndb.KeyProperty(kind="Subscription", repeated=True)
    def IdSet(self, value):
        # By default, the property "id" assumes the "id" will be an integer in a
        # simple key -- e.g. ndb.Key(GSModel, 10) -- which is the default behavior
        # if no key is set. Instead, we wish to use a string value as the "id" here,
        # so first check if the value being set is a string.
        if not isinstance(value, basestring):
            raise TypeError('ID must be a string.')
        # We call UpdateFromKey, which each of EndpointsModel.IdSet and
        # EndpointsModel.EntityKeySet use, to update the current entity using a
        # datastore key. This method sets the key on the current entity, attempts to
        # retrieve a corresponding entity from the datastore and then patch in any
        # missing values if an entity is found in the datastore.
        self.UpdateFromKey(ndb.Key(Csuser, value))

    @EndpointsAliasProperty(setter=IdSet, required=True)
    def id(self):
        # First check if the entity has a key.
        if self.key is not None:
            # If the entity has a key, return only the string_id. The method id()
            # would return any value, string, integer or otherwise, but we have a
            # specific type we wish to use for the entity "id" and that is string.
            return self.key.string_id()

    @EndpointsAliasProperty(repeated=True,property_type=Subscription.ProtoModel())
    def subscriptions(self):
    return ndb.get_multi(self.subscription_key)

@endpoints.api(name='Blah', version='v1')
    class testAPI(remote.Service):

    @my_request.method(
        response_message=MyUserResourceContainer,
        name='myAPImethod', 
        path='blah',
        http_method='POST')
    def myAPImethod(self, req):
        #do something
        this_sub = MySubscription()
        subs = []

        ... how you manipulate this object is up to you ...

        for sub in subs
            sub_msg = MySubscriptionResponseMessage(
                id=this_sub.id, 
                accountType=this_sub.accountType, 
                isActive=this_sub.isActive, 
                thisAcctEmail=this_sub.thisAcctEmail, 
                subscriberSince=this_sub.subscriberSince, 
            subs.append(sub_msg)

        return MyUserResponseMessage(
            id=user1.id, 
            subscriptions=subs)

正如您所看到的,这比您的简单示例更深入。

奖励积分:使用路径参数

如果您希望接受方法的路径参数,例如我们的假设用户的id属性(即path='users/{id}/delete'),我们将在方法本身之前添加以下块,以便使用它:

MY_REQUEST_RESOURCE_PAGE = endpoints.ResourceContainer(
    message_types.VoidMessage,
    id=messages.StringField(1, variant=messages.Variant.STRING),
    accountType=messages.EnumField(Account_Type, 2, required=True)
    isActive=messages.BooleanField(3, required=True),
    thisAcctEmail=messages.StringField(4, variant=messages.Variant.STRING),
    subscriberSince=messages.message_types.DateTimeField(5),
    cursor=messages.StringField(6, variant=messages.Variant.STRING, required=False, default="1"),
    limit=messages.IntegerField(7, variant=messages.Variant.INT32, required=False, default=10)
)

注意:请注意其他属性cursorlimit,它们允许在您拥有数百个用户的情况下对返回的结果进行分页;为了这个目的,这些在模型查询中经常使用。

现在,要完成接受路径参数的更改,请从上面的示例中替换此行:

response_message=MyUserResponseMessage,

有了这个:

MY_REQUEST_RESOURCE_PAGE , MyUserResponseMessage,

最后一点,这个设置 - 无论是否使用路径参数 - 允许您在MySubscriptionResponseMessage内嵌套一个或多个MyUserResponseMessage项以返回被调用者,就像{{1}一样} model可能包含多个MyUser模型。这不需要向api方法添加任何内容,因为它已经是MySubscription中的嵌套项。此外,如果不需要将这些项目返回给被调用者,则无需在响应消息中复制模型中的项目。