如何在 servant 中使用 Reqbody 接收 POST
How to receive POST with Reqbody in servant
下面的代码应该能够发送 json 正文。
但是我总是收到以下请求的错误:
curl -X POST -i http://localhost:8080/comtrade --data 'name=nut&age=12'
错误信息是:
Status Code: 405 Method Not Allowed
content-type: text/plain
date: Fri, 12 Mar 2021 18:49:04 GMT
server: Warp/3.3.14
transfer-encoding: chunked
data User = User {
age :: Int,
name :: String
} deriving Generic
instance FromJSON User where
parseJSON = withObject "User" parseUser
parseUser :: Object -> Parser User
parseUser o = do
n <- (o .: "name")
a <- (o .: "age")
return (User a n)
instance ToJSON User where
toJSON user = object
[ "age" .= age user
, "name" .= name user
]
type ComTradeAPI =
"comtrade" :> ReqBody '[JSON] User :> Post '[JSON] Int
:<|> "test" :> Get '[JSON] User
:<|> Raw
myServer :: Server ComTradeAPI
myServer = getUser
:<|> test
:<|> serveDirectoryWebApp "site"
where
test :: Handler User
test = return (User 12 "nut")
getUser :: User -> Handler Int
getUser usr = return 12
main :: IO ()
main = openBrowser "http://localhost:8080/index.html"
>> run 8080 (serve (Proxy :: Proxy ComTradeAPI) myServer)
谁能告诉我如何让 servant-server 接收 POST 消息?
正如 Fyodor Soikin 在评论中指出的那样,OP 中的 cURL 示例没有 post JSON,但是URL-编码数据。如果您使用 -v
(verbose) 选项代替 cURL 而不是 -i
:
$ curl -v http://localhost:8080/comtrade -d "{ \"name\": \"nut\", \"age\": 12 }"
* Trying ::1:8080...
* TCP_NODELAY set
* Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /comtrade HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.67.0
> Accept: */*
> Content-Length: 28
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 28 out of 28 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 405 Method Not Allowed
< Transfer-Encoding: chunked
< Date: Sat, 13 Mar 2021 12:52:59 GMT
< Server: Warp/3.2.28
< Content-Type: text/plain
<
Only GET or HEAD is supported
注意 Content-Type
是 application/x-www-form-urlencoded
。
ReqBody '[JSON] User
类型声明 API 期望正文为 JSON。那么,您需要做的第一件事就是 post JSON 而不是 URL 编码数据。
然而,这本身是不够的:
$ curl -v http://localhost:8080/comtrade -d "{ \"name\": \"nut\", \"age\": 12 }"
* Trying ::1:8080...
* TCP_NODELAY set
* Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /comtrade HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.67.0
> Accept: */*
> Content-Length: 28
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 28 out of 28 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 405 Method Not Allowed
< Transfer-Encoding: chunked
< Date: Sat, 13 Mar 2021 12:56:42 GMT
< Server: Warp/3.2.28
< Content-Type: text/plain
<
Only GET or HEAD is supported
请注意 cURL 仍然默认 Content-Type
为 application/x-www-form-urlencoded
。由于 API 被声明为接收 JSON,因此您必须明确告诉它 JSON:
$ curl -i http://localhost:8080/comtrade -H "Content-Type: application/json" -d "{ \"name\": \"nut\", \"age\": 12 }"
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Date: Sat, 13 Mar 2021 12:58:09 GMT
Server: Warp/3.2.28
Content-Type: application/json;charset=utf-8
12
据我所知,Haskell 代码没有任何问题。是正确使用HTTP协议的问题
下面的代码应该能够发送 json 正文。 但是我总是收到以下请求的错误:
curl -X POST -i http://localhost:8080/comtrade --data 'name=nut&age=12'
错误信息是:
Status Code: 405 Method Not Allowed content-type: text/plain date: Fri, 12 Mar 2021 18:49:04 GMT server: Warp/3.3.14 transfer-encoding: chunked
data User = User {
age :: Int,
name :: String
} deriving Generic
instance FromJSON User where
parseJSON = withObject "User" parseUser
parseUser :: Object -> Parser User
parseUser o = do
n <- (o .: "name")
a <- (o .: "age")
return (User a n)
instance ToJSON User where
toJSON user = object
[ "age" .= age user
, "name" .= name user
]
type ComTradeAPI =
"comtrade" :> ReqBody '[JSON] User :> Post '[JSON] Int
:<|> "test" :> Get '[JSON] User
:<|> Raw
myServer :: Server ComTradeAPI
myServer = getUser
:<|> test
:<|> serveDirectoryWebApp "site"
where
test :: Handler User
test = return (User 12 "nut")
getUser :: User -> Handler Int
getUser usr = return 12
main :: IO ()
main = openBrowser "http://localhost:8080/index.html"
>> run 8080 (serve (Proxy :: Proxy ComTradeAPI) myServer)
谁能告诉我如何让 servant-server 接收 POST 消息?
正如 Fyodor Soikin 在评论中指出的那样,OP 中的 cURL 示例没有 post JSON,但是URL-编码数据。如果您使用 -v
(verbose) 选项代替 cURL 而不是 -i
:
$ curl -v http://localhost:8080/comtrade -d "{ \"name\": \"nut\", \"age\": 12 }"
* Trying ::1:8080...
* TCP_NODELAY set
* Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /comtrade HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.67.0
> Accept: */*
> Content-Length: 28
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 28 out of 28 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 405 Method Not Allowed
< Transfer-Encoding: chunked
< Date: Sat, 13 Mar 2021 12:52:59 GMT
< Server: Warp/3.2.28
< Content-Type: text/plain
<
Only GET or HEAD is supported
注意 Content-Type
是 application/x-www-form-urlencoded
。
ReqBody '[JSON] User
类型声明 API 期望正文为 JSON。那么,您需要做的第一件事就是 post JSON 而不是 URL 编码数据。
然而,这本身是不够的:
$ curl -v http://localhost:8080/comtrade -d "{ \"name\": \"nut\", \"age\": 12 }"
* Trying ::1:8080...
* TCP_NODELAY set
* Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /comtrade HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.67.0
> Accept: */*
> Content-Length: 28
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 28 out of 28 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 405 Method Not Allowed
< Transfer-Encoding: chunked
< Date: Sat, 13 Mar 2021 12:56:42 GMT
< Server: Warp/3.2.28
< Content-Type: text/plain
<
Only GET or HEAD is supported
请注意 cURL 仍然默认 Content-Type
为 application/x-www-form-urlencoded
。由于 API 被声明为接收 JSON,因此您必须明确告诉它 JSON:
$ curl -i http://localhost:8080/comtrade -H "Content-Type: application/json" -d "{ \"name\": \"nut\", \"age\": 12 }"
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Date: Sat, 13 Mar 2021 12:58:09 GMT
Server: Warp/3.2.28
Content-Type: application/json;charset=utf-8
12
据我所知,Haskell 代码没有任何问题。是正确使用HTTP协议的问题