首页 文章

RESTful设计的社交网络

提问于
浏览
14

我想在社交网络方面理解它 . 我读过REST API Design Rulebook,看了一些其他书籍并阅读了很多在线资源 . 然而,当将规则应用于现实世界的问题时,我意识到我并不完全理解一切 . 我想通过指定一些问题来了解我是否对REST有正确的理解:

层次与平面设计

我们假设我用一个特定的用户来识别

/users/42

现在,该用户上传的照片最终会进入

/users/42/photos

如果他/她在照片中标记了他/她的朋友,那么这些标签就会结束

/users/42/photos/1337/tags

但这无法找到特定用户被标记的所有照片 . 我应该为此提出不同的层次结构吗?这看起来有点尴尬 . 我是否可以完全忽略层次结构并提供与此类查询相结合的照片?

/photos?owner=42
/photos?tagged=42

当内容因不同用户而异时进行缓存

Web服务应设计为提供可缓存的数据(以便客户端可以决定使用本地副本,如果它认为没有任何更改),但这会如何影响不同用户的隐私设置?登录的两个用户可能有权查看有关例如用户42.这是否意味着我需要为访问同一用户的配置文件信息的不同用户请求不同的URI,或者只要用户提供不同的凭据,缓存就不会成为问题?

从同一资源提供HTML和JSON / XML

我提到的那本书指出了一个API应该可以在 api 开头的子域中访问的规则,在它们的例子中是 http://api.soccer.restapi.org . 我曾计划使用相同的控制器进行用户访问和机器访问(例如移动应用程序) . 控制器将通过HTTP请求标头中的 Accept 字段决定提供哪个视图( text/htmlapplication/jsonapplication/xml ) . 我认为由于某些原因这是一个坏主意(因为用户希望看到子域 www ,而不是 api ),但我不明白为什么 . wwwapi 可以指向同一台服务器,还是应该尝试将HTML视图移动到其他虚拟主机?为什么?

我相信Ruby on Rails(Convention over Configuration)会从同一个控制器提供HTML和JSON,因此我认为HTML和JSON只是相同数据的不同表示形式 .

换句话说,我的书说某个资源应该只有一个URI,并且应该根据 Accept 字段提供不同的表示 . 在不同子域之间重定向用户将违反关于从同一资源提供任何表示的规则,并且复制该信息(即,将两个子域指向同一虚拟主机)违反了关于不为同一资源提供多个URI的规则 . 不提供 api 子域违反了另一个设计规则 . 如何在不违反任何规则的情况下解决这个问题?

限制发回的数据

查询组件应该用于分页,但是我是否可以拒绝遵守缺少搜索条件的列表请求并限制项目数量而不违反REST?我想减少数据库负载,避免让某人映射整个用户目录 . 我想要

/users

是非法请求,而

/users?name=leet+hacker

是有效的,但只返回,例如100项 .

我还想知道,只有在使用查询专门请求时才返回数据库列的子集以及更多/所有数据列是合法的 .

控制器提供冗余数据

我认为提供像这样的控制器是合法的

/users/me

但是它应该提供与文档URI完全相同的信息

/users/42

还是应该重定向到它?

某些用户的扩展权限

哪种RESTful方式可以提供其他功能,例如管理权限?我现在假设管理员(照片,一组用户或整个网站的管理员)将能够看到有关特定对象的更多信息,而不是其他用户 . 该信息是否应保存在完全相同的URI中并自动发送给管理员而不是其他任何人,如果它存储在不同的位置,是应该使用某个管理员查询请求还是以其他方式提供?

本地化和设置更新

尽管视图应提供用户可见的大多数字符串,但仍有一些可能涉及API的设计决策 . 最明显的是名字 . 社交网络有时允许用户输入要在不同语言环境中显示的不同名称 . 某些语言的名称(如俄语和阿拉伯语)不易自动转录 . 在其他语言中,如中文,本地和国际名称可以完全不同,根本没有相似之处或联系 . 什么是RESTful处理方式?我有一种感觉,答案将是 Accept-Language 字段,但有人认真考虑在他们所属的社交网络上切换语言吗?是应该每次都将所有这些信息都返回给呼叫者,还是可以依赖设置?这可以用缓存吗?

2 回答

  • 13

    正如@Mark Dickinson所提到的,这里有很多问题,这些问题确实应该是分开的,但我会尽我所能 .

    Hierarchy vs. flat design

    REST中没有任何内容表明您不能拥有多个并行层次结构(尽管我知道使用Rails这样做很尴尬) . 让 /users/42/photos/owner/photos/owner/42 包含相同的设置就可以了 . 类似地, /users/42/photos/tagged/photos/tagged/42 可以包含相同的集合 . 但是,您现在不应该担心URI . 有一篇很好的文章A RESTful Hypermedia API in Three Easy Steps描述了如何设计API . 在本文中,URI被确定为最后一步 .

    此外,使用HATEOAS contstraint,客户端应在运行时通过应用程序提供的链接和表单发现这些不同的URI .

    Caching when content differs for different users

    如果您要从同一个URL提供不同的内容,那么它将不会是可缓存的 . 您可以将站点划分为两种类型的内容,即公共内容和个性化内容 . 每个人的公共内容应该相同,并且可以缓存 . 每个用户的个性化内容都不同,这意味着您可以缓存的数量将大大减少(使用您在示例中使用的URL格式减少到零) .

    要在个性化内容上至少进行少量缓存,请由用户对内容进行分区,这样对于给予用户,您将获得一些缓存命中 . 例如,不要让每个人都可以访问 /users/42 ,而是使用 /<UID>/users/42 where是请求用户的用户ID . 例如用户234将使用URI /234/users/42 访问用户42的简档页面 . 对于匿名用户,您可以删除 /<UID 部分或为其使用特定的用户ID,例如 /public/users/42 .

    Providing both HTML and JSON/XML from the same resource

    使用 Accept 标头 . 这就是它的用途 .

    Limiting the data sent back

    您不需要将 /users 视为非法请求 . Treat是一个集合,返回允许请求者查看的分页用户列表 . 例如,对于匿名请求,您可能会提供一个空列表(或204 No Content)

    <users/>
    

    对于特定的登录用户,您可以提供他们的朋友 .

    <users>
        <user id="42" href="/users/42" name="John Doe"/>
        <user id="53" href="/users/53" name="Jane Doe"/>
        ...
        <next href="/users?page=2"/>
    </users>
    

    当该用户然后获取 /users?page=2 时,您将提供下一页结果

    <users>
        <user id="69" href="/users/69" name="John Smo"/>
        <user id="84" href="/users/84" name="Jane Smo"/>
        ...
        <next href="/users?page=3"/>
        <prev href="/users"/>
    </users>
    

    最后一页的结果不提供 next 链接 . 要添加搜索功能,只需添加适当的表单作为响应的一部分 .

    <users>
        <user id="69" href="/users/69" name="John Smo"/>
        <user id="84" href="/users/84" name="Jane Smo"/>
        ...
        <next href="/users?page=2"/>
        <prev href="/users"/>
        <search href="/users" method="get">
            <name cardinality="required" type="regex"/>
        </search>
    </users>
    

    搜索结果将被分页,就像 /users 列表一样 . 例如,搜索 leet hacker (假设您有权查看系统中的许多leet黑客)会产生类似的东西

    <users>
        <user id="234" href="/users/234" name="leet hacker"/>
        <user id="999" href="/users/999" name="leet hacker"/>
        ...
        <next href="/users?name=leet+hacker&page=2"/>
        <search href="/users" method="get">
            <name cardinality="required" type="regex"/>
        </search>
    </users>
    

    但是,您可能需要在用户元素中提供更多详细信息,以便可以区分leet黑客 .

    Controllers providing redundant data

    两者都可以接受 . 但是如上所述(出于缓存原因)我会使用 /<UID>/users/42 ,在这种情况下,您可能希望将 /42/users/me 重定向到 /42/users/42 .

    这也有助于从分析的角度来看,因为您可能希望单独跟踪用户访问他们自己的页面,从用户访问其他用户页面,找出哪些用户是受欢迎的以及哪些用户是自恋的 . 你甚至可能发现两者之间的相关性:)

    Extended rights for some users

    在相应的响应中提供管理链接和表单 . 例如,访问其他人的图像的详细信息可能会提供

    <image id="266" href="/photos/266" caption="leet hacker with computer"
           src="http://us.123rf.com/400wm/400/400/creatista/creatista0911/creatista091100003/5827629.jpg"/>
        <tagged-users>
            <user id="234" href="/users/234" name="leet hacker"/>
        </tagged-users>
        <owner id="234" href="/users/234" name="leet hacker"/>
    </image>
    

    但对于图像所有者,它可能会提供

    <image id="266" href="/photos/266" caption="leet hacker with computer"
           src="http://us.123rf.com/400wm/400/400/creatista/creatista0911/creatista091100003/5827629.jpg"/>
        <tagged-users>
            <tagged-user id="234" href="/users/234" name="leet hacker">
                <delete href="/photos/266/tagged/234" method="delete"/> 
            </tagged-user>
            <add href="/photos/266" method="put">
                <user cardinality="required" type="user-id"/>
            </add>
        </tagged-users>
        <owner id="234" href="/users/234" name="leet hacker"/>
        <delete href="/photos/266" method="delete"/>
        <update href="/photos/266" method="put">
            <caption cardinality="optional" type="string"/>
        </update>
    </image>
    

    Localization and settings updates

    使用 Accept-Language 作为默认语言,但允许用户更改其设置中的语言 .

  • 0

    如果您正在使用WCF Web Api(或者即使您不是),那么在那里查看OData支持可能是值得的 . 它确实解决了使事情更易于搜索的一些问题 .

相关问题