首先说一下这种业务的应用场景:
把一个长 url 转换为一个短 url 网址
主要用于微博, 二维码, 等有字数限制的场景
主要实现的功能分析:
把长 url 的地址转换为短 url 地址
通过短 url 获取对应的原始长 url 地址
相同长 url 地址是否需要同样的短 url 地址
这里实现的是一个 api 服务
数据库设计
数据库的设计其实也没有非常复杂, 如图所示:
这里有个设置需要主要就是关于数据库表中 id 的设计, 需要设置为自增的
并且这里有个问题需要提前知道, 我们的思路是根据 id 的值会转换为 62 进制关于进制转换的代码为:
- // 将十进制转换为 62 进制 0-9a-zA-Z 六十二进制
- func transTo62(id int64)string{
- // 1 --> 1
- // 10--> a
- // 61--> Z
- charset := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
- var shortUrl []byte
- for{
- var result byte
- number := id % 62
- result = charset[number]
- var tmp []byte
- tmp = append(tmp,result)
- shortUrl = append(tmp,shortUrl...)
- id = id / 62
- if id == 0{
- break
- }
- }
- fmt.Println(string(shortUrl))
- return string(shortUrl)
- }
所以这里需要设置一下数据库 id 的起始值, 可以设置的大一点, 这样转换为 62 进制之后不至于太短
代码逻辑
项目完整的代码 git 地址: https://github.com/pythonsite/go_simple_code/tree/master/short_url
当然这里的代码还有待后面继续做优化, 但是这里通过 golang 内置的 net/http 库实现了一个简单的 api 功能
代码的目录结构
- |____logic
- | |____logic.go
- |____model
- | |____data.go
- |____api
- | |____api.go
- |____client
- | |____client.go
logic 目录为主要的处理逻辑
model 是定义了 request 和 response 结构体
api 目录为程序的入口程序
client 为测试请求, 进行地址的转换
model 代码为:
- package model
- type Long2ShortRequest struct {
- OriginUrl string `json:"origin_url"`
- }
- type ResponseHeader struct {
- Code int `json:"code"`
- Message string `json:"message"`
- }
- type Long2ShortResponse struct {
- ResponseHeader
- ShortUrl string `json:"short_url"`
- }
- type Short2LongRequest struct {
- ShortUrl string `json:"short_url"`
- }
- type Short2LongResponse struct {
- ResponseHeader
- OriginUrl string `json:"origin_url"`
- }
logic 的代码为:
- package logic
- import(
- "go_dev/11/short_url/model"
- "github.com/jmoiron/sqlx"
- "fmt"
- "crypto/md5"
- "database/sql"
- )
- var (
- Db *sqlx.DB
- )
- type ShortUrl struct {
- Id int64 `db:"id"`
- ShortUrl string `db:"short_url"`
- OriginUrl string `db:"origin_url"`
- HashCode string `db:"hash_code"`
- }
- func InitDb(dsn string)(err error) {
- // 数据库初始化
- Db, err = sqlx.Open("mysql",dsn)
- if err != nil{
- fmt.Println("connect to mysql failed:",err)
- return
- }
- return
- }
- func Long2Short(req *model.Long2ShortRequest) (response *model.Long2ShortResponse, err error) {
- response = &model.Long2ShortResponse{}
- urlMd5 := fmt.Sprintf("%x",md5.Sum([]byte(req.OriginUrl)))
- var short ShortUrl
- err = Db.Get(&short,"select id,short_url,origin_url,hash_code from short_url where hash_code=?",urlMd5)
- if err == sql.ErrNoRows{
- err = nil
- // 数据库中没有记录, 重新生成一个新的短 url
- shortUrl,errRet := generateShortUrl(req,urlMd5)
- if errRet != nil{
- err = errRet
- return
- }
- response.ShortUrl = shortUrl
- return
- }
- if err != nil{
- return
- }
- response.ShortUrl = short.ShortUrl
- return
- }
- func generateShortUrl(req *model.Long2ShortRequest,hashcode string)(shortUrl string,err error){
- result,err := Db.Exec("insert INTO short_url(origin_url,hash_code)VALUES (?,?)",req.OriginUrl,hashcode)
- if err != nil{
- return
- }
- // 0-9a-zA-Z 六十二进制
- insertId,_:= result.LastInsertId()
- shortUrl = transTo62(insertId)
- _,err = Db.Exec("update short_url set short_url=? where id=?",shortUrl,insertId)
- if err != nil{
- fmt.Println(err)
- return
- }
- return
- }
- // 将十进制转换为 62 进制 0-9a-zA-Z 六十二进制
- func transTo62(id int64)string{
- // 1 --> 1
- // 10--> a
- // 61--> Z
- charset := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
- var shortUrl []byte
- for{
- var result byte
- number := id % 62
- result = charset[number]
- var tmp []byte
- tmp = append(tmp,result)
- shortUrl = append(tmp,shortUrl...)
- id = id / 62
- if id == 0{
- break
- }
- }
- fmt.Println(string(shortUrl))
- return string(shortUrl)
- }
- func Short2Long(req *model.Short2LongRequest) (response *model.Short2LongResponse, err error) {
- response = &model.Short2LongResponse{}
- var short ShortUrl
- err = Db.Get(&short,"select id,short_url,origin_url,hash_code from short_url where short_url=?",req.ShortUrl)
- if err == sql.ErrNoRows{
- response.Code = 404
- return
- }
- if err != nil{
- response.Code = 500
- return
- }
- response.OriginUrl = short.OriginUrl
- return
- }
api 的代码为:
- package main
- import (
- "io/ioutil"
- "net/http"
- "fmt"
- "encoding/json"
- "go_dev/11/short_url/logic"
- "go_dev/11/short_url/model"
- _ "github.com/go-sql-driver/mysql"
- )
- const (
- ErrSuccess = 0
- ErrInvalidParameter = 1001
- ErrServerBusy = 1002
- )
- func getMessage(code int) (msg string){
- switch code {
- case ErrSuccess:
- msg = "success"
- case ErrInvalidParameter:
- msg = "invalid parameter"
- case ErrServerBusy:
- msg = "server busy"
- default:
- msg = "unknown error"
- }
- return
- }
- // 用于将返回序列化数据, 失败的返回
- func responseError(w http.ResponseWriter, code int) {
- var response model.ResponseHeader
- response.Code = code
- response.Message = getMessage(code)
- data, err := json.Marshal(response)
- if err != nil {
- w.Write([]byte("{\"code\":500, \"message\": \"server busy\"}"))
- return
- }
- w.Write(data)
- }
- // 用于将返回序列化数据, 成功的返回
- func responseSuccess(w http.ResponseWriter, data interface{}) {
- dataByte, err := json.Marshal(data)
- if err != nil {
- w.Write([]byte("{\"code\":500, \"message\": \"server busy\"}"))
- return
- }
- w.Write(dataByte)
- }
- // 长地址到短地址
- func Long2Short(w http.ResponseWriter, r *http.Request) {
- // 这里需要说明的是发来的数据是通过 post 发过来一个 json 格式的数据
- data, err := ioutil.ReadAll(r.Body)
- if err != nil {
- fmt.Println("read all failded,", err)
- responseError(w, 1001)
- return
- }
- var req model.Long2ShortRequest
- // 将反序列化的数据保存在结构体中
- err = json.Unmarshal(data, &req)
- if err != nil {
- fmt.Println("Unmarshal failded,", err)
- responseError(w, 1002)
- return
- }
- resp, err := logic.Long2Short(&req)
- if err != nil {
- fmt.Println("Long2Short failded,", err)
- responseError(w, 1003)
- return
- }
- responseSuccess(w, resp)
- }
- // 短地址到长地址
- func Short2Long(w http.ResponseWriter, r *http.Request) {
- // 这里需要说明的是发来的数据是通过 post 发过来一个 json 格式的数据
- data, err := ioutil.ReadAll(r.Body)
- if err != nil {
- fmt.Println("read all failded,", err)
- responseError(w, 1001)
- return
- }
- var req model.Short2LongRequest
- // 将反序列化的数据保存在结构体中
- err = json.Unmarshal(data, &req)
- if err != nil {
- fmt.Println("Unmarshal failded,", err)
- responseError(w, 1002)
- return
- }
- resp, err := logic.Short2Long(&req)
- if err != nil {
- fmt.Println("Long2Short failded,", err)
- responseError(w, 1003)
- return
- }
- responseSuccess(w, resp)
- }
- func main(){
- err := logic.InitDb("root:123456@tcp(192.168.50.145:3306)/short_url?parseTime=true")
- if err != nil{
- fmt.Printf("init db failed,err:%v\n",err)
- return
- }
- http.HandleFunc("/trans/long2short", Long2Short)
- http.HandleFunc("/trans/short2long", Short2Long)
- http.ListenAndServe(":18888", nil)
- }
小结
这次通过这个小代码对 go 也有了一个初步的认识和使用, 同时也通过 net/http 包实现了 api 的功能, 也对其基本使用有了大致了解
来源: https://www.cnblogs.com/zhaof/p/8576946.html