RESTful actions/services that don't correspond

2020-07-03 07:25发布

问题:

I like RESTful for its simplicity and how it avoids the cruft of normal 'enterprise' systems like SOAP, or the binary-hardness of DCOM and RPC.

But REST seems more suited to database entities than more abstract services. I was wondering if you could advise me on how you'd do these situations:

For example, suppose I have a RESTful web-service for a normal database system (say, a dairy shopping site), so I'd have /products/eggs/battery and /products/milk/skimmed

Doing an INSERT would be achieved by making a POST to /products/eggs.

But how would you do a "clear all" command? The DELETE verb is only suitable for single entities. And a "DELETE /products/milk" implies deletion of the "milk" product category itself rather than just all products within the milk category. And what if you want to accomplish both?

Another question I have relates to web-service actions that are not related to an entity. For example, if I'm designing a web-service for a password database, I'd have operations like "GET /passwords/stackoverflow.com" which is fine, but I would also have operations to disable the website in case of intrusion detection. Under the 'old school' web-service model I'd have a method simply named "disableWebsite", however I can't create a HTTP verb called "DISABLE" and a resource called "/website" (so the request would be "DISABLE /website"). What's the solution here?

Finally, how do you reconcile HTML Forms with RESTful? Web forms can only make GET requests using querystrings or POST. If I have a search form I want it to request "/products/search/{query}" but right now the request would look like "/products/search?query={query}".

回答1:

I think you should stop thinking about resources as synonyms for database entities. Yes, they're often related, but a resource is really just an addressable concept in your domain. I think it's much more useful to think about resources as the things you see on the web when you use your browser (lists, items, posts, comments, images, whatever).

But how would you do a "clear all" command?

I'm not sure why DELETE /products/milk implies the deletion of the milk category itself, but if you'd rather:

DELETE /products?category=milk

DELETE doesn't imply the deletion of a single database entity. It implies the deletion of a single resource. And "/products?category=milk" (or "/products/milk", for that matter) identifies a single resource. If you can GET it, you can DELETE it.

And what if you want to accomplish both?

How about this?

DELETE /product-categories/milk

One trick that became popular with Ruby on Rails is to provide a form (using GET) for any PUT/POST/DELETE operation. So, for these deletes, you may want to provide a form like this:

GET /product-categories/milk/delete

In that form (think HTML), you could ask your user if it's really OK to delete the whole category.(Please don't pay attention to the notion that HTML forms aren't really compatible with RESTful web services. HTML is a pretty successful format for interacting with resources on the web, and a well-designed AJAX application probably first works as a well-designed HTML application. There are some details that need to be worked out to support both browsers and your other clients, but they're all legitimate REST clients.)

How to disable a website?

There are lots of ways to do this. Just a PUT to /sites/stackoverflow.com with a flag to disable might work.

Finally, how do you reconcile HTML Forms with RESTful?

You can't really do an HTTP PUT or DELETE from the browser, but you can provide a hidden field in your form to fake it:

<input type="hidden" name="_method" value="PUT" />

As long as your routing engine supports it, this is a good way to route a browser post to the appropriate handler (I've also seen people use an X-HTTP-Method-Override header for non-HTML clients without full support for HTTP verbs).

If you're interested in digging in, I recommend the Web Services Cookbook as a starter. Also, take a look at the Richardson Maturity Model. And remember that REST is just like the rest of the web. It wouldn't be very useful without the links. Provide your clients with a way to get around.

<a href="/products/milk/delete" rel="delete" />

<atom:link href="/products/milk/delete" rel="delete" />


回答2:

RESTful systems interact with "resources" which do not correlate with the standard notion of an entity.
A "resource" is a very fuzzy concept, with a few strict rules. I like to think of a resource as any useful concept that can help in allowing the client to do what they need to do. So, if you need to delete a bunch of entities, then create a resource that represents that "bunch of entities" and then use the DELETE method on it.

The real trick to designing restful systems is to stop trying to assign significance to particular resources, but to assign significance to the relationship between two resources. Think about how HTML works. You download a page, and there is a stylesheet link. The rel="stylesheet" defines the meaning of what you find at the end of the href URL.
If you want to disable a web site, the access the website resource and use the URL that is in the link with rel="disabler". RESTful design is about defining resources that are interrelated via links. Some links are used just for retrieval of information, others are actually used to make changes to resource state.

The strict rules that a resource must follow are that a resource must have an identifier (e.g. an URL) and you can only interact with that resource via "representations" of that resource. Those representations could be in a wide range of different formats.

As for HTML forms, forget about what the URI looks like, using query string parameters is just as RESTful as path parameters. HTML Forms let you do GET and POST, i.e. safe operations and unsafe operations. That's is enough for anyone to do REST. Sure DELETE and PUT can be useful, but in reality the benefits those extra methods bring to a RESTful system is very small, in comparison to something like hypermedia.



回答3:

But how would you do a "clear all" command? The DELETE verb is only suitable for single entities.

False.

And a "DELETE /products/milk" implies deletion of the "milk" product category itself rather than just all products within the milk category.

Correct.

And what if you want to accomplish both?

What's wrong with DELETE on /cart/milk/?

Under the 'old school' web-service model I'd have a method simply named "disableWebsite",

What?

however I can't create a HTTP verb called "DISABLE" and a resource called "/website" (so the request would be "DISABLE /website"). What's the solution here?

POST. POST to /passwords/stackoverflow.com/disable/ The entity (passwords/stackoverflow.com) contains an "disable" and "enable" sub-entity within it. You can post to either one to change the state of the parent entity.

If I have a search form I want it to request "/products/search/{query}"

That's why most RESTful web services are written in Javascript or Flex or something like that.

RESTful web services and "native" HTML forms aren't really compatible. They're not meant to be.



标签: rest