我正在 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 语法,因此我的代码无法编译。
如何编写一些内容,允许我将 API 调用标记为 protected 并在 Main.Update.update
的顶层捕获 401
错误?
最佳答案
目前,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 模块生成低级指令,然后将其提供给主
模块。Main
模块不需要关心这些低级指令是什么,它只是将Task
转换为Cmd
并等待结果。- 结果返回时采用低级格式(例如成功/失败)。此时,
Main
模块可以跳转并处理 401 错误的重定向。否则,它可以将结果传递给功能模块,以便它可以处理结果。
关于elm - 拦截 Elm 中的任何响应的 401,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50574477/