• golang restful api


    https://medium.com/@petrousov/how-to-build-a-restful-api-in-go-for-phonebook-app-d55f7234a10

    ----------------------------------------

    How to build a RESTful API in Go for phonebook app

    TL;DR

    In this tutorial I am going to show you how I created a RESTful API for a hypothetical phonebook application and how you can create your own APIs based on this example. All the code is stored on github.

    Disclaimer

    • This is a project to learn Go myself
    • The storage of data (database) and file structure is out of the scope of this tutorial and was not implemented

    Phonebook API

    A phonebook application stores records of peoples contact information.

    The models

    In our context, a person’s record includes the first and last names and contact details such as the city, the zipcode and the phone number. To model this in Go we are going to write the following structs and create a slice where we are going to store our records.

    package main

    import (
    "encoding/json"
    )
    type Person struct {
    ID string `json:"id,omitempty"`
    Firstname string `json:"firstname,omitempty"`
    Lastname string `json:"lastname,omitempty"`
    Contactinfo `json:"contactinfo,omitempty"`
    }
    type Contactinfo struct {
    City string `json:"city,omitempty"`
    Zipcode string `json:"Zipcode,omitempty"`
    Phone string `json:"phone,omitempty"`
    }
    var people []Person

    There are a couple of things worth mentioning about the above snippet. The fields of the structs need to begin with an uppercase letter so they can be exported. This is necessary because the JSON library we are going to use to encode/decode our data can only access exported values. See https://blog.golang.org/json-and-go for more details.

    The other thing worth mentioning is the parameter next to our field types enclosed in backquotes. This is a special parameter which specifies the name of the key our fields are going to have in the JSON format. For example, the value of the field Firstname in our struct will be referenced with the key firstname in the JSON format. For more information, checkout the Marshal() function from the official documentation https://golang.org/pkg/encoding/json/#Marshal.

    The handlers

    Our backend needs to be able to perform the following 5 operations on our records.

    1. retrieve the records of all the people
    2. retrieve the record of a specific person
    3. create a new person record in the catalog
    4. update a person’s record information
    5. delete a person’s record from the catalog

    We are going to analyze the function used to update a person’s information (4) since the rest follow a similar implementation. Given the updated record of a person, this handler will look for this person in our slice and if it finds a matching id, will update the record.

    func UpdatePersonEndpoint(w http.ResponseWriter, r *http.Request) {
    var person Person
    _ = json.NewDecoder(r.Body).Decode(&person)
    params := mux.Vars(r)
    for i, p := range people {
    if p.ID == params["id"] {
    people[i] = person
    json.NewEncoder(w).Encode(person)
    break
    }
    }
    }

    The first thing we must notice is the that this function’s name starts with an uppercase letter which means it’s exported. This is not necessary for our example since we store everything in one main.go file. However, if we had a separate file or package called handlers, we would need to be able to call those handlers from a different namespace (main). This is only possible if we have them exported.

    All of our functions/handlers accept the same 2 parameters (wr). These parameters represent data streams which our handlers use to retrieve information from (r) and send information to (w). Consider them as the STDIO (keyboard and monitor) of our backend. It’s not necessary to know the implementation of these interfaces, but if you are curious, check out the official documentation https://golang.org/pkg/net/http/#ResponseWriter

    In order to implement communication through these data streams, we use two assistive functions, json.NewDecoder() and json.NewEncoder(). These functions allow us to send and receive our data.

    The first function is associated with the data stream we use to read from (r) and returns a decoder element. Using the Decode() function on this element, we retrieve the information from the body of a HTTP request and store it in the person variable we created. This information is in JSON, which is human readable format, so we “decode” it into our struct which is readable by our server. A struct variable is a “pass by value” element, so we need pass the address of the variable person to the Decode() function so it can store the values in it.

    The second function is associated with the stream we use to write information to (w) and returns an encoder element. Using the Encode() function on this element, we respond to a HTTP request. So, we transform our person variable into JSON and send it back to the responder.

    If needed, checkout the docs for more information on the above functions https://golang.org/pkg/encoding/json/#NewDecoder

    The last thing to mention about the update handler is that it identifies the record to update by it’s id which is passed as a parameter through the URL when we make the HTTP request. We extract all the variables from the URL using the mux.Vars() function, which returns a map, and reference them using their keys.

     

    The rest of the handlers use the same components to implement our API’s functionality.

    func GetPeopleEndpoint(w http.ResponseWriter, r *http.Request) {
    json.NewEncoder(w).Encode(people)
    }
    func GetPersonEndpoint(w http.ResponseWriter, r *http.Request) {
    params := mux.Vars(r)
    for _, p := range people {
    if p.ID == params["id"] {
    json.NewEncoder(w).Encode(p)
    return
    }
    }
    json.NewEncoder(w).Encode("Person not found")
    }
    func CreatePersonEndpoint(w http.ResponseWriter, r *http.Request) {
    var person Person
    _ = json.NewDecoder(r.Body).Decode(&person)
    people = append(people, person)
    json.NewEncoder(w).Encode(person)
    }
    func DeletePersonEndpoint(w http.ResponseWriter, r *http.Request) {
    params := mux.Vars(r)
    for i, p := range people {
    if p.ID == params["id"] {
    copy(people[i:], people[i+1:])
    people = people[:len(people)-1]
    break
    }
    }
    json.NewEncoder(w).Encode(people)
    }

    The router

    We now have our models and handlers which are able to receive and respond to HTTP requests and convert the data from JSON into our models and back. The next thing we need to implement is the mapping which shows the correspondence of a URL and HTTP request type to our handlers.

    1. /people (GET) -> GetPeopleEndpoint()
    2. /people/{id} (GET) -> GetPersonEndpoint()
    3. /people (POST) -> CreatePersonEndpoint()
    4. /people/{id} (PUT) -> UpdatePersonEndpoint()
    5. /people/{id} (DELETE) -> DeletePersonEndpoint()

    This mapping shows that an HTTP GET call to the /people URL will execute the GetPeopleEndpoint() handler. Another HTTP PUT call to /people/{id} will execute the UpdatePersonEndpoint() handler so on and so forth.

    For the implementation of the router, we are going to use the gorilla/mux package and write the following code.

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

    "github.com/gorilla/mux"
    )
    func main() {
    router := mux.NewRouter()
    router.HandleFunc("/people", GetPeopleEndpoint).Methods("GET")
    router.HandleFunc("/people/{id}", GetPersonEndpoint).Methods("GET")
    router.HandleFunc("/people", CreatePersonEndpoint).Methods("POST")
    router.HandleFunc("/people/{id}", DeletePersonEndpoint).Methods("DELETE")
    router.HandleFunc("/people/{id}", UpdatePersonEndpoint).Methods("PUT")
    }

    The logic is pretty straightforward, we initially create a new router instance. Then, we proceed to map our URL endpoints to the handlers we wrote earlier. As we can see, our handlers now have also the HTTP method they require in order to be called defined with the Methods() function.

    All these functions are provided by the mux package and its documentation can be found online http://www.gorillatoolkit.org/pkg/mux

    Populating with dummy data

    For the sake of simplicity we are not going to use a database to store our data. Instead, everything will be stored locally in our slice named people. So, in order to populate our API with some dummy data, we are going to create a couple of entries.

    people = append(people, Person{ID: "1", Firstname: "Bruce", Lastname: "Wayne", Contactinfo: Contactinfo{City: "Gotham", Zipcode: "735", Phone: "012345678"}})
    people = append(people, Person{ID: "2", Firstname: "Clark", Lastname: "Kent", Contactinfo: Contactinfo{City: "Metropolis", Zipcode: "62960", Phone: "9876543210"}})
    }

    The server

    The last thing left to complete our API is to make it accessible from the network, in other words serve it. To accomplish this, we are going to use the ListenAndServe() function from the http package which starts a HTTP server.

    package main

    import (
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/mux"
    )
    func main() {
    fmt.Println("Starting server on port 8000...")
    log.Fatal(http.ListenAndServe(":8000", router))
    }

    Our server is going to be listening on port 8000. The last line wraps the server function in a log function which will print an error message and return a non-zero code (1) if something goes wrong. The documentation for it can be found online https://golang.org/pkg/log/#Fatal

    Testing

    A working version of our API is available online from github. Let’s fire up our server by running the go run command.

    go run main.go
    Starting server on port 8000...

    For our tests, we are going to use Postman and fire up all the HTTP requests to confirm the functionality of our handlers.

    1. Retrieve the records of all the people (GET)
     

    2. Retrieve the record of a specific person (GET)

     

    3. Create a new person record in the catalog (POST)

     

    4. Update a person’s record information (PUT)

     

    5. Delete a person’s record from the catalog (DELETE)

     

    Delete a person’s record using it’s id

    Conclusion

    In this post I showed you how you can build a simple API in Go which can respond to HTTP requests. Following along you should be able to modify the phonebook API to serve your purpose and follow the documentation if necessary to clear some clouds.

    References

  • 相关阅读:
    0180 定时器 之 setInterval() :开启定时器,京东倒计时案例,停止定时器,发送短信倒计时案例
    0179 定时器 之 setTimeout() :开启定时器,5秒后关闭广告案例,停止定时器
    json常用的注解
    Base64 编码与解码详解
    API 交互中怎么做好图片验证码?
    CSS中cursor 鼠标指针光标样式(形状)
    本地数据存储解决方案以及cookie的坑
    base64原理浅析
    Web前端十种常用的技术
    FreeMarker网页静态化
  • 原文地址:https://www.cnblogs.com/oxspirt/p/10863154.html
Copyright © 2020-2023  润新知