23

I'm developing a toy program which uses the Google URL shortener API. To shorten a URL, you need to send this request:

POST https://www.googleapis.com/urlshortener/v1/url
Content-Type: application/json

{"longUrl": "http://www.google.com/"}

and you will get this as response:

{
 "kind": "urlshortener#url",
 "id": "http://goo.gl/fbsS",
 "longUrl": "http://www.google.com/"
}

At first I use Network.HTTP, but found it doesn't support HTTPS, and Google's API only supports HTTPS. So I turn to Network.Curl. I find that there's a convenient function for HTTP GET

curlGetString :: URLString -> [CurlOption] -> IO (CurlCode, String)

but there's no such a function for HTTP POST. Even worse, I can't find a way to get the response data of HTTP POST. All I know is that I can issue a HTTP POST request using

curlPost :: URLString -> [String] -> IO ()

Could anyone show me a way out? Thanks.

fuz
  • 88,405
  • 25
  • 200
  • 352
kevin Chen
  • 303
  • 3
  • 7
  • 2
    jfyi, I ran into the same problem requiring `https://`, which `Network.HTTP` didn't support, but luckily the `http-enumerator` (http://hackage.haskell.org/package/http-enumerator) provided the support I wanted with a nicer API. – hvr Apr 10 '11 at 21:19

3 Answers3

15

To provide another alternative with http-conduit:

{-# LANGUAGE OverloadedStrings #-}

import Network.HTTP.Conduit
import qualified Data.ByteString.Lazy as L

main = do
  initReq <- parseUrl "https://www.googleapis.com/urlshortener/v1/url"

  let req' = initReq { secure = True } -- Turn on https
  let req = (flip urlEncodedBody) req' $
             [ ("longUrl", "http://www.google.com/")
  --           ,
             ]

  response <- withManager $ httpLbs req

  L.putStr $ responseBody response

Differences from http-enumerator

  1. The POST method is automatically set for you.
  2. The posted payload is automatically urlencoded.
  3. The Content-Type is automatically set as "application/x-www-form-urlencoded"
iamnat
  • 4,056
  • 1
  • 23
  • 36
Davorak
  • 7,362
  • 1
  • 38
  • 48
  • Any way to do this with http Basic authentication? – Lionel Apr 19 '14 at 21:46
  • 1
    It looks like you can just use [applyBasicAuth](http://hackage.haskell.org/package/http-conduit-2.1.1/docs/Network-HTTP-Conduit.html#v:applyBasicAuth) in the http-conduit package to modify the request. That what you are looking for? – Davorak Apr 19 '14 at 23:09
  • How do you post a JSON payload instead? – asjo Mar 31 '18 at 23:08
15

Just to provide an alternative solution via use of http-enumerator:

{-# LANGUAGE OverloadedStrings #-}

import Network.HTTP.Enumerator
import Network.HTTP.Types
import qualified Data.ByteString.Lazy as L

main = do
  req0 <- parseUrl "https://www.googleapis.com/urlshortener/v1/url"

  let req = req0 { method = methodPost
                 , requestHeaders = [("Content-Type", "application/json")]
                 , requestBody = RequestBodyLBS "{\"longUrl\": \"http://www.google.com/\"}"
                 }

  res <- withManager $ httpLbs req

  L.putStrLn $ responseBody res
hvr
  • 7,775
  • 3
  • 33
  • 47
  • Well, this is nice, I wasn't aware of this package before. I think this package has a nicer interface than Network.Curl. – kevin Chen Apr 11 '11 at 04:42
  • 7
    The developer of [http-enumerator](https://github.com/snoyberg/http-enumerator#readme) has moved future development to a new package: [http-conduit](https://github.com/snoyberg/http-conduit) – atomicules Feb 09 '12 at 11:42
  • WARNING: http-enumerator has been deprecated in favor of http-conduit – madnight Sep 23 '17 at 14:56
2

If you look at the source for curlPost you'll see it does this:

curlPost s ps = initialize >>= \ h -> do
  setopt h (CurlVerbose True)
  setopt h (CurlPostFields ps)
  setopt h (CurlCookieJar "cookies")
  setopt h (CurlURL s)
  perform h
  return ()

So I think you need to do a similar thing, but instead of the last two lines, write

  resp <- perform_with_response h

Also it looks like setopt is not exported, but setopts is, so you can use that instead.

Robin Green
  • 32,079
  • 16
  • 104
  • 187