Send request as content type of x-www-form-urlencoded with wreq

403 Views Asked by At

I'm learning to use wreq this weekend and I've run into some strange behavior.

I have a module AuthRequest

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
module AuthRequest where

import Data.Aeson
import GHC.Generics
import Data.Monoid

data AuthRequest = AuthRequest {
    client_id :: String
  , client_secret :: String
  , grant_type :: String
} deriving (Generic,  Show)

instance ToJSON AuthRequest where
    toJSON (AuthRequest id_ secret grant) =
      object [ "client_id" .= id_
             , "client_secret" .= secret
             , "grant_type" .= grant
             ]
    toEncoding(AuthRequest id_ secret grant) =
      pairs ("client_id" .= id_ <> "client_secret" .= secret <> "grant_type" .= grant)

and a module HttpDemo

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
module HttpDemo where

import Control.Lens
import Network.Wreq
import AuthRequest
import Data.Aeson

clientId = "some_id"
clientSecret = "some_secret"
url = "http://localhost:5000"

opts :: Options
opts = defaults
  & header "Content-Type" .~ ["application/x-www-form-urlencoded"]

req :: AuthRequest
req = AuthRequest clientId clientSecret "credentials"

postIt = postWith opts url (toJSON req)

On the other end, I have a simple python flask server that listens to this request with a breakpoint so I can see the value that comes through.

When I look at the request.form on the server side, I see this: ImmutableMultiDict([('{"client_secret":"some_secret","client_id":"some_id","grant_type":"whatever"}', '')])

The key is what should be my post body!

But if I make a similar request using the requests python library

requests.post('http://localhost:5000', data={'client_id': clientId, 'client_secret': clientSecret, 'grant_type': grant_type}, headers={'content-type': 'application/x-www-form-urlencoded'})

I see what I expect: ImmutableMultiDict([('grant_type', 'whatever'), ('client_id', 'some_id'), ('client_secret', 'some_secret')])

What I think I want is to send this request as x-www-form-urlencoded. I see there are some docs here around this, but not clear on how to proceed. Maybe I need a FormValue instance? An example would be helpful.

1

There are 1 best solutions below

0
On

As per your discussion with @Alexis, your client seems to be sending JSON, while the server is expected urlencoded. The documentation of Post shows how to send urlencoded data, using the := constructor. In this case this would be

postIt = post url ["client_id" := clientId, "client_secret" := clientSecret, "grant_type" := grantType]

I've given the example using post rather than postWith since the default appears to be that it uses application/x-www-form-urlencoded.


There seems to be a slight complication with OverloadedStrings. To make a compilable program, I had to remove the AuthRequest module and explicitly give the types of the constants as below

{-# LANGUAGE OverloadedStrings #-}
module Main where

import Network.Wreq
import Data.ByteString as B

clientId = "some_id" :: ByteString
clientSecret = "some_secret" :: ByteString
grantType = "credentials" :: ByteString
url = "http://localhost:8080"

postIt = post url ["client_id" := clientId, "client_secret" := clientSecret, "grant_type" := grantType]

main = postIt