Elm对接Django时如何自动将CSRF Token添加至HTTP请求头?
解决方案
Elm官方的Http库没有提供类似Axios的全局默认配置能力,无法声明式全局自动注入请求头,但是可以通过两层封装实现相同的使用体验,无需每次编写请求时手动处理CSRF token。
第一步:获取CSRF Token
Elm运行环境无法直接读取浏览器Cookie,有两种稳定的获取CSRF Token的方案:
- 方案1:Django模板注入到Elm启动参数(Flags)
在Django渲染的入口HTML页面中,直接将CSRF Token通过初始化参数传给Elm:<script> // 从Django模板获取csrf token,传入Elm const app = Elm.Main.init({ flags: { csrfToken: "{{ csrf_token }}" } }) </script> Elm侧对应定义Flags类型,将拿到的token存在全局Model中即可。 - 方案2:通过端口从JS侧读取Cookie
如果Elm应用是纯静态部署、不通过Django模板渲染,可以定义Elm端口,在JS侧读取Cookie中的csrftoken值传给Elm:// JS侧读取Cookie的csrftoken function getCsrfToken() { const value = `; ${document.cookie}`; const parts = value.split(`; csrftoken=`); if (parts.length === 2) return parts.pop().split(';').shift(); } // 通过端口传给Elm app.ports.receiveCsrfToken.send(getCsrfToken());
第二步:封装通用HTTP请求函数
一次性封装带CSRF头的请求工具函数,后续所有业务请求直接调用封装后的函数即可,无需重复处理头信息:
import Http import Json.Decode as D import Json.Encode as E -- 通用带CSRF头的请求构建函数,参数为存在Model中的csrfToken authenticatedRequest : String -> String -> String -> Http.Body -> D.Decoder a -> Http.Request a authenticatedRequest csrfToken method url body decoder = Http.request { method = method , url = url , headers = [ Http.header "X-CSRFTOKEN" csrfToken ] , body = body , expect = Http.expectJson decoder , timeout = Nothing , withCredentials = True -- 跨域请求时需要开启,同域可设为False } -- 封装常用POST请求 csrfPost : String -> String -> E.Value -> D.Decoder a -> Cmd (Result Http.Error a) csrfPost csrfToken url reqBody decoder = Http.send identity <| authenticatedRequest csrfToken "POST" url (Http.jsonBody reqBody) decoder -- 封装常用PUT请求 csrfPut : String -> String -> E.Value -> D.Decoder a -> Cmd (Result Http.Error a) csrfPut csrfToken url reqBody decoder = Http.send identity <| authenticatedRequest csrfToken "PUT" url (Http.jsonBody reqBody) decoder -- 封装常用GET请求 csrfGet : String -> String -> D.Decoder a -> Cmd (Result Http.Error a) csrfGet csrfToken url decoder = Http.send identity <| authenticatedRequest csrfToken "GET" url Http.emptyBody decoder
业务侧使用示例
后续发起请求时只需传入存在Model中的CSRF Token即可,无需再手动处理头信息:
-- 示例:提交用户信息 submitUserInfo : Model -> Cmd Msg submitUserInfo model = csrfPost model.csrfToken "/api/user/" (E.object [ ("name", E.string model.inputName) , ("age", E.int model.inputAge) ] ) userInfoDecoder
注意事项
- 未稳定的Effect Manager API可以实现全局请求拦截,但官方不推荐在生产环境使用,封装工具函数是当前最稳妥的方案
- 跨域场景下需要在Django侧配置
CORS_ALLOW_CREDENTIALS = True,同时配置正确的CORS允许域名,不要使用通配符
内容的提问来源于stack exchange,提问作者Ulrich Schuster
相关产品推荐
相关产品推荐

