Configure request method for a Siesta resource

2019-04-02 18:37发布

问题:

I have this api where the login works via post while most other requests work with get. Now I am using siesta to define the login url as a resource.

func login(username: String, password: String) -> Resource {
    return self.resource("login").withParam("username", username).withParam("password", password);
}

The problem is that when I use .loadIfNeeded() on this resource it will do a get request, but that won't work because it needs to be a post request.

Now I know of the existence of .decorateRequests, but I'm unsure how to use that to make my login resource use post as request method.

Thanks in advance,

Peter

回答1:

Siesta’s load() and loadIfNeeded() are only for GET requests.

Why? Those Siesta methods are build on the assumption that they don’t have side effects, that their results can be cached, and that they are safe to call zero, one, or many times. In HTTP, this is the contract of GET. However, POST, PUT, etc do no make any such promise; each request can have a separate effect, and they are thus dangerous to call either repeatedly or optionally.

To make requests using POST, PUT, and DELETE, use Resource.request(…):

loginResource.request(.post)

(See the section on requests in the Siesta user guide for more info on how request(…) differs from load(…).)


Why isn’t there a separate setting to, for example, make a resource be a “POST resource?” Because the REST approach is that /foo is the name of a logical thing — a resource — and GET /foo and PUT /foo are different actions on it, one that retrieves its state and one that changes it. This is not merely a matter of aesthetic purity; the strong promises of GET are closely tied to it.

If your API is completely non-REST-shaped, Siesta may not be a good fit for it. However, you may also be able to write a NetworkProvider that translates REST-shaped requests into your API’s own structure, letting Siesta essentially treat it as a REST API.


APIs usually don’t take passwords in a query string (which is what withParam(…) does), but rather in a post body.

(Aside: if your API does take a password in a query string, you probably don’t want it to. Passwords in query strings are easily leak into insecure places — log files, for example. But you know your API, and I realize you often have to work with what you’ve got!)

In the likely event that your API does take a password in a POST body instead of a query string, you can do this:

// If it’s a JSON request
loginResource.request(.post, json: ["user": user, "password": pass])

// If it an HTML form encoded request
loginResource.request(.post, urlEncoded: ["user": user, "password": pass])

If you really want Siesta to cache the result of your auth call as if it had been a GET request, you can use Resource.load(using:):

authResource.load(using:
  authResource.request(
      .post, json: ["user": user, "password": pass]))

This is useful if you want to publish your auth credentials within the app using ResourceObservers, for example. The more common approach is to use an onSuccess hook to grab the credentials once and update the service configuration, but load(using:) can be a helpful alternative in some situations.