如何在进行API调用之前使用ReactiveCocoa进行透明身份验证?

前端之家收集整理的这篇文章主要介绍了如何在进行API调用之前使用ReactiveCocoa进行透明身份验证?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我在一个应用程序中使用ReactiveCocoa调用远程Web API。但在从给定API主机检索任何内容之前,应用程序必须提供用户的凭据并检索API令牌,然后用于对后续请求进行签名。

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

// 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令牌?

这也是一个要求,我可以使用combineLatest :(或类似)启动多个并发请求,他们将隐式等待令牌被检索。

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

此外,如果在进行API调用时检索令牌请求已经处于运行中,那么该API调用必须等待,直到检索令牌请求完成。

我的部分解答如下:

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

假设有一些方便的扩展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调用

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

所以,这里有两个主要的事情:

>你想共享一些副作用(在这种情况下,获取令牌),而不是每次有新的订阅者重新触发。
>您希望任何订阅-getToken的人获得相同的值,无论什么。

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

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客户端类的私有属性

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

现在,这将解决所有需要相同的未来结果(API调用在请求令牌正在进行中)的N个当前订阅者的情况,但是我们仍然需要别的以确保未来的订阅者获得相同的结果(已经 – 获取令牌),无论他们什么时候订阅

这是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,它通过使用特定类型的主题将正常信号转换为连接。

我们可以在初始化时连接大多数行为:

- (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懒惰地触发抓取:

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

    return self.tokenConnection.signal;
}

之后,订阅-getToken(如-requestSignalWithURLRequest :)的结果的任何内容都将获取令牌(如果尚未提取),如果需要,请开始提取令牌,或者等待正在传送的请求(如果有)。

猜你在找的React相关文章