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
您能否建议一些替代方法来实现此行为,或者我是否必须在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 方法,这会在所有处理函数(此处为 Handle2、Handle1)中重复出现。
我希望我的仓库设计实现的是:
- 附加到 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)
}

