You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在WAI中间件中使用全局状态(Scotty框架场景)

在Scotty中间件中访问全局状态

要解决Scotty中间件访问全局Token状态的问题,核心是让中间件运行在全局状态的StateT上下文里,而非先取出状态再传递给中间件。以下是具体实现方案:

1. 定义全局状态类型

先定义存储有效Token的全局状态结构:

import qualified Data.Set as Set
import Data.Text (Text)

data AppState = AppState
  { validTokens :: Set.Set Text  -- 存储有效Token的集合
  } deriving (Show)

-- 初始状态:空Token集合
initialAppState :: AppState
initialAppState = AppState Set.empty

2. 实现带状态访问的认证中间件

让中间件直接在StateT AppState IO上下文运行,通过gets获取当前有效Token集合:

import Web.Scotty
import Network.Wai (Middleware, requestHeaders)
import Network.HTTP.Types.Status (status401)
import Network.Wai.Response (responseLBS)
import Data.List.Split (splitOn)

authMiddleware :: Middleware (StateT AppState IO)
authMiddleware app req respond = do
  -- 从Authorization头解析Bearer Token
  let maybeToken = lookup "Authorization" (requestHeaders req) >>= parseBearer
  isValid <- case maybeToken of
    Nothing -> return False
    Just token -> do
      tokens <- gets validTokens
      return $ Set.member token tokens
  
  if isValid
    then app req respond  -- Token有效,继续处理请求
    else respond $ responseLBS status401 [] "Unauthorized"  -- Token无效,返回401
  where
    parseBearer header = case splitOn " " header of
      ["Bearer", token] -> Just token
      _ -> Nothing

3. 在Scotty应用中使用中间件和全局状态

在路由中通过webM操作全局状态(比如登录时添加新Token),并直接注册中间件:

import Control.Monad.State (modify)
import Data.Aeson (object, (.=))

app :: ScottyT Text (StateT AppState IO) ()
app = do
  -- 注册认证中间件(作用于后续所有路由)
  middleware authMiddleware

  -- 登录路由:无需认证,生成并存储Token
  post "/login" $ do
    -- 此处添加用户名密码验证逻辑,示例省略
    let newToken = "generated-token-123"  -- 替换为实际Token生成逻辑
    -- 将新Token添加到全局状态
    webM $ modify (\s -> s { validTokens = Set.insert newToken (validTokens s) })
    json $ object ["token" .= newToken]

  -- 需要认证的示例路由
  get "/protected" $ do
    html "<h1>Protected Content</h1>"

4. 启动服务器

用runStateT将Scotty应用与初始状态绑定,启动服务器:

main :: IO ()
main = do
  _ <- runStateT (scotty 3000 app) initialAppState
  return ()

原代码问题说明

你尝试先通过webM取出tokensList再传给中间件,但middleware是在Scotty上下文直接调用的,中间件本身可直接访问StateT上下文的状态,无需提前提取。直接在中间件内用gets获取最新全局Token集合,能避免状态同步问题。

内容的提问来源于stack exchange,提问作者KevinT3Hu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 08:32:07