REST - 复杂的应用程序 [英] REST - complex applications

查看:48
本文介绍了REST - 复杂的应用程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在努力将 RESTful 原则应用于我正在开发的新 Web 应用程序.特别是,要成为 RESTful,每个 HTTP 请求都应该自己携带足够的信息供其接收者对其进行处理,以使其与 HTTP 的无状态性质完全一致.

I'm struggling to apply RESTful principles to a new web application I'm working on. In particular, it's the idea that to be RESTful, each HTTP request should carry enough information by itself for its recipient to process it to be in complete harmony with the stateless nature of HTTP.

该应用程序允许用户搜索药物.搜索接受过滤器作为输入,例如,返回已停产的药物,包括免费治疗等.总共有大约 30 个过滤器可以应用.

The application allows users to search for medications. The search accepts filters as input, for example, return discontinued medicines, include complimentary therapy etc..etc. In total there are around 30 filters that can be applied.

此外,还可以输入患者的详细信息,包括患者的年龄、性别、当前用药等.

Additionally, patient details can be entered including the patients age, gender, current medications etc.

为了 Restful,所有这些信息都应该包含在每个请求中吗?这似乎给网络带来了巨大的开销.另外,至少对于 GET 而言,对 URL 长度的限制不会使这变得不可行吗?

To be Restful, should all this information be included with every request? This seems to place a huge overhead on the network. Also, wouldn't the restrictions on URL length, at least for GET, make this unfeasible?

推荐答案

过滤为资源"是一个完美的策略.

The "Filter As Resource" is a perfect tact for this.

您可以将过滤器定义放入过滤器资源中,它可以返回过滤器 ID.

You can PUT the filter definition to the filter resource, and it can return the filter ID.

PUT 是幂等的,所以即使过滤器已经存在,您只需要检测您之前见过过滤器,这样您就可以返回过滤器的正确 ID.

PUT is idempotent, so even if the filter is already there, you just need to detect that you've seen the filter before, so you can return the proper ID for the filter.

然后,您可以向其他请求添加过滤器参数,他们可以获取过滤器以用于查询.

Then, you can add a filter parameter to your other requests, and they can grab the filter to use for the queries.

GET/medications?filter=1234&page=4&pagesize=20

GET /medications?filter=1234&page=4&pagesize=20

我会通过某种规范化过程来运行原始过滤器,只是为了有一个规范化的集合,例如过滤器firstname=Bob lastname=Eubanks"与lastname=Eubanks firstname=Bob"相同.不过那只是我.

I would run the raw filters through some sort of canonicalization process, just to have a normalized set, so that, e.g. filter "firstname=Bob lastname=Eubanks" is identical to "lastname=Eubanks firstname=Bob". That's just me though.

唯一真正担心的是,随着时间的推移,您可能需要淘汰一些过滤器.如果有人使用缺少或过时的过滤器提出请求,您可以简单地错误地提出请求.

The only real concern is that, as time goes on, you may need to obsolete some filters. You can simply error out the request should someone make a request with a missing or obsolete filter.

编辑回答问题...

让我们从基础开始.

简单地说,您想指定一个用于查询的过滤器,但这些过滤器(可能)涉及且复杂.如果它是简单的/medications/1234,这将不是问题.

Simply, you want to specify a filter for use in queries, but these filters are (potentially) involved and complicated. If it was simple /medications/1234, this wouldn't be a problem.

实际上,您始终需要将过滤器发送到查询.问题是如何表示该过滤器.

Effectively, you always need to send the filter to the query. The question is how to represent that filter.

REST 系统中的会话之类的基本问题是它们通常是带外"管理的.比如说,当您创建药物时,您可以向药物资源发送或发布信息,然后您将获得对该药物的引用.

The fundamental issue with things like sessions in REST systems is that they're typically managed "out of band". When you, say, go and create a medication, you PUT or POST to the medications resource, and you get a reference back to that medication.

对于会话,您(通常)会取回一个 cookie,或者可能是其他一些代表该会话的令牌.如果您对药物资源的 PUT 也创建了一个会话,那么实际上,您的请求创建了两个资源:一个药物和一个会话.

With a session, you would (typically) get back a cookie, or perhaps some other token to represent that session. If your PUT to the medications resource created a session also, then, in truth, your request created two resources: a medication, and a session.

不幸的是,当您使用 cookie 之类的东西,并且您的请求需要该 cookie 时,资源名称不再是资源的真实表示.现在是资源名称(URL)和 cookie.

Unfortunately, when you use something like a cookie, and you require that cookie for your request, the resource name is no longer the true representation of the resource. Now it's the resource name (the URL), and the cookie.

因此,如果我对名为/medications/search 的资源执行 GET,并且 cookie 代表一个会话,并且该会话恰好在其中包含一个过滤器,您可以看到效果如何,该资源名称/medications/search,根本没有用.由于 cookie 和会话以及其中的过滤器的副作用,我没有有效使用所需的所有信息.

So, if I do a GET on the resource named /medications/search, and the cookie represents a session, and that session happens to have a filter in it, you can see how in effect, that resource name, /medications/search, isn't really useful at all. I don't have all of the information I need to make effective use, because of the side effect of the cookie and the session and the filter therein.

现在,您或许可以重写名称:/medications/search?session=ABC123,有效地将 cookie 嵌入到资源名称中.

Now, you could perhaps rewrite the name: /medications/search?session=ABC123, effectively embedding the cookie in the resource name.

但是现在您遇到了典型的会话合同,特别是它们是短暂的.因此,该命名资源的用处不大,从长远来看,不是没用,只是没用.现在,这个查询给了我有趣的数据.明天?可能不会.我会收到一些关于会话消失的严重错误.

But now you run in to the typical contract of sessions, notably that they're short lived. So, that named resource is less useful, long term, not useless, just less useful. Right now, this query gives me interesting data. Tomorrow? Probably not. I'll get some nasty error about the session being gone.

另一个问题是会话通常不作为资源进行管理.例如,它们通常是副作用,而不是通过 GET/PUT/DELETE 显式管理.会话也是 Web 应用程序状态的垃圾堆".在这种情况下,我们只是希望会话能够正确填充此请求所需的内容.我们实际上并不真正知道.同样,这是一个副作用.

The other problem is that sessions typically are not managed as a resource. For example, they're usually a side effect, vs explicitly managed via GET/PUT/DELETE. Sessions are also the "garbage heap" of web app state. In this case, we're just kind of hoping that the session is properly populated with what is needed for this request. We actually don't really know. Again, it's a side effect.

现在,让我们稍微改变一下.让我们使用/medications/search?filter=ABC123.

Now, let's turn it on its head a little bit. Let's use /medications/search?filter=ABC123.

显然,随便,这看起来是一样的.我们只是将名称从会话"更改为过滤器".但是,正如所讨论的,在这种情况下,过滤器是一流的资源".它们需要像药物、JPEG 或系统中的任何其他资源一样被创建、管理等.这是关键的区别.

Obviously, casually, this looks identical. We just changed the name from 'session' to 'filter'. But, as discussed, Filters, in this case, ARE a "first class resource". They need to be created, managed, etc. the same as a medication, a JPEG, or any other resource in your system. This is the key distinction.

当然,您可以将会话"视为一流资源,创建它们,直接将内容放入其中等等.但是您可以看到,至少从清晰的角度来看,一流"会话是如何对于这种情况,这不是一个很好的抽象.使用会话,就像去清洁工那里交出你的整个钱包或公文包.是的,票就在某个地方,挖出你想要的东西,给我我的衣服",尤其是与过滤器等明确的东西相比.

Certainly, you could treat "sessions" as a first class resource, creating them, putting stuff in them directly, etc. But you can see how, at least from a clarity point of view, a "first class" session isn't really a good abstraction for this case. Using a session, its like going to the cleaners and handing over your entire purse or briefcase. "Yea, the ticket is in there somewhere, dig out what you want, give me my clothes", especially compared to something explicit like a filter.

因此,您可以看到,在 30,000 英尺处,过滤器和会话之间的情况并没有太大区别.但是当你放大时,它们就完全不同了.

So, you can see how, at 30,000 feet, there's not a lot of difference in the case between a filter and a session. But when you zoom in, they're quite different.

使用过滤器资源,您可以选择让它们成为永久的东西.你可以让它们过期,你可以为所欲为.会话往往有先入为主的语义:短暂的、连接的持续时间等.过滤器可以有任何你想要的语义.它们与会话附带的内容完全分开.

With the filter resource, you can choose to make them a persistent thing forever and ever. You can expire them, you can do whatever you want. Sessions tend to have pre-conceived semantics: short live, duration of the connection, etc. Filters can have any semantics you want. They're completely separate from what comes with a session.

如果我这样做,我将如何使用过滤器?

If I were doing this, how would I work with filters?

我认为我真的不关心过滤器的内容.具体来说,我怀疑我是否会查询按名字搜索的所有过滤器".在这个时候,它似乎是无趣的信息,所以我不会围绕它进行设计.

I would assume that I really don't care about the content of a filter. Specifically, I doubt I would ever query for "all filters that search by first name". At this juncture, it seems like uninteresting information, so I won't design around it.

接下来,我将对过滤器进行标准化,就像我上面提到的那样.确保等效过滤器真正等效.您可以通过对表达式进行排序、确保字段名称全部为大写或其他方式来实现这一点.

Next, I would normalize the filters, like I mentioned above. Make sure that equivalent filters truly are equivalent. You can do this by sorting the expressions, ensuring fieldnames are all uppercase, or whatever.

然后,我会将过滤器存储为 XML 或 JSON 文档,以更适合/更适合应用程序为准.我会给每个过滤器一个唯一的键(自然),但我也会用过滤器存储实际文档的哈希值.

Then, I would store the filter as an XML or JSON document, whichever is more comfortable/appropriate for the application. I would give each filter a unique key (naturally), but I would also store a hash for the actual document with the filter.

我会这样做是为了能够快速找到过滤器是否已经存储.由于我正在对其进行规范化,因此我知道"逻辑上等效的过滤器的 XML(比如说)是相同的.所以,当有人去 PUT 或插入一个新的过滤器时,我会检查散列以查看它之前是否已存储.我很可能会返回多个(当然,散列可能会发生冲突),因此我需要检查实际的 XML 负载以查看它们是否匹配.

I would do this to be able to quickly find if the filter is already stored. Since I'm normalizing it, I "know" that the XML (say) for logically equivalent filters would be identical. So, when someone goes to PUT, or insert a new filter, I would do a check on the hash to see if it has been stored before. I may well get back more than one (hashes can collide, of course), so I'll need to check the actual XML payloads to see whether they match.

如果过滤器匹配,我返回对现有过滤器的引用.如果没有,我会创建一个新的并返回.

If the filters match, I return a reference to the existing filter. If not, I'd create a new one and return that.

我也不允许过滤器更新/发布.由于我要分发对这些过滤器的引用,我会将它们设置为不可变的,以便引用可以保持有效.如果我想要按角色"进行过滤,例如获取所有过期药物过滤器",那么我将创建一个命名过滤器"资源,将名称与过滤器实例相关联,以便实际过滤器数据可以更改,但名称保持不变.

I also would not allow a filter UPDATE/POST. Since I'm handing out references to these filters, I would make them immutable so the references can remain valid. If I wanted a filter by "role", say, the "get all expire medications filter", then I would create a "named filter" resource that associates a name with a filter instance, so that the actual filter data can change but the name remain the same.

还要注意,在创建过程中,您处于竞争状态(两个请求试图创建相同的过滤器),因此您必须考虑到这一点.如果您的系统具有高过滤量,这可能是一个潜在的瓶颈.

Mind, also, that during creation, you're in a race condition (two requests trying to make the same filter), so you would have to account for that. If your system has a high filter volume, this could be a potential bottleneck.

希望这可以为您澄清问题.

Hope this clarifies the issue for you.

这篇关于REST - 复杂的应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆