我正在努力将RESTful原则应用于我正在研究的新Web应用程序.特别是,为了成为RESTful,每个HTTP请求应该自己携带足够的信息,以便其接收者处理它以与HTTP的无状态特性完全协调.
该应用程序允许用户搜索药物.搜索接受过滤器作为输入,例如,返回停止的药物,包括免费治疗等.等等.总共有大约30个可以应用的过滤器.
此外,可以输入患者详细信息,包括患者年龄,性别,当前药物等.
要保持安静,每个请求都应包含所有这些信息吗?这似乎给网络带来了巨大的开销.此外,对于URL长度的限制,至少对GET来说,这不是不可行吗?
"作为资源过滤"是一个完美的机智.
您可以将过滤器定义PUT到过滤器资源,它可以返回过滤器ID.
PUT是幂等的,所以即使过滤器已经存在,您只需要检测到之前已经看过过滤器,这样您就可以返回过滤器的正确ID.
然后,您可以向其他请求添加过滤器参数,他们可以获取用于查询的过滤器.
GET /药物?filter = 1234&page = 4&pagesize = 20
我会通过某种规范化过程运行原始过滤器,只是为了得到一个规范化集合,这样,例如过滤器"firstname = Bob lastname = Eubanks"与"lastname = Eubanks firstname = Bob"相同.那只是我.
唯一真正令人担忧的是,随着时间的推移,您可能需要废弃一些过滤器.如果有人通过丢失或过时的过滤器发出请求,您可以简单地将错误输出.
编辑回答问题......
让我们从基本面开始吧.
简单地说,您希望指定用于查询的过滤器,但这些过滤器(可能)涉及且复杂.如果它很简单/药物/ 1234,这不会是一个问题.
实际上,您始终需要将过滤器发送到查询.问题是如何表示该过滤器.
REST系统中的会话之类的基本问题是它们通常是"带外"管理的.比如说,当你去创造一种药物时,你可以将PUT或POST给药物资源,然后你会得到一份参考药物的参考资料.
通过会话,您(通常)会返回一个cookie,或者可能是其他一些令牌来代表该会话.如果您对药物资源的PUT也创建了一个会话,那么,实际上,您的请求创建了两个资源:药物和会话.
不幸的是,当您使用像cookie一样的东西,并且您需要为您的请求提供cookie时,资源名称不再是资源的真实表示.现在它是资源名称(URL)和cookie.
所以,如果我对名为/ medications/search的资源进行GET,并且cookie代表一个会话,并且该会话碰巧有一个过滤器,你可以看到该资源名称/药物/搜索的效果如何,根本没用.由于cookie和会话及其中的过滤器的副作用,我没有获得有效使用所需的所有信息.
现在,您可以重写名称:/ medications/search?session = ABC123,有效地将cookie嵌入资源名称中.
但现在你会遇到典型的会议合同,特别是他们的生活很短暂.因此,该命名资源不太有用,长期而非无用,只是不那么有用.现在,这个查询给了我有趣的数据.明天?可能不是.关于会话的消失我会得到一些令人讨厌的错误.
另一个问题是会话通常不作为资源进行管理.例如,它们通常是副作用,而不是通过GET/PUT/DELETE明确管理.会话也是Web应用程序状态的"垃圾堆".在这种情况下,我们只是希望会话正确填充此请求所需的内容.我们实际上并不知道.再次,这是一个副作用.
现在,让我们把它转过头来.我们使用/ medications/search?filter = ABC123.
显然,随便看,这看起来很相似.我们刚刚将名称从"会话"更改为"过滤器".但是,如上所述,在这种情况下,过滤器是"一流的资源".它们需要像药物,JPEG或系统中的任何其他资源一样创建,管理等.这是关键的区别.
当然,您可以将"会话"视为一流的资源,创建它们,直接将它们放入其中等等.但是你可以看到,至少从清晰的角度来看,"第一类"会话是不是真的这个案例的一个很好的抽象.使用会话,它就像去清洁工并交出你的整个钱包或公文包."是的,门票在某处,挖出你想要的东西,给我衣服",特别是与过滤器之类的东西比较.
因此,您可以看到,在30,000英尺处,过滤器和会话之间的情况没有太大区别.但是当你放大时,它们就完全不同了.
使用过滤器资源,您可以选择永久地使它们成为永久性的东西.你可以让它们过期,你可以做任何你想做的事.会话往往具有预先设想的语义:短暂,连接的持续时间等.过滤器可以具有您想要的任何语义.它们与会话中的内容完全分开.
如果我这样做,我将如何使用过滤器?
我认为我真的不关心过滤器的内容.具体来说,我怀疑我会查询"所有按名字搜索的过滤器".在这个时刻,它似乎是无趣的信息,所以我不会围绕它设计.
接下来,我将规范化过滤器,就像我上面提到的那样.确保等效过滤器真正等效.您可以通过对表达式进行排序来确保字段名都是大写的,或者其他任何内容.
然后,我将过滤器存储为XML或JSON文档,无论哪种更适合应用程序.我会给每个过滤器一个唯一的键(自然地),但我也会使用过滤器存储实际文档的哈希值.
我这样做是为了能够快速找到过滤器是否已经存储.由于我正在规范它,我"知道"逻辑等效过滤器的XML(比如说)是相同的.因此,当有人去PUT或插入新的过滤器时,我会检查哈希值以查看它是否已经存储过.我可能会回来多个(当然,哈希可能会发生冲突),因此我需要检查实际的XML有效负载以查看它们是否匹配.
如果过滤器匹配,我将返回对现有过滤器的引用.如果没有,我会创建一个新的并返回.
我也不允许过滤UPDATE/POST.由于我正在分发对这些过滤器的引用,我会使它们不可变,因此引用可以保持有效.如果我想通过"角色"过滤,比如"获取所有过期药物过滤器",那么我将创建一个"名称过滤器"资源,将名称与过滤器实例相关联,以便实际的过滤器数据可以更改,但是名字保持不变.
还要注意,在创建过程中,您处于竞争状态(两个请求尝试制作相同的过滤器),因此您必须考虑到这一点.如果您的系统具有较高的过滤器体积,这可能是一个潜在的瓶颈.
希望这能为您澄清问题.
要保持安静,每个请求都应包含所有这些信息吗?
不会.如果您的服务器发送(或接收)过多信息,则可能存在尚未识别的一个或多个资源.
设计RESTful系统的第一步也是最重要的一步是识别和命名您的资源.你会如何为你的系统做到这一点?
根据您的描述,这里有一组可能的资源:
用户 - 系统的用户(可能是医生或患者(?) - 角色可能需要在此处作为资源公开)
药物 - 瓶子里的东西,但它也可能代表瓶子的种类(数量和含量),或者它可能代表一个特定的瓶子 - 取决于你是药店还是服务台.
疾病 - 患者可能想要服用药物的条件.
患者 - 可能服用药物的人
建议 - 根据患者所患疾病可能对患者有益的药物.
然后你可以寻找资源之间的关系;
用户拥有并属于许多角色
药物治疗已经并且属于许多疾病
疾病有很多建议.
患者拥有并属于许多药物和疾病(可怜的家伙)
患者有很多建议
建议有一名患者并且患有一种疾病
具体细节可能不适合您的特定问题,但这个想法很简单:在您的资源之间创建一个关系网络.
此时考虑URI结构可能会有所帮助,但请记住REST API必须是超文本驱动的:
# view all Recommendations for the patient GET http://server.com/patients/{patient}/recommendations # view all Recommendations for a Medication GET http://servier.com/medications/{medication}/recommendations # add a new Recommendation for a Patient PUT http://server.com/patients/{patient}/recommendations
因为这是REST,所以您将花费大部分时间来定义用于在客户端和服务器之间传输资源表示的媒体类型.
通过公开更多资源,您可以减少每个请求期间需要传输的数据量.另请注意,URI中没有查询参数.服务器可以像需要一样有状态地跟踪它,并且每个请求都可以是完全独立的.
REST适用于API,而非(典型)应用程序.不要试图将基本的有状态交互楔入无状态模型只是因为你在维基百科上阅读它.
要保持安静,每个请求都应包含所有这些信息吗?这似乎给网络带来了巨大的开销.此外,对于URL长度的限制,至少对GET来说,这不是不可行吗?
与服务器发送的资源大小相比,参数的大小通常无关紧要.如果您使用的这些大型参数是网络负担,请将它们放在服务器上一次,然后将它们用作资源.
URL长度没有明显限制 - 如果您的服务器有这样的限制,请升级它.它可能已经存在了很多年,而且充满了安全漏洞.
并非所有这些都不必在每个请求中.
每个资源(药物,患者病史等)都应该具有唯一标识它的规范URI.在某些应用程序(例如,基于Rails的应用程序)中,这将类似于"/ patients/1234"或"/ drugs/5678",但URL格式并不重要.
先前已获取资源的URI(例如来自搜索或来自嵌入在另一资源中的链接)的客户端可以使用此URI来检索它.