Haskell 中 GET 请求的编码问题

Encoding problem with GET requests in Haskell

我正在尝试使用 Haskell 从 Jira 服务器获取一些 Json 数据。我将其计为 "me having problems with Haskell" 而不是编码或 Jira,因为我的问题是在 Haskell.

中执行此操作时

当 URL(或查询)有加号时会出现问题。在构建我对 theproject+order+by+created 的请求后,Haskell 将其打印为:

Request {
  host                 = "myjiraserver.com"
  port                 = 443
  secure               = True
  requestHeaders       = [("Content-Type","application/json"),("Authorization","<REDACTED>")]
  path                 = "/jira/rest/api/2/search"
  queryString          = "?jql=project%3Dtheproject%2Border%2Bby%2Bcreated"
  method               = "GET"
  proxy                = Nothing
  rawBody              = False
  redirectCount        = 10
  responseTimeout      = ResponseTimeoutDefault
  requestVersion       = HTTP/1.1
}

但是请求失败并返回以下响应:

- 'Error in the JQL Query: The character ''+'' is a reserved JQL character. You must
  enclose it in a string or use the escape ''\u002b'' instead. (line 1, character
  21)'

所以 Jira 似乎不喜欢 Haskell 的 %2B。您对我可以做些什么来解决这个问题有什么建议,或者有什么可能有用的资源吗?没有 +order+by+created 部分的相同请求成功。

代码(从these examples一起修补):

{-# LANGUAGE OverloadedStrings #-}
import           Data.Aeson
import qualified Data.ByteString.Char8 as S8
import qualified Data.Yaml             as Yaml
import           Network.HTTP.Simple
import           System.Environment    (getArgs)

-- auth' is echo -e "username:passwd" | base64
foo urlBase proj' auth' = do
    let proj = S8.pack (proj' ++ "+order+by+created")
        auth = S8.pack auth'
    request'' <- parseRequest urlBase
    let request'
            = setRequestMethod "GET"
            $ setRequestPath "/jira/rest/api/2/search"
            $ setRequestHeader "Content-Type" ["application/json"]
            $ request''
        request
            = setRequestQueryString [("jql", Just (S8.append "project=" proj))]
            $ setRequestHeader "Authorization" [S8.append "Basic " auth]
            $ request'
    return request

main :: IO ()
main = do
    args <- getArgs
    case args of
      (urlBase:proj:auth:_) -> do
          request <- foo urlBase proj auth
          putStrLn $ show request
          response <- httpJSON request
          S8.putStrLn $ Yaml.encode (getResponseBody response :: Value) -- apparently this is required
          putStrLn ""

      _ -> putStrLn "usage..."

(如果你知道一种更简单的方法来完成上述操作,那么我也会采纳这些建议,我只是想做一些类似于此的事情 Python:

import requests
import sys
if len(sys.argv) >= 4:
    urlBase = sys.argv[1]
    proj = sys.argv[2]
    auth = sys.argv[3]
    urlBase += "/jira/rest/api/2/search?jql=project="
    proj += "+order+by+created"
    h = {}
    h["content-type"] = "application/json"
    h["authorization"] = "Basic " + auth
    r = requests.get(urlBase + proj, headers=h)
    print(r.json())

)

project+order+by+created 是实际请求 project order by createdURL-encoded 字符串(用空格代替 +)。函数 setRequestQueryString 需要一个原始请求(带空格,而不是 URL 编码),然后 URL 对其进行编码。

您提供的用于比较的 Python 脚本本质上是手动执行 URL 编码。

因此解决方法是将原始请求放入 proj:

foo urlBase proj' auth' = do
    let proj = S8.pack (proj' ++ " order by created")  -- spaces instead of +
    ...