尝试使用 oAuth2 在 haskell 中执行 https get 请求

Trying to execute https get request in haskell with oAuth2

这是我尝试连接到 TDAmeritrade 的 api 使用 haskell 中的 Network.HTTP.Req 库接收 json 响应。

{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE OverloadedStrings #-}

module Main where

import Lib
import Wuss
import Control.Concurrent
import Control.Monad
import Data.Text
import Network.WebSockets 
import Network.Connection
import Network.WebSockets.Stream
import Data.Text.Lazy
import Data.ByteString
import Lens.Micro.GHC.Internal
import Network.Curl
import Data.ByteString.Lazy.UTF8
import Network.HTTP.Req
import Control.Monad
import Control.Monad.IO.Class
import Data.Aeson
import Data.Maybe 
import Data.Monoid 
import GHC.Generics
import Network.HTTP.Req
import Data.ByteString.Char8

tok = Data.ByteString.Char8.pack "accesstoken"
main :: IO ()
main = runReq defaultHttpConfig $ do
  v <- req GET (https "api.tdameritrade.com" /: "v1" /: "userprincipals?fields=streamerConnectionInfo") (NoReqBody) jsonResponse (oAuth2Bearer tok)
  liftIO $ Data.ByteString.Char8.putStrLn (responseBody v)

如果正确完成,响应应该只会给出一个错误,指出身份验证令牌不正确,因为我故意没有在这里提供有效的令牌。 “访问令牌”

如果我使用 bsResponse 而不是 jsonResponse,它会进行类型检查,但不会使用 jsonResponse 进行类型检查。 TDAmeritrade 的回复将在 JSON.

这是错误:

No instance for (FromJSON Data.ByteString.Char8.ByteString)
    arising from a use of ‘req’

这是我使用 bsResponse 的结果:

  port                 = 443
  secure               = True
  requestHeaders       = [("Authorization","<REDACTED>")]
  path                 = "/v1/userprincipals%3Ffields=streamerConnectionInfo"
  queryString          = ""
  method               = "GET"
  proxy                = Nothing
  rawBody              = False
  redirectCount        = 10
  responseTimeout      = ResponseTimeoutDefault
  requestVersion       = HTTP/1.1
}
 (StatusCodeException (Response {responseStatus = Status {statusCode = 400, statusMessage = "Bad Request"}, responseVersion = HTTP/1.1, responseHeaders = [("Date","Wed, 08 Sep 2021 15:27:41 GMT"),("Content-Type","application/json"),("Content-Length","78"),("Connection","keep-alive"),("Host","api.tdameritrade.com"),("X-Forwarded-Port","9002"),("X-Forwarded-Proto","http"),("Accept-Encoding","gzip"),("Authorization","Bearer accesstoken"),("NS-Proxy-Client-IP","82.46.185.98"),("Access-Control-Allow-Origin",""),("Access-Control-Allow-Headers","origin, x-requested-with, accept, authorization, content-type, correlationid, apikey, application-name"),("Access-Control-Max-Age","3628800"),("Access-Control-Allow-Methods","GET, PUT, POST, DELETE, OPTIONS, HEAD, PATCH"),("X-Xss-Protection","1; mode=block"),("X-Content-Type-Options","nosniff"),("X-Frame-Options","SAMEORIGIN"),("Content-Security-Policy","frame-ancestors 'self'"),("Cache-Control","no-cache,no-store,must-revalidate"),("Strict-Transport-Security","max-age=31536000")], responseBody = (), responseCookieJar = CJ {expose = []}, responseClose' = ResponseClose}) "\n          {\n        \"error\":\"The endpoint doesnt exist.\"\n      \t\t}\n          "))

作为参考,终端中的这个 curl 命令产生了正确的响应:

curl -X GET --header "Authorization: Bearer authorizationtoken" "https://api.tdameritrade.com/v1/userprincipals?fields=streamerConnectionInfo"

我完全迷路了。请帮忙

要修复您的即时错误,您必须使用 (=:) 函数构造请求参数,而不是手动编写 ?fields=streamerConnectionInfo 部分。并且您还需要禁用错误状态代码检查。这是复制您的 curl 命令的工作代码:

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString.Char8
import Network.HTTP.Req
import Control.Monad.IO.Class
import Data.Text

tok = Data.ByteString.Char8.pack "accesstoken"
main :: IO ()
main = runReq defaultHttpConfig { httpConfigCheckResponse = \_ _ _ -> Nothing } $ do
  v <- req GET (https "api.tdameritrade.com" /: "v1" /: "userprincipals") (NoReqBody) bsResponse ("fields" =: ("streamerConnectionInfo" :: Text) <> oAuth2Bearer tok)
  liftIO $ Data.ByteString.Char8.putStrLn (responseBody v)

另一个问题是从生成的 JSON 主体访问字段。我建议从阅读 the documentation of Aeson 开始,这是主要的 Haskell JSON 包。如果您使用 jsonResponse 请求类型,那么它会自动将响应转换为具有 FromJSON 实例的任何类型。

或许也能帮到你