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

golang 實(shí)現(xiàn)一個(gè)restful微服務(wù)的操作

 更新時(shí)間:2021年04月30日 11:05:55   作者:貓哭  
這篇文章主要介紹了golang 實(shí)現(xiàn)一個(gè)restful微服務(wù)的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧

如何用net/http構(gòu)建一個(gè)簡單的web服務(wù)

Golang提供了簡潔的方法來構(gòu)建web服務(wù)

package main 
import (
    "net/http"
)
 
func HelloResponse(rw http.ResponseWriter, request *http.Request) {
    fmt.Fprintf(w, "Hello world.")
}
 
func main() {
    http.HandleFunc("/", HelloResponse)
    http.ListenAndServe(":3000", nil)
}

其中核心的兩個(gè)方法:

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)):HandleFunc注冊一個(gè)handler function對應(yīng)到給定的pattern。

func ListenAndServe(addr string, handler Handler) error:ListenAndServe監(jiān)聽給定的TCP網(wǎng)絡(luò)地址,接著帶上handler調(diào)用Serve方法來接收請求。

在go build之后,執(zhí)行編譯后的文件就能在客戶端看到hello world了

有了web服務(wù),就可以制定小目標(biāo)了

我認(rèn)為作為第一版本,不需要復(fù)雜的設(shè)計(jì),只需要接收到用戶的請求,并且找到對應(yīng)的handler,執(zhí)行其邏輯,然后返回JSON響應(yīng)就好了。

小目標(biāo)有了,那怎么實(shí)現(xiàn)呢?

1.設(shè)計(jì)用戶如何注冊Controller和Action

據(jù)我觀察,一些框架是在Controller里預(yù)先設(shè)定了GET,POST,PUT等一系列方法,負(fù)責(zé)接收GET,POST,PUT的HTTP請求。

我認(rèn)為這樣設(shè)計(jì)的確有其優(yōu)勢,因?yàn)橛脩糁恍枰獙?shí)現(xiàn)這些方法就好了,但在業(yè)務(wù)層面也有其劣勢,因?yàn)槲覀儧]有辦法保證負(fù)責(zé)一個(gè)頁面或者功能的Controller只接收一個(gè)GET請求,如果有2個(gè)GET請求,那就需要再建立一個(gè)Controller,單單實(shí)現(xiàn)其GET方法。

因此我借鑒了PHP社區(qū)中Laravel注冊Controller和Action的語法:Get("/", "IndexController@Index")。

用戶只需要定義:

type IndexController struct {
}
 
func (IndexController *IndexController) Index(//params) (//return values) {
}

當(dāng)然這樣思考后,就給框架帶入了一點(diǎn)動(dòng)態(tài)腳本語言的特性,肯定會用到Golang的reflect庫。

2.設(shè)計(jì)Path和Controller還有Action的關(guān)系容器

我運(yùn)用了Golang的map,定義了map[string]map[string]map[string]string這樣的數(shù)據(jù)結(jié)構(gòu)

以["/":["GET":["IndexController":"Get"], "POST":["IndexController":"Post"]], "/foo":["GET":["IndexController":"Foo"]]]舉例:

這個(gè)說明了在"/"這個(gè)PATH下面,有GET和POST請求,分別對應(yīng)了IndexController下的Get和Post方法,在"/foo"這個(gè)PATH下面,有GET請求,對應(yīng)IndexController下的Foo方法。

在接受請求時(shí)候,如果沒有找到對應(yīng)的方法,就返回405。

3.如何將注冊了的一系列Method與PATH綁定來接收外部請求

我們可以看到,func HandleFunc(pattern string, handler func(ResponseWriter, *Request))要求的handler類型是func(ResponseWriter, *Request)),這和我們設(shè)計(jì)的functionfunc (IndexController *IndexController) Index(//params) (//return values) {}有所差距。

這時(shí)候我發(fā)現(xiàn)由于Golang具備First Class Functions特性,因此我們可以將函數(shù)做如下處理:

http.HandleFunc(path, HandleRequest()) 
func HandleRequest() {
    return func(rw http.ResponseWriter, request *http.Request) {
        // do your logic
    }
}

4.和encoding/json說Hi

當(dāng)我們接收到function的返回值后,我們就需要對結(jié)果進(jìn)行json encode,而encoding/json正是負(fù)責(zé)這個(gè)功能。 我用的是json.Marshal():

func Marshal(v interface{}) ([]byte, error): Marshal返回v的encoding結(jié)果。

如何使用

package main 
import (
    "net/url"
    "net/http"
    "github.com/ZhenhangTung/GoGym"
)
 
type IndexController struct {
}
 
func (IndexController *IndexController) Index(request map[string]url.Values, headers http.Header) (statusCode int, response interface{}) {
    return 200, map[string]string{"hello": "world"}
}
 
type BarController struct {
}
 
func (*BarController) Bar(request map[string]url.Values, headers http.Header) (statusCode int, response interface{}, responseHeader http.Header) {
    return 200, map[string]string{"GoTo": "Bar"}, http.Header{"Foo": {"Bar", "Baz"}}
}
 
func main() {
    var apiService = GoGym.Prepare()
    apiService.Get("index", "IndexController@Index")
    apiService.Post("bar", "BarController@Bar")
    controllers := []interface{}{&IndexController{}}
    apiService.RegisterControllers(controllers)
    apiService.RegisterController(&BarController{})
    apiService.Serve(3000)
}

項(xiàng)目完整代碼

package GoGym 
import (
    "encoding/json"
    "fmt"
    "net/http"
    "net/url"
    "reflect"
    "strings"
)
 
const (
    GETMethod     = "GET"
    POSTMethod    = "POST"
    PUTMethod     = "PUT"
    PATCHMethod   = "PATCH"
    DELETEMethod  = "DELETE"
    OPTIONSMethod = "OPTIONS"
)
 
const (
    HTTPMethodNotAllowed = 405
)
 
// APIService for now is the struct for containing controllerRegistry and registeredPathAndController,
// and it is the core service provider
type APIService struct {
    // controllerRegistry is where all registered controllers exist
    controllerRegistry map[string]interface{}
    //registeredPathAndController is a mapping of paths and controllers
    registeredPathAndController map[string]map[string]map[string]string
    requestForm                 map[string]url.Values
}
 
func (api *APIService) Get(path, controllerWithActionString string) {
    mapping := api.mappingRequestMethodWithControllerAndActions(GETMethod, path, controllerWithActionString)
    api.registeredPathAndController[path] = mapping
}
 
func (api *APIService) Post(path, controllerWithActionString string) {
    mapping := api.mappingRequestMethodWithControllerAndActions(POSTMethod, path, controllerWithActionString)
    api.registeredPathAndController[path] = mapping
}
 
func (api *APIService) Put(path, controllerWithActionString string) {
    mapping := api.mappingRequestMethodWithControllerAndActions(PUTMethod, path, controllerWithActionString)
    api.registeredPathAndController[path] = mapping
}
 
func (api *APIService) Patch(path, controllerWithActionString string) {
    mapping := api.mappingRequestMethodWithControllerAndActions(PATCHMethod, path, controllerWithActionString)
    api.registeredPathAndController[path] = mapping
}
 
func (api *APIService) Options(path, controllerWithActionString string) {
    mapping := api.mappingRequestMethodWithControllerAndActions(OPTIONSMethod, path, controllerWithActionString)
    api.registeredPathAndController[path] = mapping
}
 
func (api *APIService) Delete(path, controllerWithActionString string) {
    mapping := api.mappingRequestMethodWithControllerAndActions(DELETEMethod, path, controllerWithActionString)
    api.registeredPathAndController[path] = mapping
}
 
// mappingRequestMethodWithControllerAndActions is a function for mapping request method with controllers
// which containing actions
func (api *APIService) mappingRequestMethodWithControllerAndActions(requestMethod, path, controllerWithActionString string) map[string]map[string]string {
    mappingResult := make(map[string]map[string]string)
    if length := len(api.registeredPathAndController[path]); length > 0 {
        mappingResult = api.registeredPathAndController[path]
    }
    controllerAndActionSlice := strings.Split(controllerWithActionString, "@")
    controller := controllerAndActionSlice[0]
    action := controllerAndActionSlice[1]
    controllerAndActionMap := map[string]string{controller: action}
    mappingResult[requestMethod] = controllerAndActionMap
    return mappingResult
}
 
// HandleRequest is a function to handle http request
func (api *APIService) HandleRequest(controllers map[string]map[string]string) http.HandlerFunc {
    return func(rw http.ResponseWriter, request *http.Request) {
        request.ParseForm()
        method := request.Method
        api.requestForm["query"] = request.Form
        api.requestForm["form"] = request.PostForm
        macthedControllers, ok := controllers[method]
        if !ok {
            rw.WriteHeader(HTTPMethodNotAllowed)
        }
        for k, v := range macthedControllers {
            controllerKey := "*" + k
            controller := api.controllerRegistry[controllerKey]
            in := make([]reflect.Value, 2)
            in[0] = reflect.ValueOf(api.requestForm)
            in[1] = reflect.ValueOf(request.Header)
            returnValues := reflect.ValueOf(controller).MethodByName(v).Call(in)
            statusCode := returnValues[0].Interface()
            intStatusCode := statusCode.(int)
            response := returnValues[1].Interface()
            responseHeaders := http.Header{}
            if len(returnValues) == 3 {
                responseHeaders = returnValues[2].Interface().(http.Header)
            }
            api.JSONResponse(rw, intStatusCode, response, responseHeaders)
        }
    }
}
 
// RegisterHandleFunc is a function registers a handle function to handle request from path
func (api *APIService) RegisterHandleFunc() {
    for k, v := range api.registeredPathAndController {
        path := k
        if !strings.HasPrefix(k, "/") {
            path = fmt.Sprintf("/%v", k)
        }
        http.HandleFunc(path, api.HandleRequest(v))
    }
}
 
// RegisterControllers is a function registers a struct of controllers into controllerRegistry
func (api *APIService) RegisterControllers(controllers []interface{}) {
    for _, v := range controllers {
        api.RegisterController(v)
    }
}
 
// RegisterControllers is a function registers a controller into controllerRegistry
func (api *APIService) RegisterController(controller interface{}) {
    controllerType := getType(controller)
    api.controllerRegistry[controllerType] = controller
}
 
// getType is a function gets the type of value
func getType(value interface{}) string {
    if t := reflect.TypeOf(value); t.Kind() == reflect.Ptr {
        return "*" + t.Elem().Name()
    } else {
        return t.Name()
    }
}
 
// Serve is a function
func (api *APIService) Serve(port int) {
    api.RegisterHandleFunc()
    fullPort := fmt.Sprintf(":%d", port)
    http.ListenAndServe(fullPort, nil)
}
 
// JSONResponse is a function return json response
func (api *APIService) JSONResponse(rw http.ResponseWriter, statusCode int, response interface{}, headers http.Header) {
    for k, v := range headers {
        for _, header := range v {
            rw.Header().Add(k, header)
        }
    }
    rw.Header().Add("Content-Type", "application/json")
    rw.WriteHeader(statusCode)
    rsp, err := json.Marshal(response)
    if err != nil {
        // TODO: logging error
        fmt.Println("JSON err:", err)
    }
    rw.Write(rsp)
}
 
// Prepare is a fucntion prepare the service and return prepared service to the user
func Prepare() *APIService {
    var apiService = new(APIService)
    apiService.controllerRegistry = make(map[string]interface{})
    apiService.registeredPathAndController = make(map[string]map[string]map[string]string)
    apiService.requestForm = make(map[string]url.Values)
    return apiService
}

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。

相關(guān)文章

  • Go語言中基本數(shù)據(jù)類型的相互轉(zhuǎn)換詳解

    Go語言中基本數(shù)據(jù)類型的相互轉(zhuǎn)換詳解

    Go在不同類型的變量之間賦值時(shí)需要顯示轉(zhuǎn)換,不能自動(dòng)轉(zhuǎn)換。這篇文章主要和大家介紹了Go語言中基本數(shù)據(jù)類型的相互轉(zhuǎn)換,感興趣的小伙伴可以了解一下
    2022-10-10
  • golang之?dāng)?shù)據(jù)校驗(yàn)的實(shí)現(xiàn)代碼示例

    golang之?dāng)?shù)據(jù)校驗(yàn)的實(shí)現(xiàn)代碼示例

    這篇文章主要介紹了golang之?dāng)?shù)據(jù)校檢的實(shí)現(xiàn)代碼示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • GO語言中的方法值和方法表達(dá)式的使用方法詳解

    GO語言中的方法值和方法表達(dá)式的使用方法詳解

    這篇文章主要介紹了GO的方法值和方法表達(dá)式的使用方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-02-02
  • go語言實(shí)現(xiàn)markdown解析庫的方法示例

    go語言實(shí)現(xiàn)markdown解析庫的方法示例

    這篇文章主要介紹了go語言實(shí)現(xiàn)markdown解析庫的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • Go?Java算法之單詞搜索示例詳解

    Go?Java算法之單詞搜索示例詳解

    這篇文章主要為大家介紹了Go?Java算法之單詞搜索示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • golang sql語句超時(shí)控制方案及原理

    golang sql語句超時(shí)控制方案及原理

    一般應(yīng)用程序在執(zhí)行一條sql語句時(shí),都會給這條sql設(shè)置一個(gè)超時(shí)時(shí)間,本文主要介紹了golang sql語句超時(shí)控制方案及原理,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12
  • go語言實(shí)戰(zhàn)之實(shí)現(xiàn)比特幣地址校驗(yàn)步驟

    go語言實(shí)戰(zhàn)之實(shí)現(xiàn)比特幣地址校驗(yàn)步驟

    這篇文章主要介紹了go語言實(shí)戰(zhàn)之實(shí)現(xiàn)比特幣地址校驗(yàn)步驟,利用生產(chǎn)的隨機(jī)數(shù)采用橢圓加密算法生成公鑰,具體步驟實(shí)例代碼請參考下本文
    2021-05-05
  • Golang接口的定義與空接口及斷言的使用示例

    Golang接口的定義與空接口及斷言的使用示例

    在?Golang?中,接口是一種類型,它是由一組方法簽名組成的抽象集合。這篇文章主要為大家介紹了Golang接口的具體使用,希望對大家有所幫助,空接口是特殊形式的接口類型,普通的接口都有方法,而空接口沒有定義任何方法口,也因此,我們可以說所有類型都至少實(shí)現(xiàn)了空接口
    2023-04-04
  • Golang 1.16 中 Modules的主要變化更新

    Golang 1.16 中 Modules的主要變化更新

    這篇文章主要介紹了Golang 1.16 中 Modules的主要變化更新,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02
  • Go語言中使用 buffered channel 實(shí)現(xiàn)線程安全的 pool

    Go語言中使用 buffered channel 實(shí)現(xiàn)線程安全的 pool

    這篇文章主要介紹了Go語言中使用 buffered channel 實(shí)現(xiàn)線程安全的 pool,因?yàn)镚o語言自帶的sync.Pool并不是很好用,所以自己實(shí)現(xiàn)了一線程安全的 pool,需要的朋友可以參考下
    2014-10-10

最新評論

德兴市| 台江县| 光山县| 黑龙江省| 北辰区| 扶风县| 普陀区| 镇康县| 喀喇| 芦山县| 静乐县| 平泉县| 泉州市| 平潭县| 眉山市| 青阳县| 湖北省| 会泽县| 咸丰县| 乐昌市| 湘潭市| 沙田区| 宜兰市| 德昌县| 盘山县| 潼南县| 余江县| 孙吴县| 道孚县| 定兴县| 长宁区| 固安县| 岚皋县| 鄂伦春自治旗| 同仁县| 广宗县| 杭州市| 昔阳县| 甘孜| 新营市| 武清区|