Table of Contents
Rest API Golang
API URL
https://<host>/<productType>/<productName>/v1
siteID/{siteID}/<resource>/resourceID
Hello World !
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
// 1. http.HandleFunc(pattern, handler)
// 2. http.ListenAndServe(url, handler)
// Register the pattern and handler with default mutiplexer
// define routes
http.HandleFunc("/greet", greet)
err := http.ListenAndServe("localhost:8000", nil)
if err != nil {
log.Fatal(err)
}
}
func greet(writer http.ResponseWriter, request *http.Request) {
fmt.Fprint(writer, "Hello world !")
}
How to run
- Start server in 1st terminal
go run server.go
Verify
curl http://localhost:8000/greet
Hello world !%
Marshalling Data structure to Json Representation
- Use json.NewEncoder to list of customers in response
- e.g. json.NewEncoder(writer).Encode(customers)
Basic code
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
type Customer struct {
Name string
City string
Zipode int
}
func main() {
// 1. http.HandleFunc(pattern, handler)
// 2. http.ListenAndServe(url, handler)
// Register the pattern and handler with default mutiplexer
// define routes
http.HandleFunc("/greet", greet)
// 2nd API
http.HandleFunc("/customers", getAllCustomer)
err := http.ListenAndServe("localhost:8000", nil)
if err != nil {
log.Fatal(err)
}
}
func getAllCustomer(writer http.ResponseWriter, request *http.Request) {
customers := []Customer{
{Name: "Roger", City: "Delhi", Zipode: 110012},
{Name: "Rafa", City: "Mumbai", Zipode: 410100},
}
// But here response is set as Content Type : text/plain; charset=utf-8
// And not JSON
json.NewEncoder(writer).Encode(customers)
}
func greet(writer http.ResponseWriter, request *http.Request) {
fmt.Fprint(writer, "Hello world !")
}
Response
Wrong Content Type
We observe context type as text, but is should be json.
Correct JSON Content Type
// Add new key to header for setting correct content type
writer.Header().Add("Content-Type", "application/json")
Updated Code
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
type Customer struct {
Name string
City string
Zipode int
}
func main() {
// 1. http.HandleFunc(pattern, handler)
// 2. http.ListenAndServe(url, handler)
// Register the pattern and handler with default mutiplexer
// define routes
http.HandleFunc("/greet", greet)
// 2nd API
http.HandleFunc("/customers", getAllCustomer)
err := http.ListenAndServe("localhost:8000", nil)
if err != nil {
log.Fatal(err)
}
}
func getAllCustomer(writer http.ResponseWriter, request *http.Request) {
customers := []Customer{
{Name: "Roger", City: "Delhi", Zipode: 110012},
{Name: "Rafa", City: "Mumbai", Zipode: 410100},
}
// But here response is set as Content Type : text/plain; charset=utf-8
// And not JSON
// Add new key to header for setting correct content type
writer.Header().Add("Content-Type", "application/json")
json.NewEncoder(writer).Encode(customers)
}
func greet(writer http.ResponseWriter, request *http.Request) {
fmt.Fprint(writer, "Hello world !")
}
Corrected Response
[
{
"Name": "Roger",
"City": "Delhi",
"Zipode": 110012
},
{
"Name": "Rafa",
"City": "Mumbai",
"Zipode": 410100
}
]
With Updated json tags
type Customer struct {
Name string `json:"full_name"`
City string `json:"city"`
Zipode int `json:"zip_code"`
}
********************************
Output
********************************
[
{
"full_name": "Roger",
"city": "Delhi",
"zip_code": 110012
},
{
"full_name": "Rafa",
"city": "Mumbai",
"zip_code": 410100
}
]
XML encoding
- Add xml tags
- Use xml.NewEncoder
package main
import (
"encoding/xml"
"fmt"
"log"
"net/http"
)
type Customer struct {
Name string `json:"full_name" xml:"name"`
City string `json:"city" xml:"city"`
Zipode int `json:"zip_code" xml:"zipcode"`
}
func main() {
// 1. http.HandleFunc(pattern, handler)
// 2. http.ListenAndServe(url, handler)
// Register the pattern and handler with default mutiplexer
// define routes
http.HandleFunc("/greet", greet)
// 2nd API
http.HandleFunc("/customers", getAllCustomer)
err := http.ListenAndServe("localhost:8000", nil)
if err != nil {
log.Fatal(err)
}
}
func getAllCustomer(writer http.ResponseWriter, request *http.Request) {
customers := []Customer{
{Name: "Roger", City: "Delhi", Zipode: 110012},
{Name: "Rafa", City: "Mumbai", Zipode: 410100},
}
// But here response is set as Content Type : text/plain; charset=utf-8
// And not JSON
// Add new key to header for setting correct content type
// writer.Header().Add("Content-Type", "application/json")
// json.NewEncoder(writer).Encode(customers)
// Add new key to header for setting correct content type
writer.Header().Add("Content-Type", "application/xml")
xml.NewEncoder(writer).Encode(customers)
}
func greet(writer http.ResponseWriter, request *http.Request) {
fmt.Fprint(writer, "Hello world !")
}
Output
***********************
Body Response
***********************
<Customer>
<name>Roger</name>
<city>Delhi</city>
<zipcode>110012</zipcode>
</Customer>
<Customer>
<name>Rafa</name>
<city>Mumbai</city>
<zipcode>410100</zipcode>
</Customer>
***********************
Content-Type : application/xml
Client Side Request Header
package main
import (
"encoding/json"
"encoding/xml"
"fmt"
"log"
"net/http"
)
type Customer struct {
Name string `json:"full_name"`
City string `json:"city"`
Zipode int `json:"zip_code"`
}
func main() {
// 1. http.HandleFunc(pattern, handler)
// 2. http.ListenAndServe(url, handler)
// Register the pattern and handler with default mutiplexer
// define routes
http.HandleFunc("/greet", greet)
// 2nd API
http.HandleFunc("/customers", getAllCustomer)
err := http.ListenAndServe("localhost:8000", nil)
if err != nil {
log.Fatal(err)
}
}
func getAllCustomer(writer http.ResponseWriter, request *http.Request) {
customers := []Customer{
{Name: "Roger", City: "Delhi", Zipode: 110012},
{Name: "Rafa", City: "Mumbai", Zipode: 410100},
}
// But here response is set as Content Type : text/plain; charset=utf-8
// And not JSON
// Add new key to header for setting correct content type
// writer.Header().Add("Content-Type", "application/json")
// json.NewEncoder(writer).Encode(customers)
if request.Header.Get("Content-Type") == "application/xml" {
writer.Header().Add("Content-Type", "application/xml")
xml.NewEncoder(writer).Encode(customers)
} else {
writer.Header().Add("Content-Type", "application/json")
json.NewEncoder(writer).Encode(customers)
}
}
func greet(writer http.ResponseWriter, request *http.Request) {
fmt.Fprint(writer, "Hello world !")
}
Output
<Customer>
<Name>Roger</Name>
<City>Delhi</City>
<Zipode>110012</Zipode>
</Customer>
<Customer>
<Name>Rafa</Name>
<City>Mumbai</City>
<Zipode>410100</Zipode>
</Customer>
Go Modules
- Create new package of app which will contain pattern and all handlers
- Create a separate function Start which will contain code for starting server.
New folder Structure will looks like this
restapi/
- Readme.txt
- go.mod
- server.go
app/
- app.go
- handlers.go
Commands used
go mod init <name of module>
e.g. go mod init restapi
go build
go run server.go
go.mod file
module restapi
go 1.21.13
server.go file
package main
import (
"restapi/app"
)
// created custom multiplexer
func main() {
app.Start()
}
app/app.go file
package app
import (
"log"
"net/http"
)
// created custom multiplexer
func Start() {
mux := http.NewServeMux()
mux.HandleFunc("/greet", greet)
mux.HandleFunc("/customers", getAllCustomer)
err := http.ListenAndServe("localhost:8000", mux)
if err != nil {
log.Fatal(err)
}
}
app/handlers.go file
package app
import (
"encoding/json"
"encoding/xml"
"fmt"
"net/http"
)
type Customer struct {
Name string `json:"full_name"`
City string `json:"city"`
Zipode int `json:"zip_code"`
}
func getAllCustomer(writer http.ResponseWriter, request *http.Request) {
customers := []Customer{
{Name: "Roger", City: "Delhi", Zipode: 110012},
{Name: "Rafa", City: "Mumbai", Zipode: 410100},
}
if request.Header.Get("Content-Type") == "application/xml" {
writer.Header().Add("Content-Type", "application/xml")
xml.NewEncoder(writer).Encode(customers)
} else {
writer.Header().Add("Content-Type", "application/json")
json.NewEncoder(writer).Encode(customers)
}
}
func greet(writer http.ResponseWriter, request *http.Request) {
fmt.Fprint(writer, "Hello world !")
}
gorilla/mux
Here, we will cover below topics
- Use of github.com/gorilla/mux
- Get values from request using mux.Vars(request)
- check to accept int only using regex
- /customers/{customer_id:[0-9]+}
- Method Matcher
- e.g. http.MethodGet, MethodPost for particular handlers
router.HandleFunc("/customers/{customer_id:[0-9]+}", getCustomer).Methods(http.MethodGet)
router.HandleFunc("/customers", createCustomer).Methods(http.MethodPost)
// File : app/app.go
package app
import (
"log"
"net/http"
"github.com/gorilla/mux"
)
// method matcher
func Start() {
// mux := http.NewServeMux()
router := mux.NewRouter()
router.HandleFunc("/greet", greet).Methods(http.MethodGet)
router.HandleFunc("/customers", getAllCustomer).Methods(http.MethodGet)
router.HandleFunc("/customers/{customer_id:[0-9]+}", getCustomer).Methods(http.MethodGet)
router.HandleFunc("/customers", createCustomer).Methods(http.MethodPost)
err := http.ListenAndServe("localhost:8000", router)
if err != nil {
log.Fatal(err)
}
}
- Getting values from request URL
- using mux.Vars(request)
- Post api handling
func createCustomer(writer http.ResponseWriter, request *http.Request) {
fmt.Fprint(writer, "Post request received.")
}
func getCustomer(writer http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
fmt.Fprint(writer, vars["customer_id"])
}
// File : app/handlers.go
package app
import (
"encoding/json"
"encoding/xml"
"fmt"
"net/http"
"github.com/gorilla/mux"
)
type Customer struct {
Name string `json:"full_name"`
City string `json:"city"`
Zipode int `json:"zip_code"`
}
func createCustomer(writer http.ResponseWriter, request *http.Request) {
fmt.Fprint(writer, "Post request received.")
}
func getCustomer(writer http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
fmt.Fprint(writer, vars["customer_id"])
}
func getAllCustomer(writer http.ResponseWriter, request *http.Request) {
customers := []Customer{
{Name: "Roger", City: "Delhi", Zipode: 110012},
{Name: "Rafa", City: "Mumbai", Zipode: 410100},
}
// But here response is set as Content Type : text/plain; charset=utf-8
// And not JSON
// Add new key to header for setting correct content type
// writer.Header().Add("Content-Type", "application/json")
// json.NewEncoder(writer).Encode(customers)
if request.Header.Get("Content-Type") == "application/xml" {
writer.Header().Add("Content-Type", "application/xml")
xml.NewEncoder(writer).Encode(customers)
} else {
writer.Header().Add("Content-Type", "application/json")
json.NewEncoder(writer).Encode(customers)
}
}
func greet(writer http.ResponseWriter, request *http.Request) {
fmt.Fprint(writer, "Hello world !")
}
Assignment
Question
- Build the time API based on the instructions and the requirements and handle errors appropriately
- SomeTime Inc wants to build an API that returns the current time.
Pro Tip: Focus on one task at a time
Below are the requirements
- The endpoint should be exposed as
/api/time
- The output should be in JSON
{
"current_time": "2021-08-09 11:18:06 +0000 UTC"
}
- A user can also request to return the current time in another timezone.
- A list of TZ database names are available at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
/api/time?tz=America/New_York
- If the tz parameter is not provided in the URL, it should return the time in UTC
- In case of an invalid TZ database, the API should return the error message “invalid timezone” along with the status code 404
- Bonus: A user can request time in multiple timezones i.e.
/api/time?tz=America/New_York,Asia/Kolkata
API Response:
{
"Asia/Kolkata": "2021-08-09 01:23:42 +0530 IST",
"America/New_York": "2021-08-09 01:23:42 -0400 EDT"
}
The below program will help you with the timezone conversion
package main
import (
"fmt"
"time"
)
func main() {
loc, _ := time.LoadLocation("Asia/Shanghai")
fmt.Println(time.Now().In(loc))
loc, _ := time.LoadLocation("America/New_York")
fmt.Println(time.Now().In(loc))
}
Please visit https: https://codeandalgo.com for more such contents.
Leave a Reply