如何在进行API调用之前使用ReactiveCocoa进行透明身份验证? [英] How to using ReactiveCocoa to transparently authenticate before making API calls?

查看:163
本文介绍了如何在进行API调用之前使用ReactiveCocoa进行透明身份验证?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在一个应用程序中使用ReactiveCocoa调用远程Web API。但在从给定API主机检索任何内容之前,应用程序必须提供用户的凭据并检索API令牌,然后用于对后续请求进行签名。

I am using ReactiveCocoa in an app which makes calls to remote web APIs. But before any thing can be retrieved from a given API host, the app must provide the user's credentials and retrieve an API token, which is then used to sign subsequent requests.

我想抽象掉这个身份验证过程,以便每当我进行API调用时自动发生。假设我有一个包含用户凭据的API客户端类。

I want to abstract away this authentication process so that it happens automatically whenever I make an API call. Assume I have an API client class that contains the user's credentials.

// getThing returns RACSignal yielding the data returned by GET /thing.
// if the apiClient instance doesn't already have a token, it must
// retrieve one before calling GET /thing 
RAC(self.thing) = [apiClient getThing]; 

如何使用ReactiveCocoa透明地导致对API的第一个(而且只有第一个)请求检索和作为副作用,在任何后续请求之前安全地存储API令牌?

How can I use ReactiveCocoa to transparently cause the first (and only the first) request to an API to retrieve and, as a side effect, safely store an API token before any subsequent requests are made?

这也是一个要求,我可以使用 combineLatest:(或类似)启动多个并发请求,

It is also a requirement that I can use combineLatest: (or similar) to kick off multiple simultaneous requests and that they will all implicitly wait for the token to be retrieved.

RAC(self.tupleOfThisAndThat) = [RACSignal combineLatest:@[ [apiClient getThis], [apiClient getThat]]];

此外,如果检索令牌请求已经在调用API调用时,

Further, if the retrieve-token request is already in flight when an API call is made, that API call must wait until the retrieve-token request has completed.

我的部分解决方案如下:

My partial solution follows:

将使用 flattenMap:将产生令牌的信号映射到给定令牌,执行所需请求并产生API调用的结果的信号。

The basic pattern is going to be to use flattenMap: to map a signal which yields the token to a signal that, given the token, performs the desired request and yields the result of the API call.

假设对 NSURLRequest 有一些方便的扩展:

Assuming some convenient extensions to NSURLRequest:

- (RACSignal *)requestSignalWithURLRequest:(NSURLRequest *)urlRequest {
    if ([urlRequest isSignedWithAToken])
        return [self performURLRequest:urlRequest];

    return [[self getToken] flattenMap:^ RACSignal * (id token) {
        NSURLRequest *signedRequest = [urlRequest signedRequestWithToken:token];
        assert([urlRequest isSignedWithAToken]);
        return [self requestSignalWithURLRequest:signedRequest];
    }
}

现在考虑 -getToken


  • 在琐碎的情况下,当已经检索到令牌时,

  • 如果没有检索到令牌,则订阅会延迟到返回令牌的身份验证API调用。

  • 如果身份验证API调用正在进行中,应该可以安全地添加另一个观察者,而不会通过线路重复身份验证API调用。

我不知道如何做到这一点。此外,如何和在哪里安全地存储令牌?某些类型的持续/可重复信号?

However I'm not sure how to do this. Also, how and where to safely store the token? Some kind of persistent/repeatable signal?

推荐答案

因此,这里有两个主要的事情:

So, there are two major things going on here:


  1. 您希望在每次有新订阅者时共享一些副作用(在这种情况下,获取令牌),而不重新触发。

  2. 您希望任何订阅 -getToken 的用户都能获得相同的值。

  1. You want to share some side effects (in this case, fetching a token) without re-triggering them every time there's a new subscriber.
  2. You want anyone subscribing to -getToken to get the same values no matter what.

为了共享副作用(上面#1),我们将使用 RACMulticastConnection 。像文档所说:

In order to share side effects (#1 above), we'll use RACMulticastConnection. Like the documentation says:


多播连接封装了向许多订户共享一个订阅信号的想法。如果订阅底层信号涉及副作用,或者不应该多次调用,那么这是最常见的。

A multicast connection encapsulates the idea of sharing one subscription to a signal to many subscribers. This is most often needed if the subscription to the underlying signal involves side-effects or shouldn't be called more than once.

添加其中一个作为API客户端类的私有属性:

Let's add one of those as a private property on the API client class:

@interface APIClient ()
@property (nonatomic, strong, readonly) RACMulticastConnection *tokenConnection;
@end

现在,这将解决N个当前订阅者都需要相同的未来结果(API调用正在等待请求令牌正在传输),但是我们仍然需要其他东西,以确保未来订阅者获得相同的结果(已经获取的令牌),无论何时他们订阅。

Now, this will solve the case of N current subscribers that all need the same future result (API calls waiting on the request token being in-flight), but we still need something else to ensure that future subscribers get the same result (the already-fetched token), no matter when they subscribe.

这是 RACReplaySubject 用于:


重放主题保存发送的值(达到其定义的容量),并重新发送给新订阅者。

A replay subject saves the values it is sent (up to its defined capacity) and resends those to new subscribers. It will also replay an error or completion.

为了将这两个概念绑定在一起,我们可以使用RACSignal's-multicast:method ,它将正常信号转换为连接

To tie these two concepts together, we can use RACSignal's -multicast: method, which turns a normal signal into a connection by using a specific kind of subject.

我们可以在初始化时连接大部分行为:

We can hook up most of the behaviors at initialization time:

- (id)init {
    self = [super init];
    if (self == nil) return nil;

    // Defer the invocation of -reallyGetToken until it's actually needed.
    // The -defer: is only necessary if -reallyGetToken might kick off
    // a request immediately.
    RACSignal *deferredToken = [RACSignal defer:^{
        return [self reallyGetToken];
    }];

    // Create a connection which only kicks off -reallyGetToken when
    // -connect is invoked, shares the result with all subscribers, and
    // pushes all results to a replay subject (so new subscribers get the
    // retrieved value too).
    _tokenConnection = [deferredToken multicast:[RACReplaySubject subject]];

    return self;
}

然后,我们实现 -getToken 可以延迟触发抓取:

Then, we implement -getToken to trigger the fetch lazily:

- (RACSignal *)getToken {
    // Performs the actual fetch if it hasn't started yet.
    [self.tokenConnection connect];

    return self.tokenConnection.signal;
}

之后,任何订阅 getToken (如 -requestSignalWithURLRequest:)将获取令牌,如果它还没有被提取,开始提取它,如果必要,或等待飞行中的请求(如果有)。

Afterwards, anything that subscribes to the result of -getToken (like -requestSignalWithURLRequest:) will get the token if it hasn't been fetched yet, start fetching it if necessary, or wait for an in-flight request if there is one.

这篇关于如何在进行API调用之前使用ReactiveCocoa进行透明身份验证?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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