拦截401以获取榆树中的任何响应

时间:2018-05-28 23:25:40

标签: elm

我在Elm中构建一个应用程序,其中大多数API调用都受到保护;即,用户需要登录才能使API调用起作用。如果用户未登录,则会收到401 Unauthorized响应。如果任何响应是401,我希望应用程序重定向到登录页面。

目前,我只为单个API调用设置了此重定向。这是一个精简版的代码,可以让我们了解它是如何设置的:

-- Util/Api.elm
type alias Data data =
    { data : data
    }

-- Resources/Expense.elm
getExpenses : (Progress (Api.Data (List Expense)) -> msg) -> Sub msg
getExpenses msg =
    (dataDecoder expenseListDecoder)
        |> Http.get expensesEndpoint
        |> Progress.track expensesEndpoint msg

-- Main/Msg.elm
type Msg
    = ExpenseListMsg ExpenseListMsg
    | RedirectToLogin

-- Main/Update.elm
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        ExpenseListMsg msg ->
            ExpenseList.Update.update msg model

        GoTo path ->
            model ! [ Navigation.newUrl path ]

        RedirectToLogin ->
            model ! [ Navigation.load "path/to/login" ]

-- ExpenseList/Msg.elm
type ExpenseListMsg
    = GetExpensesProgress (Progress (Api.Data (List Expense)))
    | SetLoading

-- ExpenseList/Update.elm
update : ExpenseListMsg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        SetLoading ->
            { model | expenses = setExpensesLoading model.expenses } ! []

        GetExpensesProgress (Done { data }) ->
            { model | expenses = addExpenses model.expenses data } ! []

        GetExpensesProgress (Fail (BadStatus { status })) ->
            case status.code of
                401 ->
                    model ! [ msgToCmd RedirectToLogin ]

                _ ->
                    model ! []

        GetExpensesProgress (Fail error) ->
            model ! []

        GetExpensesProgress progress ->
            { model | expenses = setExpensesLoading model.expenses } ! []

基本上,我想将401响应的逻辑从ExpenseList/Update.elm移到Main/Update.elm,以便我可以将它用于我想要的任何请求。

我尝试过很多东西,但是对于Elm的类型系统来说没有什么用处。例如,我想做的一件事是尝试在中间进行缺少特异性的嵌套模式匹配,例如:

-- Main/Update.elm
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        ApiCall (messageType (msg (Fail (BadStatus { status })))) ->
            case status of ->
              . . .
      . . .

我希望这样的东西可以工作,并且会匹配一条看起来像ApiCall (ExpenseListMsg (GetExpensesProgress (Fail (BadStatus))))的消息。不幸的是,它不是正确的Elm语法,所以我的代码没有编译。

如何编写允许我在401的顶层将API调用标记为受保护并捕获Main.Update.update错误的内容?

1 个答案:

答案 0 :(得分:1)

目前,API调用由ExpenseList/Update模块封装。这种封装使得API调用结果对Main模块不可用。交互的工作方式如下:Main -> FeatureModule -> API

由于API提供了确定应用程序是否应重定向到登录页面所需的信息,并且您希望Main模块执行重定向,因此Main模块需要访问API。因此,需要进行封装。相反,你可以:

  • 拥有一个API模块,通过生成Task来提供低级API功能。与生成Cmd不同,这允许调用者(例如Main模块)决定如何处理Task的结果,这可以通过转换Task来获取到Cmd并将其提供给Elm运行时执行。
  • ExpenseList.Update模块使用API​​模块创建Tasks

通过这种安排:

  • Main模块高级命令发送到功能模块,功能模块然后使用API​​模块生成低级指令,然后将其提供给{{1模块。
  • Main模块并不需要关心这些低级指令是什么,它只是将Main转换为Task并等待结果。
  • 当结果返回时,它处于低级格式(例如成功/失败)。此时,Cmd模块可以跳入并处理401错误的重定向。否则,它可以将结果传递给功能模块,以便它可以处理结果。