Handling HTTP request in Go Echo framework (2)


Update @ 2019-12-13: Starting from Go 1.13, please use the built-in Go Module to manage dependencies.


This is the second post about handling HTTP request in Echo framework and we will continue to work on the example we made previously.

This time we will create a simple HTML form and submit it as a POST request.

Create a POST request endpoint

Checkout this repo and create the api/post_full_name.go to handle the POST request. This new file is almost the same as api/get_full_name.go except the JSON is prettified before returning to the client.

api/post_full_name.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package api

import (
  "bytes"
  "encoding/json"
  "fmt"
  "net/http"

  "github.com/labstack/echo"

  "gitlab.com/ykyuen/handling-http-request-in-go-echo-example-1/model"
)

func PostFullName(c echo.Context) error {
  // Bind the input data to ExampleRequest
  exampleRequest := new(model.ExampleRequest)
  if err := c.Bind(exampleRequest); err != nil {
    return err
  }

  // Manipulate the input data
  greeting := exampleRequest.FirstName + " " + exampleRequest.LastName

  // Pretty print the json []byte
  var resp bytes.Buffer
  var b = []byte(
    fmt.Sprintf(`{
      "first_name": %q,
      "last_name": %q,
      "msg": "Hello %s"
    }`, exampleRequest.FirstName, exampleRequest.LastName, greeting),
  )
  err := json.Indent(&resp, b, "", "  ")
  if err != nil {
    return err
  }

  // Return the json to the client
  return c.JSONBlob(
    http.StatusOK,
    []byte(
      fmt.Sprintf("%s", resp.Bytes()),
    ),
  )
}

Add the new route in main.go.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
...
func main() {
  // Echo instance
  e := echo.New()

  // Instantiate a template registry with an array of template set
  // Ref: https://gist.github.com/rand99/808e6e9702c00ce64803d94abff65678
  templates := make(map[string]*template.Template)
  templates["home.html"] = template.Must(template.ParseFiles("view/home.html", "view/base.html"))
  templates["about.html"] = template.Must(template.ParseFiles("view/about.html", "view/base.html"))
  e.Renderer = &TemplateRegistry{
    templates: templates,
  }

  // Route => handler
  e.GET("/", handler.HomeHandler)
  e.GET("/about", handler.AboutHandler)

  // Route => api
  e.GET("/api/get-full-name", api.GetFullName)
  e.POST("/api/post-full-name", api.PostFullName)

  // Start the Echo server
  e.Logger.Fatal(e.Start(":1323"))
}

Test the new endpoint using curl

Run the Echo server as send a POST request using curl.

1
curl http://localhost:1323/api/post-full-name -H "Content-Type: application/json" -d '{ "first_name": "Kit", "last_name": "Yuen"  }'

Here is the example result.

A JSON is return based on our POST request payload.

A JSON is return based on our POST request payload.

Add an HTML form

The POST request endpoint is ready and now we could add an HTML form and complete the whole setup. Let’s add the form in the about page.

view/about.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
{{define "title"}}
  Boatswain Blog | {{index . "name"}}
{{end}}

{{define "body"}}
  <h1>{{index . "msg"}}</h1>
  <h2>This is the about page.</h2>

  <!-- HTML form -->
  <form id="post-full-name-form">
    <input type="text" id="first-name" placeholder="First Name">
    <input type="text" id="last-name" placeholder="Last Name">
    <button type="button" id="submit-btn">Submit</button>
  </form>

  <!-- Print the response after the jQuery ajax request -->
  <pre id="form-result"></pre>

  <script type="text/javascript">
    // Send a ajax request when the submit button is clicked
    $("#submit-btn").on("click", function(){
      var firstName = $("#first-name").val();
      var lastName = $("#last-name").val();

      $.ajax({
        type: "POST",
        url: "/api/post-full-name",
        data: JSON.stringify({
          first_name: firstName,
          last_name: lastName
        }),
        processData: false,
        contentType: "application/json",
        dataType: "json"
      }).done(
        function(data){
          $("#form-result").text(JSON.stringify(data, null, 2));
        }
      ).fail(
        function(data){
          $("#form-result").text("POST request failed!");
        }
      )
    });
  </script>
{{end}}

As the form submission required jQuery, let’s include it in the view/base.html.

view/base.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{{define "base.html"}}
  <!DOCTYPE html>
  <html>
    <head>
      <title>{{template "title" .}}</title>
      <!-- jQuery -->
      <script
        src="https://code.jquery.com/jquery-3.3.1.min.js"
        integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
        crossorigin="anonymous"></script>
    </head>
    <body>
      {{template "body" .}}
    </body>
  </html>
{{end}}

Test the HTML form in the about page

Start the Echo server and browse the about page. Fill in the first and last name and then click the submit button. It should show the response JSON as follow.

Click the submit button and get the response.

Click the submit button and get the response.

The project structure

Here is our new project structure with changes highlighted. (The project is renamed to handling-http-request-in-go-echo-example-2, make sure you set the import statement in the Golang source files correctly if you want to change the project name.)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
handling-http-request-in-go-echo-example-2/
  ├── api/                     # folder of api endpoints
  │   ├── get_full_name.go     # api for get full name
  │   ├── post_full_name.go    # api for post full name
  ├── handler/                 # folder of request handlers
  │   ├── home_handler.go      # handler for home page
  │   └── about_handler.go     # handler for about page
  ├── model/                   # folder of custom struct types
  │   ├── example_request.go   # struct type of get_full_name request
  │   └── example_response.go  # hstruct type of get_full_name response
  ├── vendor/                  # dependencies managed by dep
  │   ├── github.com/*
  │   └── golang.org/*
  ├── view/                    # folder of html templates
  │   ├── base.html            # base layout template
  │   ├── home.html            # home page template
  │   └── about.html           # about page template
  ├── Gopkg.lock               # dep config file
  ├── Gopkg.toml               # dep config file
  └── main.go                  # programme entrypoint

Summary

This is the second episode of Handling HTTP request in Go Echo framework (1) and we have added the following:

  • Setup a POST request endpoint in the Echo framework.
  • Prettify the JSON byte array.
  • Add an HTML form and bind it to the new POST request using AJAX in jQuery/javascript.

The complete example could be found on gitlab.com.