【Golang 1.18】短網址產生器 - 極短篇
記得很久很久以前,短網址很流行,也許是因為網址太長的關係吧,當時很想自己做一個,覺得很厲害,後來去年發現一篇文章有解析它的原理,天啊,原來很簡單嘛,讓我們自己來做一個吧…
作業環境
項目 | 版本 |
---|---|
CPU | Apple M1 |
macOS | Big Sur 12.3 arm64 |
Golang | 1.18.2 arm64 |
Visual Studio Code | 1.67 arm64 |
Postman | 9.0.9 arm64 |
DB Browser for SQLite | 3.12.1 x86_64 |
SQLite | 3.31.0 |
原理說明
- 原理其實很單純,就是將URL => 壓縮成一串文字,其中使用MD5(https://zh.wikipedia.org/zh-tw/MD5) / SHA1(https://zh.wikipedia.org/wiki/SHA-1) / SHA256等公開的演算法去求值,也可以是自己寫的演算法…
初始化設定
安裝套件
- 這裡使用的套件如下…
go mod init william // 產生go.mod
go get -u gorm.io/gorm // gorm本體
go get -u gorm.io/driver/sqlite // gorm的sqlite-driver
go get -u github.com/gin-gonic/gin // gin => 打API用
HTML轉址
<html><meta http-equiv='refresh' content='0;url=<URL>'/></html>
功能 | API | 方式 |
---|---|---|
搜尋短轉址後轉址 | http://localhost:8080/tinyUrl/<Code> | GET |
新增短網址對照表 | http://localhost:8080/tinyUrl/ | POST |
package main
import (
"crypto/md5"
"fmt"
"net/http"
"william/utility"
"github.com/gin-gonic/gin"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
const DatabasePath = "./material/sqliteDB.sqlite3" // 把資料庫存放在material資料夾下
// 短網址的長相
type TinyUrl struct {
gorm.Model
Url string `json:"url" gorm:"index:idx_name,unique"`
Code string `json:"code" gorm:"index:idx_name,unique"`
}
func main() {
database, error := createDatabase(DatabasePath)
if error != nil {
return
}
database.AutoMigrate(&TinyUrl{})
initRouter(database)
}
// 建立資料庫
func createDatabase(path string) (*gorm.DB, error) {
database, error := gorm.Open(sqlite.Open(path), &gorm.Config{})
return database, error
}
// 初始化Router相關設定
func initRouter(database *gorm.DB) {
router := gin.Default()
router.MaxMultipartMemory = 8 << 20
registerWebAPI(router, database)
router.Run(":8080")
}
// 註冊API
func registerWebAPI(router *gin.Engine, database *gorm.DB) {
insertTinyUrl(router, database)
selectTinyUrl(router, database)
}
// MARK: - WebAPI
// 新增短網址對照表 => MD5(URL)
func insertTinyUrl(router *gin.Engine, database *gorm.DB) {
var tinyUrl TinyUrl
router.POST("/tinyUrl", func(context *gin.Context) {
dictionary := utility.RequestBodyToMap(context)
result, error := tinyUrl._Insert(database, dictionary)
utility.ContextJSON(context, http.StatusOK, result, error)
})
}
// 搜尋短網址 => 轉址
func selectTinyUrl(router *gin.Engine, database *gorm.DB) {
var tinyUrl TinyUrl
router.GET("/tinyUrl/:code", func(context *gin.Context) {
blog := "https://william-weng.github.io/tags/golang/"
code := context.Param("code")
result := tinyUrl._Select(database, code)
refreshHtml := fmt.Sprintf("<html><meta http-equiv='refresh' content='0;url=%s'/></html>", blog)
defer func() {
context.Data(http.StatusOK, "text/html; charset=utf-8", []byte(refreshHtml))
}()
if result.ID == 0 {
return
}
refreshHtml = fmt.Sprintf("<html><meta http-equiv='refresh' content='0;url=%s'/></html>", result.Url)
})
}
// 新增短網址
func (tinyUrl *TinyUrl) _Insert(database *gorm.DB, dictionary map[string]interface{}) (map[string]interface{}, error) {
isSuccess := false
url := dictionary["url"].(string)
bytes := md5.Sum([]byte(url))
code := fmt.Sprintf("%x", bytes)[:6]
error := database.Create(&TinyUrl{Url: url, Code: code}).Error
if error == nil {
isSuccess = true
}
result := map[string]interface{}{"isSuccess": isSuccess, "code": code}
return result, error
}
// 搜尋短網址
func (_tinyUrl *TinyUrl) _Select(database *gorm.DB, code string) TinyUrl {
var tinyUrl TinyUrl
database.Take(&tinyUrl, "code=?", code)
return tinyUrl
}