Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Network.Wai.Middleware.MethodOverride not supported #7

Closed
sordina opened this issue Apr 16, 2012 · 3 comments
Closed

Network.Wai.Middleware.MethodOverride not supported #7

sordina opened this issue Apr 16, 2012 · 3 comments

Comments

@sordina
Copy link
Contributor

sordina commented Apr 16, 2012

Network.Wai.Middleware.MethodOverride allows you to override the restful method of an HTML form. For example, you could update a field and handle it as a PUT in your routes with the following HTML:

<form method="POST" action="/update">
    <input type="hidden" name="_method" value="PUT" />
    <input type="text" name="nickname" />
    <input type="submit" />
</form>

The following Scotty application fails to accept overridden PUT requests and instead interprets them as POST. In other words, the middleware fails to have the results of the rewritten request passed to the rest of the application:

{-# LANGUAGE OverloadedStrings #-}

import Prelude hiding (head, id)
import Web.Scotty hiding (body, text)
import Network.Wai.Middleware.RequestLogger  (logStdoutDev)
import Network.Wai.Middleware.MethodOverride (methodOverride)
import Data.Monoid ((<>))

main = scotty 3030 $ middleware methodOverride >> middleware logStdoutDev >> home

home = do
  get "/" $ html $ homepage $ []
  put "/" $ param "nickname" >>= html . homepage

homepage [ n ] = str n
homepage   _   = str ""

title = "Network.Wai.Middleware.MethodOverride Test"

str s = "<html><head><title>" <> title
      <> "</title></head><body><h1>" <> title
      <> "</h1><form method='post' action='/'><input type='hidden' name='_method' value='PUT' />"
      <> "<input type='text' name='nickname' value='" <> s
      <> "' /><input type='submit' value='Submit Test' /></form></body></html>"
@sordina
Copy link
Contributor Author

sordina commented Apr 16, 2012

It appears that this is supported but MethodOverride only handles query parameters, however, while testing it seems that mkEnv doesn't handle encoded parameters from other StdMethod types than POST:

mkEnv :: StdMethod -> Request -> [Param] -> ResourceT IO ActionEnv
mkEnv method req captures = do
    b <- BL.fromChunks <$> lazyConsume (requestBody req)

    let parameters = captures ++ formparams ++ queryparams
        formparams = case (method, lookup "Content-Type" [(CI.mk k, CI.mk v) | (k,v) <- requestHeaders req]) of
                        (POST, Just "application/x-www-form-urlencoded") -> parseEncodedParams $ mconcat $ BL.toChunks b
                        _ -> []
        queryparams = parseEncodedParams $ rawQueryString req

    return $ Env req parameters b

I'm not sure of the best way to generalize this, but people will probably want to be able to send parameters through the usual means. As a test I used a wildcard to capture all form-encoded parameters in mkEnv and this appears to work:

    let parameters = captures ++ formparams ++ queryparams
        formparams = case (method, lookup "Content-Type" [(CI.mk k, CI.mk v) | (k,v) <- requestHeaders req]) of
                        (_, Just "application/x-www-form-urlencoded") -> parseEncodedParams $ mconcat $ BL.toChunks b
                        _ -> []

I have not tested if this breaks existing functionality at this point.

@xich
Copy link
Member

xich commented Apr 16, 2012

Yes, MethodOverride only works with query parameters, so your html would need to be:

<form method="POST" action="/update?_method=PUT">
    <input type="text" name="nickname" />
    <input type="submit" />
</form>

Unfortunately, this is someone else's package, but I'm sure they would accept a patch if you found a clean way to look for "_method" in the form parameters.

As for mkEnv: yes this is very incomplete. There are several ways to encode form data, especially once you get to files and multipart data. I've only implemented the most basic method here because that is all I've needed so far. ;-)

Ideally, I'd rather form data get parsed in middleware, and attached to the request object on the way in. The WAI Request object has a Vault, which is probably where we'd store this. Then mkEnv could just look there.

I might take a crack at this later tonight, depending on how much else I get done today. :-P

@xich xich closed this as completed Apr 16, 2012
@sordina
Copy link
Contributor Author

sordina commented Apr 17, 2012

Thanks Andrew.

I agree that this would be best handled with middleware. It should probably be included in wai-extra, then as you say, mkEnv can just pull the values out of the vault. I've begun adding a middleware to wai-extra that will parse application/x-www-form-urlencoded data similar to how Scotty does, however, I'm not experienced with the other encodings you mentioned.

What should I be looking out for? Are there any existing libraries I can use that you know of?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants