I'm trying user BasicAuth in servant :
type ServerAPI = "news" :> BasicAuth "auth" User :> ReqBody '[JSON] News :> Post '[JSON] Bool
Documentation and cookbook both suggests having Server API like this :
server :: Server ServerAPI
server = createNewsHandler (user :: User)
and createNewsHandler of type
User -> News -> Handler Bool
However, there is no 'user' variable in the scope of server function and code does not compile of course.
I wonder where can find valid example of using this feature.
I tried reading documentation, cookbook, articles with examples and expected finding proper examples.
Related
The documentation at hackage.haskell.org/package/servant-multipart says about the main combinator type MultipartForm tag a following:
Note that the behavior of this combinator is configurable, by using serveWith from servant-server instead of serve, which takes an additional Context argument. It simply is an heterogeneous list where you can for example store a value of type MultipartOptions that has the configuration that you want, which would then get picked up by servant-multipart.
And later:
data MultipartOptions tag
Global options for configuring how the server should handle multipart data.
generalOptions lets you specify mostly multipart parsing related options, such as the maximum file size …
However, I don't understand how to correctly call the mentioned serveWithContext:
serveWithContext
:: (HasServer api context, ServerContext context)
=> Proxy api
-> Context context
-> Server api
-> Application
Searching for its usage on GitHub also didn't enlighten me, unfortunately.
Following code does the job:
import Prelude (($))
import Servant
( Application, Proxy(Proxy)
, Context(EmptyContext, (:.)), serveWithContext
)
import Servant.Multipart
( MultipartOptions(generalOptions)
, defaultMultipartOptions, Mem
)
import Network.Wai.Parse
( defaultParseRequestBodyOptions, setMaxRequestFileSize )
app :: Application
app =
let
size10MB = 10000000
multipartOpts = (defaultMultipartOptions (Proxy :: Proxy Mem))
{ generalOptions =
setMaxRequestFileSize size10MB
defaultParseRequestBodyOptions
}
context = multipartOpts :. EmptyContext
in
serveWithContext appApi context appSserver
Make sure to use the correct MultipartData tag for the Proxy type (Mem or Tmp). Documentation: hackage.haskell.org/package/servant-multipart
You can check out hackage.haskell.org/package/wai-extra for all available config changes.
The tutorial here Getting started · ArangoDB v3.4.0 Documentation uses this code:
// continued
router.post('/sum', function (req, res) {
const values = req.body.values;
res.send({
result: values.reduce(function (a, b) {
return a + b;
}, 0)
});
})
.body(joi.object({
values: joi.array().items(joi.number().required()).required()
}).required(), 'Values to add together.')
.response(joi.object({
result: joi.number().required()
}).required(), 'Sum of the input values.')
.summary('Add up numbers')
.description('Calculates the sum of an array of number values.');
What is an example of a URI that supplies the expected parameters (two numbers)?
Assuming that your server instance runs on localhost:8529 over HTTP, that the database is _system and the mount point of the Foxx service /getting-started, then the URL of the /sum endpoint is:
http://localhost:8529/getting-started/sum
Note that the database _system is special: It is the default, which means you don't have to specify it explicitly. The following URL is equivalent:
http://localhost:8529/_db/_system/getting-started/sum
Replace _system with the name of the actual database if the Foxx service is mounted in another one.
/sum is a POST route (router.post(...)) and the expected body (content/payload of the HTTP request) is described by the joi schema: A JSON object with the attribute name values and as attribute value a number array (one or more numbers).
With Curl, you can query the service like so:
curl --data "{\"values\":[5,6]}" http://localhost:8529/getting-started/sum
(The request method -X POST is inferred by Curl)
The response is a JSON object with an attribute key result and the calculated number as attribute value:
{"result":11}
If you try to access the URL in a browser instead, it will be a GET request (without payload) and fail with an HTTP error: 405 Method Not Allowed
I have a wai middleware that produces two values:
A request id generated randomly for each request
A User from a request to another service.
Here is the code:
addRequestIdToRequestHeader' :: Application -> Application
addRequestIdToRequestHeader' app req respond = do
rid <- nextRequestId :: IO ByteString
user <- fetchUserByReq req :: IO User
req' <- attachUserAndRequestIdToRequest user rid
app req' respond
Now I have a route GET /user. Inside of this route, I’d like to have access to the request id and the User. As an example, I might just print them in the log.
main :: IO ()
main =
scotty 8080 $ do
get "/user" $ do
req <- request
rid <- liftAndCatchIO $ getRequestIdFromRequest req
user <- liftAndCatchIO $ getUserFromRequest req
liftAndCatchIO $ print rid
liftAndCatchIO $ print user
text $ username user
The question is since the request id and the User are generated from the middleware, how to access them from the route? Basically how to implement the following functions used in the above code:
attachUserAndRequestIdToRequest :: User -> ByteString -> Request -> IO Request
getRequestIdFromRequest :: Request -> IO ByteString
getUserFromRequest :: Request -> IO User
The scenario is the middleware is an Auth middleware, which forwards the request to another service for authentication and gets back the user value, which will be needed in the routes.
I have a wai/warp application. How can I handle a post request? I have this:
app :: Application
app request respond =
respond $ case rawPathInfo request of
"/" -> responseFile status200 ............
"/some_post_req/" -> .....
How can I specify that some_post_req must be POST?
It should be as simple as comparing the result of Network.Wai.requestMethod against Network.Wai.methodPost:
app request respond
| requestMethod request == methodPost
= respond $ case rawPathInfo request of
{- handle POST routes -}
| otherwise
= {- handle other request methods -}
Since there are constants for methodPost, methodGet, &c., you might as well use them, but Method is an alias for ByteString, so you could also use the OverloadedStrings extension:
{-# LANGUAGE OverloadedStrings #-}
And then either compare with a string literal:
requestMethod request == "POST"
Or pattern match:
case requestMethod request of
"POST" -> {- … -}
"GET" -> {- … -}
…
parseRequestBody in the wai-extra package allows you to get the data you want out of the body of the request:
(params, files) <- parseRequestBody lbsBackEnd request
But this does nothing to specify that the request must be a POST request. Keep in mind that wai can be pretty low level, and there are higher level packages out there.
servant comes to mind as a package that allows you to define APIs at the type level. With servant, you definitely can specify the HTTP verb you expect. See here.
I am pretty new to Haskell and I am not sure how I can achieve this. I am using wreq as HTTP client and I would like to check what HTTP headers are being sent to the server. For instance, I have defined the following method, which tries to authenticate using Facebook credentials:
authenticate ⦂ String → String → IO (Response ByteString)
authenticate facebookId facebookToken =
postWith opts "https://api.somedomain.com/auth" (object $ ["facebook_token" .= facebookToken, "facebook_id" .= facebookId])
where opts = defaults & header "Content-Type" .~ ["application/json"]
& header "Accept" .~ ["application/json"]
& proxy ?~ httpProxy "localhost" 9396
With POSTman I get the proper response but with wreq I get forbidden access (403). I assume some extra request headers might be added by the library and that's what I would like to check.
Any hints?
EDIT: An http proxy is now being used to check HTTP traffic (wreq requests are not detected though).
In Wreq, the Options type is an instance of Show, so you can pass it to print, or simply inspect it on ghci:
ghci> import Network.Wreq
ghci> defaults :: Options
Options { manager = Left _, proxy = Nothing, auth = Nothing, headers =
[("User-Agent","haskell wreq-0.3.0.1")], params = [], redirects = 10, cookies = [] }
You can also use the headers lens to focus directly on the headers:
ghci> defaults ^. headers
[("User-Agent","haskell wreq-0.3.0.1")]
But if Wreq is adding some headers after passing the options to postWith, you'll have to use a web debugging proxy like Fiddler to know what's going on.