Article directory
- What is a RESTful API
- Go popular web framework-Gin
- Go Hello World
- Gin routers and controllers
- Gin handles request parameters
- Generate HTTP request response
- Gin learning content
- Practical use of Gin framework to develop RESTful API
- OAuth 2.0 interface understanding
- Develop OAuth2.0 interface example with Go
There is a rule in programming – Don’t Repeat Yourself (don’t repeat your code). The core concept of this guideline is: if there is some repeated code, this code should be extracted and encapsulated into a method.
Over time, there are a number of methods that can be integrated into tool classes. If the tool classes form a large scale, they can be integrated into class libraries. The class library is more systematic and has more complete functions. Not only should you not re-create the existing “wheels” in the project, but you should also not reinvent the “wheels” that others have already built. Just use the existing “wheels”.
What is RESTful API
- Resource overview
- Resources can be singletons or collections
- Resources can also contain sub-collection resources
- REST APIs use Uniform Resource Identifiers (URIs) to locate resources
- Use nouns to refer to resources
- document
- gather
- storage
- controller
- maintain consistency
- Use forward slashes (\) to indicate hierarchical relationships
- Do not use a forward slash at the end of a URI
- Use hyphens (-) to make URIs more readable
- Do not use underscore (_)
- Use lowercase letters in URIs
- Do not use file extensions
- Never use the name of a CRUD function in a URI
- Filter a collection of URIs using query parameters
Go popular web framework-Gin
Go HelloWorld
package main import ( "github.com/gin-gonic/gin" ) func main() {<!-- --> //Create a default routing engine r := gin.Default() // GET: request method; /hello: requested path // When the client requests the /hello path with the GET method, the following anonymous function will be executed. r.GET("/hello", func(c *gin.Context) {<!-- --> // c.JSON: Returns data in JSON format c.JSON(200, gin.H{<!-- --> "message": "Hello world!", }) }) // Start the HTTP service. The service is started at 0.0.0.0:8080 by default. r.Run() }
Gin Routing and Controller
- routing rules
- HTTP request method
- GET
- POST
- PUT
- DELETE
- URL path
- Static URL path
- URL parameters with path
- URL paths with asterisk (*) fuzzy matching parameters
- Processor function
- HTTP request method
- packet routing
Gin handles request parameters
- Get GET request parameters
- Get POST request parameters
- Get URL path parameters
- Bind request parameters to structure
Generate HTTP request response
- Generate HTTP request response as a string
- Generate HTTP request response in JSON format
- Generate HTTP request responses in XML format
- Generate HTTP request responses in file format
- Set HTTP response headers
Gin learning content
- Gin renders HTML templates
- Gin handles static resources
- Gin handles cookies
- Gin file upload
- Gin middleware
- Gin Session
Practical use of Gin framework to develop RESTful API
mysql> CREATE TABLE `users` ( -> `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, -> `phone` VARCHAR(255) DEFAULT NULL, -> `name` VARCHAR(255) DEFAULT NULL, -> `password` VARCHAR(255) DEFAULT NULL, -> PRIMARY KEY (`id`) -> ) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8;
package main import ( "crypto/sha256" "fmt" "github.com/gin-gonic/gin" "gorm.io/driver/mysql" "gorm.io/gorm" "net/http" ) type ( User struct {<!-- --> ID uint `json:"id" gorm:"column:id"` Phone string `json:"phone" gorm:"column:phone"` Name string `json:"name" gorm:"column:name"` Password string `json:"password" gorm:"column:password"` } UserRes struct {<!-- --> ID uint `json:"id"` Phone string `json:"phone"` Name string `json:"name"` } ) var db *gorm.DB func main() {<!-- --> // Connect to the database var err error dsn := "root:mm..1213@tcp(127.0.0.1:3306)/UserManager?charset=utf8mb4 & amp;parseTime=True & amp;loc=Local" db, err = gorm.Open(mysql.New(mysql.Config{<!-- --> DriverName: "mysql", DSN: dsn, }), & amp;gorm.Config{<!-- -->}) if err != nil {<!-- --> panic("Failed to connect to database") } // Auto migrate the User struct to create the corresponding table in the database err = db.AutoMigrate( & amp;User{<!-- -->}) if err != nil {<!-- --> panic("Failed to migrate the database") } router := gin.Default() v2 := router.Group("/api/v2/user") {<!-- --> v2.POST("/", createUser) v2.GET("/", fetchAllUser) v2.GET("/:id", fetchUser) v2.PUT("/:id", updateUser) v2.DELETE("/:id", deleteUser) } router.Run(":8080") } func createUser(c *gin.Context) {<!-- --> phone := c.PostForm("phone") name := c.PostForm("name") user := User{<!-- --> Phone: phone, Name: name, Password: md5Password(phone), } tx := db.Begin() if err := tx.Create( & amp;user).Error; err != nil {<!-- --> tx.Rollback() c.JSON(http.StatusInternalServerError, gin.H{<!-- --> "error": err.Error(), }) return } tx.Commit() c.JSON(http.StatusCreated, gin.H{<!-- --> "status": http.StatusCreated, "message": "User created successfully!", "ID": user.ID, }) } func md5Password(password string) string {<!-- --> hash := sha256.Sum256([]byte(password)) return fmt.Sprintf("%x", hash) } func fetchAllUser(c *gin.Context) {<!-- --> varuser[]User var_userRes[]UserRes db.Find(&user) if len(user) <= 0 {<!-- --> c.JSON( http.StatusNotFound, gin.H{<!-- --> "status": http.StatusNotFound, "message": "No user found!", }) return } for _, item := range user {<!-- --> _userRes = append(_userRes, UserRes{<!-- --> ID: item.ID, Phone: item.Phone, Name: item.Name, }) } c.JSON(http.StatusOK, gin.H{<!-- --> "status": http.StatusOK, "data": _userRes, }) } func fetchUser(c *gin.Context) {<!-- --> var userUser ID := c.Param("id") db.First( & amp;user, ID) if user.ID == 0 {<!-- --> c.JSON(http.StatusNotFound, gin.H{<!-- -->"status": http.StatusNotFound, "message": "No user found!"}) return } res := UserRes{<!-- --> ID: user.ID, Phone: user.Phone, Name: user.Name, } c.JSON(http.StatusOK, gin.H{<!-- -->"status": http.StatusOK, "data": res}) } func updateUser(c *gin.Context) {<!-- --> var userUser userID := c.Param("id") db.First(&user, userID) if user.ID == 0 {<!-- --> c.JSON(http.StatusNotFound, gin.H{<!-- -->"status": http.StatusNotFound, "message": "No user found!"}) return } db.Model( & amp;user).Update("phone", c.PostForm("phone")) db.Model( & amp;user).Update("name", c.PostForm("name")) c.JSON(http.StatusOK, gin.H{<!-- --> "status": http.StatusOK, "message": "Updated User Successfully!", }) } func deleteUser(c *gin.Context) {<!-- --> var userUser userID := c.Param("id") db.First(&user, userID) if user.ID == 0 {<!-- --> c.JSON(http.StatusNotFound, gin.H{<!-- --> "status": http.StatusNotFound, "message": "No user found!", }) return } db.Delete(&user) c.JSON(http.StatusOK, gin.H{<!-- -->"status": http.StatusOK, "message": "User deleted successfully!"}) }
- GoLand Tools-Http Client Test
DELETE http://127.0.0.1:8080/api/v2/user/58 Content-Type: application/x-www-form-urlencoded phone=10086 &name=chYiDong
Understanding the OAuth 2.0 interface
Develop OAuth2.0 interface example with Go
- After understanding it, the possibility of using it is relatively small.
- GitHub OAuth application registration
- Registration page: https://github.com/settings/applications/new
- Login authorization page
<!DOCTYPE HTML> <html> <body> <a href="https://github.com/login/oauth/authorize?client_id=5bcf804cfeb0ef7120f5 & amp;redirect_uri=http://localhost:8087/oauth/redirect"> Login by GitHub </a> </body> </html>
- Welcome Screen
<!DOCTYPE HTML> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, INItial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Hello</title> </head> <body> </body> <script> //Get url parameters function getQueryVariable(variable) {<!-- --> var query = window.location.search.substring(1); var vars = query.split(" & amp;"); for (var i = 0; i < vars.length; i + + ) {<!-- --> var pair = vars[i].split("="); if (pair[0] == variable) {<!-- --> return pair[1]; } } return (false); } // Get access_token const token = getQueryVariable("access_token"); // Call user information interface fetch('https://api.github.com/user', {<!-- --> headers: {<!-- --> Authorization: 'token ' + token } }) // Parse the requested JSON .then(res => res.json()) .then(res => {<!-- --> //Return user information const nameNode = document.createTextNode(`Hi, ${<!-- -->res.name}, Welcome to login our site by GitHub!`) document.body.appendChild(nameNode) }) </script> </html>
- Written in Go language
package main import ( "encoding/json" "fmt" "html/template" "net/http" "os" ) // const clientID = "<your client id>" const clientID = "5bcf804cfeb0ef7120f5" // const clientSecret = "<your client secret>" const clientSecret = "8d31102da18096d13eb6ec819cd81ca898ed7189" func hello(w http.ResponseWriter, r *http.Request) {<!-- --> if r.Method == "GET" {<!-- --> t, _ := template.ParseFiles("hello.html") t.Execute(w, nil) } } func login(w http.ResponseWriter, r *http.Request) {<!-- --> if r.Method == "GET" {<!-- --> t, _ := template.ParseFiles("login.html") t.Execute(w, nil) } } func main() {<!-- --> http.HandleFunc("/login", login) http.HandleFunc("/", hello) http.HandleFunc("/hello", hello) httpClient := http.Client{<!-- -->} http.HandleFunc("/oauth/redirect", func(w http.ResponseWriter, r *http.Request) {<!-- --> err := r.ParseForm() if err != nil {<!-- --> fmt.Fprintf(os.Stdout, "could not parse query: %v", err) w.WriteHeader(http.StatusBadRequest) } code := r.FormValue("code") reqURL := fmt.Sprintf("https://github.com/login/oauth/access_token?" + "client_id=%s & amp;client_secret=%s & amp;code=%s", clientID, clientSecret, code) req, err := http.NewRequest(http.MethodPost, reqURL, nil) if err != nil {<!-- --> fmt.Fprintf(os.Stdout, "could not create HTTP request: %v", err) w.WriteHeader(http.StatusBadRequest) } req.Header.Set("accept", "application/json") res, err := httpClient.Do(req) if err != nil {<!-- --> fmt.Fprintf(os.Stdout, "could not send HTTP request: %v", err) w.WriteHeader(http.StatusInternalServerError) } defer res.Body.Close() var tAccessTokenResponse if err := json.NewDecoder(res.Body).Decode( & amp;t); err != nil {<!-- --> fmt.Fprintf(os.Stdout, "could not parse JSON response: %v", err) w.WriteHeader(http.StatusBadRequest) } w.Header().Set("Location", "/hello.html?access_token=" + t.AccessToken) w.WriteHeader(http.StatusFound) }) http.ListenAndServe(":8087", nil) } type AccessTokenResponse struct {<!-- --> AccessToken string `json:"access_token"` }