formspostf#websharper

`Page not found (404)` in POST endpoint with JSON data


I'm trying to use a POST endpoint that accepts JSON data from a HTML form, but only get 404 - Page not found as a result, unless I add another endpoint without the payload.

So I can create a demo project from template and make small changes to test this issue:

cd /tmp
dotnet new websharper-web --name Demo --language F#
cd Demo
dotnet run   ## Ok!

Edit main.html to add the form:

<div class="container">
    ...
</div>
<form method="post" action="/demo" enctype="application/x-www-form-urlencoded">
    <label>Name</label>
    <input type="text" name="name" />
    <button type="submit">Post</button> 
</form>
<footer class="footer">
...
</footer>

Add to Site.fs the code to handle the form request:

type Data = { name: string }

type EndPoint =
    | [<EndPoint "GET /">] Home
    | ...
    | [<EndPoint "POST /demo"; Json "data">] Demo of data: Data
    | [<EndPoint "POST /demo">] DemoFallback

[<Website>]
    let Main =
        Application.MultiPage (fun ctx endpoint ->
            match endpoint with
            | ...
            | EndPoint.Demo data ->
                printfn "Data: %A" data
                Content.Text "Ok demo with post data"
            | EndPoint.DemoFallback ->
                Content.Text "I don't expect this route"
        )

Run the project again:

dotnet run

And from the browser I get I don't expect this route, but from curl:

curl -i -H "Content-Type: application/json" \
  -XPOST "http://localhost:5000/demo" \
  -d '{ "name": "me" }'

or with different content-type:

curl -i -H "Content-Type: application/x-www-form-urlencoded" \
  -XPOST "http://localhost:5000/demo" \
  -d '{ "name": "me" }'

both yield:

HTTP/1.1 200 OK
Date: Tue, 29 Jan 2019 12:57:19 GMT
Server: Kestrel
Transfer-Encoding: chunked

Ok demo with post data

What am I missing here?


Solution

  • An HTML form does not send the information in Json format instead it uses url-encoded which is similar to the query parameters in a url except that they go in the body of the request and not the URL, for instance if you have 2 fields User & Password:

    User=John&Password=123456
    

    In order to receive the data from the form you need to use the [< FormData >] attribute:

    type Data = { [< FormData >] name: string }
    

    and indicate the EndPoint lile this:

     | [< EndPoint "POST /demo" >]  Demo of data: Data