F#中API包装器的功能建模

布赖恩·瓦莱伦加(Brian Vallelunga)

我正在尝试学习F#和函数式编程,并且不确定如何为以下场景建模。

我试图围绕F#中的XML API编写一个简单的包装。此API需要我首先使用用户名和密码进行“登录” API调用。然后,API返回用于验证后续调用的会话令牌。在某些时候,会话将过期,我需要再次“登录”。

在一个面向对象的项目中,我将创建一个ApiClient类。它会将用户名和密码作为构造函数参数,为每个API调用公开一个公共方法,并在内部处理登录过程。会话令牌和状态可以对使用方应用程序完全隐藏,并且只要ApiClient实例存在,会话令牌就可以维护。

我不确定如何以功能方式组织代码来实现相似的结果。如果我没有创建ApiClient类,那么内部会话令牌数据应该存储在哪里?显然,我可以在F#中创建ApiClient类,但我正在寻找有关如何更有效地执行此操作的见解。

感谢您的任何见解。

内在之光

我已经对答案做了很大的修改,以尝试将该过程浓缩为最基本的要求,以帮助您入门。

首先要做的是设计一个处理会话状态的系统。最简单的方法是创建一个类型以包含只能在您的库内部创建的会话数据,并创建第二种类型(这是一个区分联合)。

type SessionData internal (id : System.Guid, creationTime : System.DateTime) = 
    member this.UniqueID  = id
    member this.CreationTime = creationTime

type SessionState<'a> =
    |Valid of 'a * SessionData
    |Invalid

您的登录功能使用用户名和密码,并返回有效或无效的会话状态。

let login username password =
    match isUsernameAndPasswordCorrect with
    |true -> Valid ((), SessionData(System.Guid.NewGuid(), System.DateTime.UtcNow))
    |false -> Invalid

当我们调用API时,我们将把会话状态映射从一个旧的会话状态更新到一个新的会话状态,并检查过程的有效性。

let private updateSession result (sessionState : SessionData) =
    let loginTimeSpan = sessionState.CreationTime - System.DateTime.UtcNow
    match loginTimeSpan < maxLoginSpan with
    |true -> Valid <| (result, SessionData(System.Guid.NewGuid(), System.DateTime.UtcNow))
    |false -> Invalid

显然,如果愿意,我们可以使验证和更新过程更加严格。

在这里,我们将在会话状态内包含对您的API的调用结果。每个API调用都有:

let apiCall args sessionState =
    updateSession (internalFunction args) sessionState

如果调用成功,我们将获得一个包含新会话和结果的新有效状态。


如果要完全隐藏交换会话状态信息的过程,则可以创建一个计算表达式来处理。一个非常简单的示例可能是:

 type XMLAPIBuilder() =
     member this.Bind (x, f) =
        fun sessionState ->
             match x sessionState with
             |Valid (call, newSessionState) -> f call newSessionState
             |Invalid -> Invalid

     member this.Return(x) =
         fun sessionState -> Valid (x, sessionState)

您可以使用以下表达式,然后使用计算表达式语法:

let xmlapi = XMLAPIBuilder()
let apiCalls = 
    xmlapi {
       let! a = apiCall1 args1
       let! b = apiCall2 args2
       let! c = apiCall3 args3
       return (a, b, c) // result of several api calls
    }
let result = apiCalls session // pass in session data once to retrieve results

为了完全删除会话数据,我们可以创建一个不包含会话状态和run函数的新类型

type APICallState<'a> =
    |CallSuccess of 'a
    |CallFailed

let run username password apiExpression =
    match testmagic.login "" "" with
    |Valid (_, state) -> 
        match apiExpression state with
        |Valid (result, _) -> CallSuccess result
        |Invalid -> CallFailed
    |Invalid -> CallFailed

这使我们可以设计一个复杂的表达式,对您的API进行一系列相关的调用。我们只提供一次用户名和密码,然后进行所有通话。

计算表达式绝对可以成为新手无法访问的语言部分之一,因此我建议您查看一些教程,例如:http : //fsharpforfunandprofit.com/series/computation-expressions.html

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章