Golang极简REST微服务实现指南

Golang极简REST微服务实现指南

package main

import (
	"fmt"
	"net/http"
)

type HelloHandler struct{}

func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	//some matching to GET/POST/PATCH.. method
}
func (h *HelloHandler) Get(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello!")
}

func main() {
	hello := HelloHandler{}
	world := WorldHandler{}
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.Handle("/hello", &hello)
	http.Handle("/world", &world)
	server.ListenAndServe()
}

这是我目前创建 API 端点的方式。 但这种方法中,ServeHTTP 方法会重复出现,尽管其处理程序执行的是相同的操作。

有人能建议更好的处理程序附加方式,以便减少 ServeHTTP 的重复吗? 提前感谢


更多关于Golang极简REST微服务实现指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html

13 回复

非常感谢。这将会非常有帮助。非常感谢。

更多关于Golang极简REST微服务实现指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


您能否建议一些替代方法来实现此行为,或者我是否必须在B中显式调用打印?

感谢您的建议。 我提议的代码也可以与以下内容一起使用。在这里,我正在为我的处理程序代码组织而苦恼。

我不确定是否完全理解了你的问题。 对于API的每一个操作,你都会有一个单独的 HandleUserCreate/HandleUserBan 函数,但它们之间会共享一个结构体。 你无法避免调用 http.Handle* 函数。

Go 不是这样工作的。我们没有继承,而是组合。这意味着在 B 中你可以找到 A,但 B 并没有从 A 继承任何东西。print 语句引用的是 A 中的 print 函数,Go 会尝试使用这个函数。

我想创建一个类似于 Flask-RESTful 的项目结构。

其中添加一个处理程序看起来像这样:

api.add_resource(Foo, '/Foo', '/Foo/<string:id>')

其中 Foo 看起来像这样:

class Foo(Resource):
    def get(self):
        pass
    def post(self):
        pass

但这样一来,我将在 Handle 函数中重定向到各种 HTTP 方法,这会在所有处理函数(此处为 Handle2Handle1)中重复出现。

我希望我的仓库设计实现的是:

  • 附加到 URI 的处理程序内部实现 HTTP 方法处理函数
  • 为相同功能减少重复代码。

看起来您想构建一个带有某种自动化/魔法功能的REST应用。在Go语言中,我们更推崇显式地处理事务,这就是为什么您几乎找不到能实现这种功能的工具。

使用mux是个好主意,但我不确定它是否能以您期望的方式解决问题。您可以使用 reflect 包来实现您的需求,查看函数的签名并注册相应的处理程序。

与Python相比,Go是一种编译型/静态类型语言,这也是实现起来更困难的原因——但仍然是可行的。如果您找到了问题的答案或在实现中遇到问题,请告诉我。

我在想你为什么试图做你正在做的事情。Go语言的方式通常是这样的:

package main

import "fmt"

type helloer interface {
	hello() string
}

type A struct {
}

func (a A) hello() string {
	return "Hello A"
}

type B struct {
}

func (b B) hello() string {
	return "Hello B"
}

type printer struct {
	h helloer
}

func (a *printer) print() {
	fmt.Println(a.h.hello())
}

func main() {
	a := printer{A{}}
	a.print()

	b := printer{B{}}
	b.print()
}

感谢您的建议,这让我意识到这不是一个与网络相关的问题。

我实际面临的问题是:

package main

import "fmt"

type A struct{}

func (a *A) hello() string {
	return "Hello A"
}

func (a *A) print() {
	fmt.Println(a.hello())
}

type B struct {
	A
}

func (b *B) hello() string{
	return "Hello B"
}

func main() {
	a := A{}
	a.print()
	
	b := B{}
	b.print()
}

如何通过 print 方法调用 b 上的 hello 方法?

你好!

你正在使用 http.Handle 函数,它接受你所讨论的接口。你可以直接将其改为 HandleFunc 并使用自定义函数。

package main

import (
	"fmt"
	"net/http"
)

type HelloHandler struct{}

func (h *HelloHandler) Handle1(w http.ResponseWriter, r *http.Request) {
	// 一些针对 GET/POST/PATCH.. 方法的匹配逻辑
}
func (h *HelloHandler) Handle2(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello!")
}

func main() {
	hello := &HelloHandler{}
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/hello", hello.Handle1)
	http.HandleFunc("/world", hello.Handle1)
	server.ListenAndServe()
}

为什么不直接使用mux呢?你可以轻松注册路由,将URL路径映射到处理函数。如果传入的请求URL匹配其中一个路径,就会调用相应的处理函数,并将(http.ResponseWriter, *http.Request)作为参数传递。路径也可以包含变量。它们使用格式{name}{name:pattern}定义。如果没有定义正则表达式模式,匹配的变量将是直到下一个斜杠之前的任何内容。以下是一个简单的示例:

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/gorilla/mux"
)

func main() {
	router := mux.NewRouter().StrictSlash(true)
	router.HandleFunc("/", indexHandler)
	router.HandleFunc("/books", booksIndexHandler)
	router.HandleFunc("/books/{id}", booksShowHandler)
	router.HandleFunc("/category", categoryIndexHandler)
	router.HandleFunc("/category/{id:[a-zA-Z0-9]+}", categoryShowHandler)

	log.Fatal(http.ListenAndServe(":8080", router))
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Welcome!")
}

func booksIndexHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Books Index!")
}

func booksShowHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	bookId := vars["id"]
	fmt.Fprintln(w, "Book show:", bookId)
}

func categoryIndexHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Category Index!")
}

func categoryShowHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	categoryId := vars["id"]
	fmt.Fprintln(w, "Category show:", categoryId)
}

你可以使用 http.HandlerFunc 来避免重复的 ServeHTTP 方法。下面是改进后的实现:

package main

import (
	"fmt"
	"net/http"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}
	fmt.Fprintf(w, "Hello!")
}

func worldHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}
	fmt.Fprintf(w, "World!")
}

func main() {
	http.HandleFunc("/hello", helloHandler)
	http.HandleFunc("/world", worldHandler)
	http.ListenAndServe("127.0.0.1:8080", nil)
}

如果你需要更结构化的方式,可以使用接口组合:

package main

import (
	"fmt"
	"net/http"
)

type BaseHandler struct{}

func (h *BaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodGet:
		h.Get(w, r)
	case http.MethodPost:
		h.Post(w, r)
	default:
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	}
}

func (h *BaseHandler) Get(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Not implemented", http.StatusNotImplemented)
}

func (h *BaseHandler) Post(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Not implemented", http.StatusNotImplemented)
}

type HelloHandler struct {
	BaseHandler
}

func (h *HelloHandler) Get(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello!")
}

type WorldHandler struct {
	BaseHandler
}

func (h *WorldHandler) Get(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "World!")
}

func main() {
	hello := &HelloHandler{}
	world := &WorldHandler{}
	
	http.Handle("/hello", hello)
	http.Handle("/world", world)
	http.ListenAndServe("127.0.0.1:8080", nil)
}

对于更复杂的路由需求,可以考虑使用第三方路由库如 gorilla/mux

package main

import (
	"fmt"
	"net/http"
	"github.com/gorilla/mux"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello!")
}

func worldHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "World!")
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc("/hello", helloHandler).Methods("GET")
	r.HandleFunc("/world", worldHandler).Methods("GET")
	
	http.ListenAndServe("127.0.0.1:8080", r)
}
回到顶部