清理URL与查询字符串 - 来自我的iOS客户端的Web服务请求

时间:2012-03-23 10:04:03

标签: ios web-services http rest

我一直在寻找一个明确的答案或解释,但找不到彻底的答案或解释。

我正在构建移动应用,这主要基于我后端的数据。 我可以使用纯粹的restful请求或带有查询字符串的请求将我的大多数请求构建到我的服务器(基于php)。

2个问题:

1。假设我有一个朋友班。我想得到或设定Dani的朋友。

休息时我会这样做:

http://www.example.com/Dani/friends - GET (to get all his friends)
http://www.example.com/Dani/friends - POST (to create a new friend for Dani)

使用查询字符串:

Http://www.example.com/ user=Dani & action=get_friends (GET method I assume?)
http://www.example.com/ user=Dani & action=add_friend (POST method I assume?)

因此,对于第一个示例,我们有缓存,加上一个常量非常易读的URL。

在第二种情况下,我们实际上没有缓存(即使有,你不知何故必须告诉你的代理刷新朋友列表的缓存,在一次调用第二个URL添加朋友之后),因为这些是2个不同的URI。休息时,它是通过定义http方法自动完成的(post / put使资源“脏”)

我在这儿吗?

我想知道什么是最好的选择: 缓存方式,安全性,单点入口(在PHP代码中),简化实现(在客户端和服务器中)等......

2。如何构建网址以仅查找某些朋友的照片?(让我们说某个位置的照片。)

我想到了:

http://www.example.com/Dani/friends?long=1&lat=2&field=photos

这是正确的,还是有更好的方法?

2 个答案:

答案 0 :(得分:12)

网址构建
关于REST URL构建要记住的最重要的事情之一是每个URL应该标识单个资源。通常,这意味着URL通常分为几部分:

  1. 顶级对象列表:/users/photos
  2. 顶级对象实例:/users/1/Dani/photos/4356
  3. 实例级列表:
    • /users/1/Dani/friends - Dani的朋友
    • /users/1/Dani/photos - Dani的照片
  4. 资源互动
    与这些资源的交互(即创建,阅读,更新,删除)通过“HTTP动词”或实际调用每个URL的“HTTP Methods”来处理。这样做的好处是每个资源(或“东西”)只需要知道如何做4种不同的事情,这意味着你有一个更简单的应用程序。

    您的应用程序也更加结构化和划分,使其更容易测试,并允许您更轻松地更新和更改它,因为事情更加松散耦合。

    如果您在单个资源和单个网址之间没有明确的1-1连接,那么您就不再拥有RESTful网址了。一旦开始将action之类的内容放入查询字符串中,您实际上就是在进行远程过程调用(RPC)而不是REST。流经中心点的所有东西都会将所有东西耦合在一起,而不是它们需要的东西,这会使你的建筑变得僵硬,难以改变,而且很难测试。

    <强>搜索
    诀窍在于,对于任何类型的“列表”类型资源,您可以将该列表作为某种查询的结果。没有什么可以说列表总是必须相同。使用http://www.example.com/Dani/friends?long=1&lat=2&field=photos是没有意义的,因为这会让你回复Dani的朋友的照片列表,这与Dani用户相距甚远。

    由于您正在寻找照片,我们已经有一个标识“照片列表”资源的网址,这是我们应该使用的网址,但只是为了获取具有特定属性的照片。

    因此,对于您找到属于某个用户的所有照片(可能是Dani的朋友之一)的示例,您可能会执行以下操作:

    GET /photos?owner=[userId]
    

    你或许可以只查找距离某个纬度/经度坐标1公里范围内拍摄的照片:

    GET /photos?owner=[userId]&radius=1&lat=[someLat]&long=[someLong]
    

    或者,如果你看得更广泛一些,也许你想要那个区域的所有 Dani的朋友的照片:

    GET /photos?ownerFriendOf=[Dani's userId]&radius=1&lat=[someLat]&long=[someLong]
    

    在所有这些情况下,您根据要发送到照片列表的查询字符串 搜索照片列表,该列表位于{{1} }。

    <强>缓存
    缓存只是一个“额外的奖励”。理论上,几乎任何请求都可以缓存,但您现在不需要担心。但是,一般情况下,如果你坚持使用REST架构,那么一旦时机成熟,你就会没事。

    安全
    HTTP有许多内置的处理安全性的方法,其中任何一种都可以工作,具体取决于您需要应用程序的安全性。基本和摘要安全性适用于应用程序到应用程序的通信,因为它们与请求一起发送其身份验证令牌(即用户名和密码)。但是,对于涉及用户的安全流,您可能希望使用会话机制并使用HTTP Cookie标头来跟踪会话。

    但是,在所有情况下,只要用户名/密码从客户端移动到服务器,就应该通过安全的SSL(https)连接来防止恶意者嗅探它。对于特别敏感的应用程序,所有交互可以通过SSL连接,而对于其他应用程序,只有登录序列可能是。但总的来说, more 安全性要好于更少。

    实施简洁性
    在安全方面,大多数Web框架都有内置方法,可以处理我刚才提到的所有安全方法。您可能想知道您是否需要使用Web框架,虽然并非严格要求,但它会大大减少您必须完成的工作量,同时会减少错误的数量,因为大多数“繁重的”由框架处理并且已经过很好的测试。

    如今,许多框架都内置了对处理RESTful请求的支持,您可以快速启动并运行。基于RPC的支持通常不太受支持,因为它没有像REST那样明确定义的应用程序体系结构,但几乎任何框架都可以使用它。

    然而,从长远来看,通过使用RESTful架构,您可能会获得更多收益。

答案 1 :(得分:4)

在我们解决您的具体问题之前,请注意,拥有“干净”,/分隔URI的想法意味着您的应用更加RESTful是一种误解。带有“查询字符串”的URI也可以。话虽如此,URI通常都有读取标志,你正在做的事情不那么RESTful。在您的情况下,这是在您的URI中提到action=method是一个标志。要采取的行动应该完全来自HTTP方法(更一般地说,你的“统一接口方法”)。

选项1最重要的参数可能是对URI发出GET请求应始终是安全的。当您开始在可通过GET请求访问的URI中使用add_friend或更差,delete_friend等方法时,您可能会冒着某人意外导航到其中一个URI并执行不需要的不安全操作的风险。存在单独的HTTP方法以防止发生这样的错误。

关于你的问题:

  1. 缓存:大多数现代客户端/浏览器和代理应该能够使用URI中的查询字符串缓存资源(假设查询字符串的顺序不会更改),但是您是正确的,以前的URL在缓存方面会更安全。

    单一条目:您仍然可以使用“更安静”的网址获得单一条目。它只需要使用像mod_rewrite这样的东西(假设您正在使用Apache)将所有URL定向到单个php控制器文件。 其他HTTP服务器(如nginx)内置了类似的支持。如果您使用的是Apache且没有mod_rewrite,您可以利用/api.php/Dani/friends的请求仍然会转到php文件api.php

    实施:客户端实施同等努力。服务器端实现实际上取决于应用程序内部结构的方式。你在使用ORM吗? “朋友”是“用户”的关系吗?如果是这样,那么能够直接将/Dani/friends之类的URI映射到(伪代码)1。初始化“Dani”用户2.获取Dani的朋友关系3.回显表示。

    如果你的底层代码已经被大量实例方法驱动(例如get_friends),那么它将需要更多的前期工作,但它可能是值得的。当您在URI中公开直接方法时,您现在已将URI与基础源代码相结合,从而使维护/更新变得棘手并且容易出错。如果你在URI中暴露类似的方法时不小心,那么这里也存在潜在的安全风险。

    值得注意的是,第一个伪代码建议也可能在较不严重的规模上遭遇类似问题。有时,最好为URI创建唯一的句柄(例如SinatraExpresssimilar PHP clones做得很好)。在灵活性,安全性,维护和“面向未来”方面,这是您最安全的选择。

  2. http://www.example.com/Dani/friends?long=1&lat=2&field=photos是一个完全可以接受的URI(参见开场白)。与带有一堆'/'的URI相比,它没有更多或更少的宁静,并且它没有任何红色标记(如URI中的动词)。