首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Google Cloud Endpoints API的DRY代码

Google Cloud Endpoints API的DRY代码
EN

Stack Overflow用户
提问于 2013-12-12 17:52:11
回答 2查看 1.5K关注 0票数 2

我希望避免使用样板代码为我的Google App Engine应用程序的不同模型创建Google Cloud Endpoints App。假设我有一个PostUserCategory模型。数据存储在数据存储区中。我想使用资源postsuserscategories创建REST API。我为posts资源编写了以下代码:

代码语言:javascript
复制
import endpoints
from protorpc import messages
from protorpc import message_types
from protorpc import remote
from blog.models import Post
from cloud_endpoints import WEB_CLIENT_ID, ANDROID_CLIENT_ID, IOS_CLIENT_ID, ANDROID_AUDIENCE


class PostMessage(messages.Message):
    id = messages.StringField(1)
    title = messages.StringField(2)
    body = messages.StringField(3)


class PostMessageCollection(messages.Message):
    post_messages = messages.MessageField(PostMessage, 1, repeated=True)


def post_to_message(post):
    return PostMessage(
        id=str(post.key()),
        title=post.title,
        body=post.body)


ID_RESOURCE = endpoints.ResourceContainer(
    message_types.VoidMessage,
    id=messages.StringField(1, variant=messages.Variant.STRING))

PUT_RESOURCE = endpoints.ResourceContainer(
    PostMessage,
    id=messages.StringField(1, variant=messages.Variant.STRING))

POST_RESOURCE = endpoints.ResourceContainer(Post)


@endpoints.api(name='posts',
               version='v1',
               allowed_client_ids=[WEB_CLIENT_ID, ANDROID_CLIENT_ID, IOS_CLIENT_ID],
               audiences=[ANDROID_AUDIENCE])
class PostsApi(remote.Service):
    """List"""
    @endpoints.method(message_types.VoidMessage,
                      PostMessageCollection,
                      path='/posts',
                      http_method='GET',
                      name='posts.listPosts')
    def list(self, unused_request):
        post_messages = []
        for post in Post.all():
            post_messages.append(post_to_message(post))

        return PostCollection(post_messages=post_messages)

    """Get"""
    @endpoints.method(ID_RESOURCE,
                      PostMessage,
                      path='/posts/{id}',
                      http_method='GET',
                      name='posts.getPost')
    def get(self, request):
        try:
            return post_to_message(Post.get(request.id))

        except (IndexError, TypeError):
            raise endpoints.NotFoundException('Post %s not found.' % (request.id,))

    """Create"""
    @endpoints.method(POST_RESOURCE,
                      message_types.VoidMessage,
                      path='/posts',
                      http_method='POST',
                      name='posts.createPost')
    def create(self, request):
        post = Post(title=request.title, body=request.body)\
        post.put()
        return message_types.VoidMessage()

    """Update"""
    @endpoints.method(PUT_RESOURCE,
                      message_types.VoidMessage,
                      path='/posts/{id}',
                      http_method='POST',
                      name='posts.updatePost')
    def update(self, request):
        try:
            post = Post.get(request.id)
            post.title = request.title
            post.body = request.body
            return message_types.VoidMessage()
        except (IndexError, TypeError):
            raise endpoints.NotFoundException('Post %s not found.' % (request.id,))

    """Delete"""
    @endpoints.method(ID_RESOURCE,
                      message_types.VoidMessage,
                      path='/posts/{id}',
                      http_method='DELETE',
                      name='posts.deletePost')
    def delete(self, request):
        try:
            post = Post.get(request.id)
            post.delete()
            return message_types.VoidMessage()

        except (IndexError, TypeError):
            raise endpoints.NotFoundException('Post %s not found.' % (request.id,))

我可以复制/粘贴这段代码,将“发布”更改为“分类”,还可以编辑PostMessagePostMessageCollectionpost_to_message,但这似乎不是什么好做法。我不想重复我的话。可以创建一个抽象API类并为PostAPICategoryAPIUserAPI创建子类吗?或者,有没有更好的方法来参数化PostPostMessagePostMessageCollectionpost_to_message和资源的路径("/posts“、"/categories”和"/users"),这样我就不必复制/粘贴每个资源的类了?这些类将具有相同的方法和相同的装饰器,我不想为每个资源重复这一点。我使用的是Python 2.7。

EN

回答 2

Stack Overflow用户

发布于 2014-02-05 03:11:37

我也偶然发现了同样的问题,不幸的是,这在google cloud endpoints上是不可能的。方法装饰器需要请求描述(这里是PostMessageCollection)。对message.Message进行子类化的请求描述不允许通过继承进行重用,因此所有消息类都必须在没有任何继承的情况下完全定义。

但是,您可以通过以下方式在某种程度上实现这一点(尽管我还没有测试过它,现在就想到了:):

代码语言:javascript
复制
# All the message and response definitions have to be here, complete.

class PostMessage(messages.Message):
    id = messages.StringField(1)
    title = messages.StringField(2)
    body = messages.StringField(3)


class PostMessageCollection(messages.Message):
    post_messages = messages.MessageField(PostMessage, 1, repeated=True)


def post_to_message(post):
    return PostMessage(
        id=str(post.key()),
        title=post.title,
        body=post.body)


ID_RESOURCE = endpoints.ResourceContainer(
    message_types.VoidMessage,
    id=messages.StringField(1, variant=messages.Variant.STRING))

PUT_RESOURCE = endpoints.ResourceContainer(
    PostMessage,
    id=messages.StringField(1, variant=messages.Variant.STRING))

POST_RESOURCE = endpoints.ResourceContainer(Post)

# Now define all the 'Category' related messages here.


@endpoints.api(name='posts_n_categories',  # The name can be a common one.
               version='v1',
               allowed_client_ids=[WEB_CLIENT_ID, ANDROID_CLIENT_ID, IOS_CLIENT_ID],
               audiences=[ANDROID_AUDIENCE])
class BaseAPI(remote.Service):
    """List"""
    # Common defs go here.
    MessageCollection = messages.Message
    PATH = '/'
    NAME = ''

    @staticmethod
    def converter(x):
        raise NotImplemented

    iterator = []
    collection = messages.Message
    @endpoints.method(message_types.VoidMessage,
                      MessageCollection,
                      path=PATH,
                      http_method='GET',
                      name=NAME)
    def list(self, unused_request):
        # Do the common work here. You can 
        _messages = []
        for post in self.__class__.iterator.all():
            _messages.append(self.__class__.converter(post))

        return self.__class__.collection(post_messages=_messages)




@endpoints.api(name='posts',  # The name can be different.
               version='v1',
               allowed_client_ids=[WEB_CLIENT_ID, ANDROID_CLIENT_ID, IOS_CLIENT_ID],
               audiences=[ANDROID_AUDIENCE])
class PostAPI(Base):
    # Post specific defs go here.
    MessageCollection = PostMessageCollection
    PATH = '/posts'
    NAME = 'posts.listPosts'
    converter = post_to_message
    iterator = Post
    collection = PostCollection


# Define the category class here.

显然,这并不能节省太多时间。

票数 1
EN

Stack Overflow用户

发布于 2016-08-03 06:33:33

这个帖子已经有两年的历史了,但无论如何我都会提出一个想法。它有一个明显的缺陷;下面有更多关于这方面的内容。

我将假设您正在通过ndb API使用数据存储。

其想法是引入通用消息类(EntityContainerEntityContainerList),这些类可以包含任何特定于应用程序的消息类的实例,并包括一个将路由映射到protorpcndb类的字典:

model.py

代码语言:javascript
复制
class Book(ndb.Model):
    title = ndb.StringProperty()
    author = ndb.StringProperty()

class Movie(ndb.Model):
    title = ndb.StringProperty()
    director = ndb.StringProperty()

main.py

代码语言:javascript
复制
class Book(messages.Message):
    title = messages.StringField(1)
    author = messages.StringField(2)

class Movie(messages.Message):
    title = messages.StringField(1)
    director = messages.StringField(2)

class EntityContainer(messages.Message):
    book = messages.MessageField(Book, 1)
    movie = messages.MessageField(Movie, 2)
    id_ = messages.StringField(3)

class EntityContainerList(messages.Message):
    entities = messages.MessageField(EntityContainer, 1, repeated=True)

Map = {
    'books': {
        'message_class': Book,
        'ndb_model_class': model.Book,
        'entity_container_key': 'book'
    },
    'movies': {
        'message_class': Movie,
        'ndb_model_class': model.Movie,
        'entity_container_key': 'movie'
    }
}

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

    GET_RESOURCE = endpoints.ResourceContainer(
        id_=messages.StringField(1),
        entities=messages.StringField(2)
    )

    LIST_RESOURCE = endpoints.ResourceContainer(
        entities=messages.StringField(2)
    )   

    POST_RESOURCE = endpoints.ResourceContainer(
        EntityContainer,
        entities=messages.StringField(1),
    )

    @endpoints.method(
        GET_RESOURCE,
        EntityContainer,
        path='{entities}/{id_}',
        http_method="GET")
    def get_entity(self, request):
        # The path tells us what kind of entity we're fetching.
        Entity = Map[request.entities]['message_class']
        key = Map[request.entities]['entity_container_key']

        # Pull from database.
        ndb_entity = ndb.Key(urlsafe=request.id_).get()

        # Formulate response.
        entity_container = EntityContainer(**{key: Entity(**ndb_entity.to_dict())})
        entity_container.id_ = request.id_
        logging.info("\n\nResponse: %s\n" % str(entity_container))
        return entity_container

    @endpoints.method(
        LIST_RESOURCE,
        EntityContainerList,
        path='{entities}',
        http_method="GET")
    def list_entities(self, request):
        # The path tells us what kinds of entities we're fetching.
        Entity = Map[request.entities]['message_class']
        NdbModel = Map[request.entities]['ndb_model_class']
        key = Map[request.entities]['entity_container_key']

        # Pull from database
        query = NdbModel.query()

        # Formulate response.
        entities = [
            EntityContainer(**{'id_': q.key.urlsafe(), key: Entity(**q.to_dict())})
                for q in query
        ]
        entity_container_list = EntityContainerList(entities=entities)
        logging.info("\n\nEntity list: %s\n" % str(entity_container_list))
        return entity_container_list

    @endpoints.method(
        POST_RESOURCE,
        EntityContainer,
        path='{entities}',
        http_method='POST'
        )
    def post_entity(self, request):
        # The path tells us what kind of entity we're' creating.
        Entity = Map[request.entities]['message_class']
        NdbModel = Map[request.entities]['ndb_model_class']
        key = Map[request.entities]['entity_container_key']

        # Extract the body of the request
        body_message = getattr(request, key)
        body_dict = {f.name: getattr(body_message, f.name)
            for f in body_message.all_fields()
                if getattr(body_message, f.name)}

        # Write to database
        ndb_entity = NdbModel(**body_dict)
        ndb_key = ndb_entity.put()
        id_ = ndb_key.urlsafe()

        # Reload entity. Maybe some model hooks treated the data.
        ndb_entity = ndb_key.get()

        entity_container = EntityContainer(**{key: Entity(**ndb_entity.to_dict())})
        entity_container.id_ = id_
        logging.info("\n\nResponse: %s\n" % str(entity_container))
        return entity_container

当然,您也可以使用DELETEPUTPATCH方法。

您拥有的消息类越多,这种方法可以节省的代码就越多。(我刚刚包含了两个-- BookMovie --用于演示。)

上面提到的缺陷是EntityContainer类膨胀了消息字段,任何类实例都只使用其中的两个字段。我不知道事情是如何在引擎盖下工作的,所以我不能评估这件事的严重性。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20540090

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档