Post form data to WebSharper sitelet from HTML/JS

Post form data to WebSharper sitelet from HTML/JS

When building websites, chances are that you will need to gather data from your users.
The most common way to gather data is via a form. But after gathering the data, there are two ways to submit it from the browser to your server:

  1. Using a HTML form
  2. Using an Ajax call

Today we will see the differences and how we can implement it.

1. Submit data using a form

Before hand, I have created a simple server which has a single endpoint accepting a POST.

type EndPoint =
| [<EndPoint "POST /upload">] Upload

let Main =
    Application.MultiPage (fun ctx -> function Upload -> Content.Text "Received")

Next we can create a simple form with one an input and button to submit.

<form method="post" action="/upload">
    <input name="my-data" type="text" />
    <button type="submit">Submit</button>
</form>

The action is the url to post to.
The method is the Http method.

When we press the submit, the form is submitted and we can see from Chrome that the data have been submitted using a POST and that the content type is x-www-form-urlencoded.

Content-Type:application/x-www-form-urlencoded

Form Data
my-data:test

Url encoded means that the content of the form has been encoded in the url and posted to the action specified.
This is the default behavior of a form submit.

But if we need to send a file, we will not be able to url encode the file, we need to send it as form-data.
Let’s start by adding a file input:

<form method="post" action="/upload" enctype="multipart/form-data">
    <input name="my-data" type="text" />
    <input type="file" name="some-file" />
    <button type="submit">Submit</button>
</form>

To tell the browser to submit as form-data we need to specify the encoding type enctype="multipart/form-data".
Now when we submit we can see that the data has been sent as multipart form data.
And the content type also include boundary=XXX which lets the server know the boundary delimiting the data in the body of the request.

Content-Type:multipart/form-data; boundary=----WebKitFormBoundary23lA5KSmXMXrKcOe

Request payload:
------WebKitFormBoundary23lA5KSmXMXrKcOe
Content-Disposition: form-data; name="my-data"

hello world
------WebKitFormBoundary23lA5KSmXMXrKcOe
Content-Disposition: form-data; name="some-file"; filename="Untitled.png"
Content-Type: image/png


------WebKitFormBoundary23lA5KSmXMXrKcOe--

And from the server side, we can see that we receive a file in the context request.

file_request

To prevent double submit (due to the page POST’ing the request being loaded), we can implement the POST/REDIRECT/GET logic (PRG).

In the era of SPAs, lot of websites now do everything via JavaScript and Ajax in particular to submit data. Let’s see how we can achieve that in JS.

2. Submit data using Ajax

Submitting data by Ajax allows to submit a json object which can be easily deserialized on our server.
To do that we can intercept the submission when the button is pressed and gather all data on submit and serialize the content of the form.

<form id="form">
    <input name="data-1" type="text" />
    <input name="data-2" type="text" />
    <button id="ajaxForm">Submit</button>
</form>
(function () {
    $("#ajaxForm").click(function (e) {
        e.preventDefault();
        var json = JSON.stringify($("#form").serializeArray());
        $.ajax({
            type: "post",
            url: "/upload",
            data: json,
            contentType: "application/json",
            success: function () { }
        });
    })
})()

Clicking on the submit button will invoke the click handle and submit data as json:

Content-Type:application/json

[{name: "data-1", value: "test1"}, {name: "data-2", value: "test2"}]

serializeArray is quite clever as it selects input with a name and the value assigned and place it into a serializable key value list.
https://api.jquery.com/serializeArray/

If we need to submit a file here, we will not be able to use Json and same as previously, we will need to use form data.

To do that we can build the form data using FormData and .append().

<form id="form2">
    <input name="data-1" type="text" />
    <input name="data-2" type="text" />
    <input type="file" name="some-file" />
    <button id="ajaxFormFormData">Submit</button>
</form>
$("#ajaxFormFormData").click(function (e) {
    e.preventDefault();

    var form = $("#form2");
    var fd = new FormData();
    $.each(form.serializeArray(), function (index, val) {
        fd.append(val.name, val.value)
    });
    fd.append('file', form.children('input[type=file]')[0].files[0]);

    $.ajax({
        type: "post",
        url: "/upload",
        data: fd,
        contentType: false,
        processData: false,
        success: function () { }
    });
});

Here we append all the data and the files to the form data. Next we pass the form data to the Ajax post and set the processData to false and contentType to false - setting contentType to false will have the multipart content type and boundary automatically set for us.

Now when we submit the form, the content plus the files will be sent as multipart/form-data.

Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryTCQcqMA9mCM3QP5H

------WebKitFormBoundaryTCQcqMA9mCM3QP5H
Content-Disposition: form-data; name="data-1"

test
------WebKitFormBoundaryTCQcqMA9mCM3QP5H
Content-Disposition: form-data; name="data-2"

test
------WebKitFormBoundaryTCQcqMA9mCM3QP5H
Content-Disposition: form-data; name="file"; filename="my_image.png"
Content-Type: image/png

And that’s it, those are the main ways we can submit data from the client to the server!

Conclusion

Today we saw how we could submit data from the client to the server via a Http POST. We saw how we could do it using classic HTML forms then we learnt how we could do it straight from JavaScript with Ajax. We also learnt the different content type and how to use them. I hope this post was helpful! As usual if you have any comments leave it here or hit me on Twitter @Kimserey_Lam. See you next time!

Other posts you will like!

Support me!

Support me by visting my website. Thank you!

Support me by downloading my app BASKEE. Thank you!

baskee

Support me by downloading my app EXPENSE KING. Thank you!

expense king

Support me by downloading my app RECIPE KEEPER. Thank you!

recipe keeper

Comments

Popular posts from this blog

A complete SignalR with ASP Net Core example with WSS, Authentication, Nginx

Verify dotnet SDK and runtime version installed

SDK-Style project and project.assets.json