最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Golang實現(xiàn)短網(wǎng)址/短鏈服務(wù)的開發(fā)筆記分享

 更新時間:2023年05月29日 09:26:11   作者:uccs  
這篇文章主要為大家詳細(xì)介紹了如何使用Golang實現(xiàn)短網(wǎng)址/短鏈服務(wù),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價值,感興趣的小伙伴可以了解一下

項目地址:https://github.com/astak16/shortlink

錯誤處理

在處理業(yè)務(wù)邏輯時,如果出錯誤了,需要統(tǒng)一處理錯誤響應(yīng)的格式,這樣可以方便前端處理錯誤信息

所以需要定義一個 Error 接口,它包含了 error 接口,以及一個 Status() 方法,用來返回錯誤的狀態(tài)碼

type Error interface {
  error
  Status() int
}

這個接口用來判斷錯誤類型,在 go 中可以通過 e.(type) 判斷錯誤的類型

func respondWithError(w http.RespondWrite, err error) {
  switch e.(type) {
  case Error:
    respondWithJSON(w, e.Status(), e.Error())
  default:
    respondWithJSON(w, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
  }
}

在 go 中 實現(xiàn) Error 接口,只需要實現(xiàn) Error() 和 Status() 方法即可

func () Error() string {
  return ""
}
func () Status() int {
  return 0
}

這樣定義的方法,只能返回固定的文本和狀態(tài)碼,如果想要返回動態(tài)內(nèi)容,可以定義一個結(jié)構(gòu)體

然后 Error 和 Status 方法接受 StatusError 類型

這樣只要滿足 StatusError 類型的結(jié)構(gòu)體,就可以返回動態(tài)內(nèi)容

所以上面的代碼可以修改為:

type StatusError struct {
  Code int
  Err error
}
func (se StatusError) Error() string {
  return se.Err.Error()
}
func (se StatusError) Status() int {
  return se.Code
}

middlerware

RecoverHandler

中間件 RecoverHandler 作用是通過 defer 來捕獲 panic,然后返回 500 狀態(tài)碼

func RecoverHandler(next http.Handler) http.Handler {
  fn := func(w http.ResponseWriter, r *http.Request) {
    defer func() {
      if r := recover(); r != nil {
        log.Println("Recover from panic %+v", r)
        http.Error(w, http.StatusText(500), 500)
      }
    }()
    next.ServeHTTP(w, r)
  }
  return http.HandlerFunc(fn)
}

LoggingHandler

LoggingHandler 作用是記錄請求耗時

func (m Middleware) LoggingHandler(next http.Handler) http.Handler {
  fn := func(w http.ResponseWriter, r *http.Request) {
    start := time.Now()
    next.ServeHTTP(w, r)
    end := time.Now()
    log.Printf("[%s] %q %v", r.Method, r.URL.Path, end.Sub(start))
  }
  return http.HandlerFunc(fn)
}

中間件使用

alice 是 go 中的一個中間件庫,可以通過 alice.New() 來添加中間件,具體使用如下:

m := alice.New(middleware.LoggingHandler, middleware.RecoverHandler)
mux.Router.HandleFunc("/api/v1/user", m.ThenFunc(controller)).Methods("POST")

生成短鏈接

redis 連接

func NewRedisCli(addr string, passwd string, db int) *RedisCli {
  c := redis.NewClient(&redis.Options{
    Addr:     addr,
    Password: passwd,
    DB:       db,
  })

  if _, err := c.Ping().Result(); err != nil {
    panic(err)
  }
  return &RedisCli{Cli: c}
}

生成唯一 ID

redis 可以基于一個鍵名生成一個唯一的自增 ID,這個鍵名可以是任意的,這個方法是 Incr

代碼如下:

err = r.Cli.Incr(URLIDKEY).Err()
if err != nil {
  return "", err
}

id, err := r.Cli.Get(URLIDKEY).Int64()
if err != nil {
  return "", err
}

fmt.Println(id) // 每次調(diào)用都會自增

存儲和解析短鏈接

一個 ID 對應(yīng)一個 url,也就是說當(dāng)外面?zhèn)魅?id 時需要返回對應(yīng)的 url

func Shorten() {
  err := r.Cli.Set(fmt.Sprintf(ShortlinkKey, eid), url, time.Minute*time.Duration(exp)).Err()
  if err != nil {
    return "", err
  }
}
func UnShorten() {
  url, err := r.Cli.Get(fmt.Sprintf(ShortlinkKey, eid)).Result()
}

redis 注意事項

redis 返回的 error 有兩種情況:

  • redis.Nil 表示沒有找到對應(yīng)的值
  • 其他錯誤,表示 redis 服務(wù)出錯了

所以在使用 redis 時,需要判斷返回的錯誤類型

if err == redis.Nil {
  // 沒有找到對應(yīng)的值
} else if err != nil {
  // redis 服務(wù)出錯了
} else {
  // 正確響應(yīng)
}

測試

在測試用例中,如何發(fā)起一個請求,然后獲取響應(yīng)的數(shù)據(jù)呢?

1.構(gòu)造請求

var jsonStr = []byte(`{"url":"https://www.baidu.com","expiration_in_minutes":60}`)
req, err := http.NewRequest("POST", "/api/shorten", bytes.NewBuffer(jsonStr))
if err != nil {
  t.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")

2.捕獲 http 響應(yīng)

rw := httptest.NewRecorder()

3.模擬請求被處理

app.Router.ServeHTTP(rw, req)

4.解析響應(yīng)

if rw.Code != http.ok {
  t.Fatalf("Excepted status created, got %d", rw.Code)
}

resp := struct {
  Shortlink string `json:"shortlink"`
}{}
if err := json.NewDecoder(rw.Body).Decode(&resp); err != nil {
  t.Fatalf("should decode the response", err)
}

最終完整代碼:

var jsonStr = []byte(`{"url":"https://www.baidu.com","expiration_in_minutes":60}`)
req, err := http.NewRequest("POST", "/api/shorten", bytes.NewBuffer(jsonStr))
if err != nil {
  t.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")

rw := httptest.NewRecorder()
app.Router.ServeHTTP(rw, req)

if rw.Code != http.ok {
  t.Fatalf("Excepted status created, got %d", rw.Code)
}
resp := struct {
  Shortlink string `json:"shortlink"`
}{}

if err := json.NewDecoder(rw.Body).Decode(&resp); err != nil {
  t.Fatalf("should decode the response")
}

代碼講解

log.SetFlags(log.LstdFlags | log.Lshortfile)

作用是設(shè)置日志輸出的標(biāo)志

它們都是標(biāo)志常量,用豎線 | 連接,這是位操作符,將他們合并為一個整數(shù)值,作為 log.SetFlags() 的參數(shù)

  • log.LstdFlags 是標(biāo)準(zhǔn)時間格式:2022-01-23 01:23:23
  • log.Lshortfile 是文件名和行號:main.go:23

當(dāng)我們使用 log.Println 輸出日志時,會自動帶上時間、文件名、行號信息

recover 函數(shù)使用

recover 函數(shù)類似于其他語言的 try...catch,用來捕獲 panic,做一些處理

使用方法:

func MyFunc() {
  defer func() {
    if r := recover(); r != nil {
      // 處理 panic 情況
    }
  }
}

需要注意的是:

  • recover 函數(shù)只能在 defer 中使用,如果在 defer 之外使用,會直接返回 nil
  • recover 函數(shù)只有在 panic 之后調(diào)用才會生效,如果在 panic 之前調(diào)用,也會直接返回 nil
  • recover 函數(shù)只能捕獲當(dāng)前 goroutine 的 panic,不能捕獲其他 goroutine 的 panic

next.ServerHttp(w, r)

next.ServeHTTP(w, r),用于將 http 請求傳遞給下一個 handler

HandleFunc 和 Handle 區(qū)別

HandleFunc 接受一個普通類型的函數(shù):

func myHandle(w http.ResponseWriter, r *http.Request) {}
http.HandleFunc("xxxx", myHandle)

Handle 接收一個實現(xiàn) Handler 接口的函數(shù):

func myHandler(w http.ResponseWriter, r *http.Request) {}
http.Handle("xxxx", http.HandlerFunc(myHandler))

他們的區(qū)別是:使用 Handle 需要自己進行包裝,使用 HandleFunc 不需要

defer res.Body.Close()

為什么沒有 res.Header.Close() 方法?

因為 header 不是資源,而 body 是資源,在 go 中,一般操作資源后,要及時關(guān)閉資源,所以 go 為 body 提供了 Close() 方法

res.Body 是 io.ReadCloser 類型的接口,表示可以讀取響應(yīng)數(shù)據(jù)并關(guān)閉響應(yīng)體的對象

w.Write()

代碼在執(zhí)行了 w.Writer(res) 后,還會繼續(xù)往下執(zhí)行,除非有顯示的 reture 和 panic 終止函數(shù)執(zhí)行

func controller(w http.ResponseWriter, r *http.Request) {
  if res, err := xxx; err != nil {
    respondWithJSON(w, http.StatusOK, err)
  }
  // 這里如果有代碼,會繼續(xù)執(zhí)行
}
func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
  res, _ json.Marshal(payload)
  w.Header().Set("Content-Type", "application/json")
  w.WriteHeader(code)
  w.Write(res)
}

需要注意的是,盡管執(zhí)行了 w.Writer() 后,還會繼續(xù)往下執(zhí)行,但不會再對響應(yīng)進行修改或?qū)懭肴魏蝺?nèi)容了,因為 w.Write() 已經(jīng)將響應(yīng)寫入到 http.ResponseWriter 中了

獲取請求參數(shù)

路由 /api/info?shortlink=2

a.Router.Handle("/api/info", m.ThenFunc(a.getShortlinkInfo)).Methods("GET")
func getShortlinkInfo(w http.ResponseWriter, r *http.Request) {
  vals := r.URL.Query()
  s := vals.Get("shortlink")
  fmt.Println(s) // 2
}

路由 /2

a.Router.Handle("/{shortlink:[a-zA-Z0-9]{1,11}}", m.ThenFunc(a.redirect)).Methods("GET")
func redirect(w http.ResponseWriter, r *http.Request) {
  vars := mux.Vars(r)
  shortlink := vars["shortlink"]
  fmt.Println(shortlink) // 2
}

獲取請求體

json.NewDecoder(r.Body) 作用是將 http 請求的 body 內(nèi)容解析為 json 格式

r.body 是一個 io.Reader 類型,它代表請求的原始數(shù)據(jù)

如果關(guān)聯(lián)成功可以用 Decode() 方法來解析 json 數(shù)據(jù)

type User struct {
  Name string `json:"name"`
  Age int `json:"age"`
}
func controller(w http.ResponseWriter, r *http.Request){
  var user User
  if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
    fmt.Println(err)
  }
  fmt.Println(user)
}

new

用于創(chuàng)建一個新的零值對象,并返回該對象的指針

它接受一個類型作為參數(shù),并返回一個指向該類型的指針

適用于任何可分配的類型,如基本類型、結(jié)構(gòu)體、數(shù)組、切片、映射和接口等

// 創(chuàng)建一個新的 int 類型的零值對象,并返回指向它的指針
ptr := new(int)  // 0

需要注意的是:new 只分配了內(nèi)存,并初始化為零值,并不會對對象進行任何進一步的初始化。如果需要對對象進行自定義的初始化操作,可以使用結(jié)構(gòu)體字面量或構(gòu)造函數(shù)等方式

到此這篇關(guān)于Golang實現(xiàn)短網(wǎng)址/短鏈服務(wù)的開發(fā)筆記分享的文章就介紹到這了,更多相關(guān)Golang短網(wǎng)址內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解go語言單鏈表及其常用方法的實現(xiàn)

    詳解go語言單鏈表及其常用方法的實現(xiàn)

    這篇文章主要介紹了詳解go語言單鏈表及其常用方法的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • Go語言使用Buffer實現(xiàn)高性能處理字節(jié)和字符

    Go語言使用Buffer實現(xiàn)高性能處理字節(jié)和字符

    在?Go?中,bytes.Buffer?是一個非常高效的類型,用于處理字節(jié)數(shù)據(jù)的讀寫操作,本文將詳細(xì)介紹一下如何使用Buffer實現(xiàn)高性能處理字節(jié)和字符,有需要的小伙伴可以了解下
    2025-01-01
  • golang常用庫之配置文件解析庫-viper使用詳解

    golang常用庫之配置文件解析庫-viper使用詳解

    viper 配置管理解析庫,是由大神 Steve Francia 開發(fā),他在google領(lǐng)導(dǎo)著 golang 的產(chǎn)品開發(fā),他也是 gohugo.io 的創(chuàng)始人之一,命令行解析庫 cobra 開發(fā)者,這篇文章主要介紹了golang常用庫之配置文件解析庫-viper使用詳解,需要的朋友可以參考下
    2020-10-10
  • Golang?分割字符串的實現(xiàn)示例

    Golang?分割字符串的實現(xiàn)示例

    本文主要介紹了Golang?分割字符串的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • golang中的defer函數(shù)理解

    golang中的defer函數(shù)理解

    defer是Go語言中的延遲執(zhí)行語句,用來添加函數(shù)結(jié)束時執(zhí)行的代碼,常用于釋放某些已分配的資源、關(guān)閉數(shù)據(jù)庫連接、斷開socket連接、解鎖一個加鎖的資源,這篇文章主要介紹了golang中的defer函數(shù)理解,需要的朋友可以參考下
    2022-10-10
  • Go語言雙向鏈表list.List的實現(xiàn)示例

    Go語言雙向鏈表list.List的實現(xiàn)示例

    Go語言中的list.List是包提供的雙向鏈表實現(xiàn),具有高效的插入和刪除操作能力,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-09-09
  • Kafka安裝部署+go整合過程

    Kafka安裝部署+go整合過程

    go語言是一種快速、分布式、并發(fā)編程的語言,它天生適合于處理高并發(fā)場景下的消息傳遞和處理,在本文中,我們將介紹Kafka安裝部署+go整合過程,感興趣的朋友一起看看吧
    2024-08-08
  • golang實現(xiàn)ftp功能簡單又實用(示例代碼)

    golang實現(xiàn)ftp功能簡單又實用(示例代碼)

    本文介紹了如何使用Go語言實現(xiàn)FTP功能,包括加載配置文件和處理FTP部分,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2025-12-12
  • 值得擁有的Go語言常用內(nèi)置包使用示例

    值得擁有的Go語言常用內(nèi)置包使用示例

    本文將介紹?Go?語言的常用內(nèi)置包,詳細(xì)介紹它們的功能、用法,并通過通俗易懂的示例代碼,讓讀者掌握這些內(nèi)置包的精髓,提高?Go?語言編程技能
    2023-11-11
  • go將request?body綁定到不同的結(jié)構(gòu)體中教程

    go將request?body綁定到不同的結(jié)構(gòu)體中教程

    這篇文章主要為大家介紹了go將request?body綁定到不同的結(jié)構(gòu)體中教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10

最新評論

德惠市| 独山县| 德阳市| 东丽区| 阿鲁科尔沁旗| 福安市| 阜阳市| 饶平县| 安陆市| 米泉市| 垫江县| 宁河县| 东乡县| 涞水县| 普兰店市| 东乡族自治县| 金溪县| 齐河县| 巢湖市| 合山市| 黔江区| 中牟县| 抚顺县| 平乡县| 太白县| 阿坝县| 洞头县| 永济市| 额济纳旗| 嘉义市| 杂多县| 旺苍县| 和平区| 吴江市| 兴文县| 道真| 临桂县| 邵东县| 元氏县| 宁阳县| 嘉定区|