我在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
错误的内容?
答案 0 :(得分:1)
目前,API调用由ExpenseList/Update
模块封装。这种封装使得API调用结果对Main
模块不可用。交互的工作方式如下:Main -> FeatureModule -> API
由于API提供了确定应用程序是否应重定向到登录页面所需的信息,并且您希望Main
模块执行重定向,因此Main
模块需要访问API
。因此,需要进行封装。相反,你可以:
Task
来提供低级API功能。与生成Cmd
不同,这允许调用者(例如Main
模块)决定如何处理Task
的结果,这可以通过转换Task
来获取到Cmd
并将其提供给Elm运行时执行。ExpenseList.Update
模块使用API模块创建Tasks
。 通过这种安排:
Main
模块将高级命令发送到功能模块,功能模块然后使用API模块生成低级指令,然后将其提供给{{1模块。 Main
模块并不需要关心这些低级指令是什么,它只是将Main
转换为Task
并等待结果。Cmd
模块可以跳入并处理401错误的重定向。否则,它可以将结果传递给功能模块,以便它可以处理结果。