golang http 中介軟體
Processing HTTP requests with Go is primarily about two things: ServeMuxes and Handlers.
A ServeMux is essentially a HTTP request router (or multiplexor). It compares incoming requests against a list of predefined URL paths, and calls the associated handler for the path whenever a match is found.
Handlers are responsible for writing response headers and bodies. Almost any object can be a handler, so long as it satisfies the http.Handler
interface. In lay terms, that simply means it must have a ServeHTTP
method with the following signature:
ServeHTTP(http.ResponseWriter, *http.Request)
Go's HTTP package ships with a few functions to generate common handlers, such as FileServer
, NotFoundHandler
and RedirectHandler
. Let's begin with a simple but contrived example:
$ mkdir handler-example
$ cd handler-example
$ touch main.go
File: main.go
package main import ( "log" "net/http" ) func main() { mux := http.NewServeMux() rh := http.RedirectHandler("http://example.org", 307) mux.Handle("/foo", rh) log.Println("Listening...") http.ListenAndServe(":3000", mux) }
Let's step through this quickly:
- In the
main
function we use thehttp.NewServeMux
function to create an empty ServeMux. - We then use the
http.RedirectHandler
function to create a new handler. This handler 307 redirects all requests it receives tohttp://example.org
. - Next we use the
mux.Handle
function to register this with our new ServeMux, so it acts as the handler for all incoming requests with the URL path/foo
. - Finally we create a new server and start listening for incoming requests with the
http.ListenAndServe
function, passing in our ServeMux for it to match requests against.
Go ahead and run the application:
$ go run main.go
Listening...
And visit http://localhost:3000/foo
in your browser. You should find that your request gets successfully redirected.
The eagle-eyed of you might have noticed something interesting: The signature for the ListenAndServe function is ListenAndServe(addr string, handler Handler)
, but we passed a ServeMux as the second parameter.
We were able to do this because the ServeMux type also has a ServeHTTP
method, meaning that it too satisfies the Handler interface.
For me it simplifies things to think of a ServeMux as just being a special kind of handler, which instead of providing a response itself passes the request on to a second handler. This isn't as much of a leap as it first sounds – chaining handlers together is fairly commonplace in Go.
Custom Handlers
Let's create a custom handler which responds with the current local time in a given format:
type timeHandler struct {
format string
}
func (th *timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(th.format)
w.Write([]byte("The time is: " + tm))
}
The exact code here isn't too important.
All that really matters is that we have an object (in this case it's a timeHandler
struct, but it could equally be a string or function or anything else), and we've implemented a method with the signature ServeHTTP(http.ResponseWriter, *http.Request)
on it. That's all we need to make a handler.
Let's embed this in a concrete example:
File: main.go
package main
import (
"log"
"net/http"
"time"
)
type timeHandler struct {
format string
}
func (th *timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(th.format)
w.Write([]byte("The time is: " + tm))
}
func main() {
mux := http.NewServeMux()
th := &timeHandler{format: time.RFC1123}
mux.Handle("/time", th)
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
In the main
function we initialised the timeHandler
in exactly the same way we would any normal struct, using the &
symbol to yield a pointer. And then, like the previous example, we use the mux.Handle
function to register this with our ServeMux.
Now when we run the application, the ServeMux will pass any request for /time
straight on to our timeHandler.ServeHTTP
method.
Notice too that we could easily reuse the timeHandler
in multiple routes:
func main() {
mux := http.NewServeMux()
th1123 := &timeHandler{format: time.RFC1123}
mux.Handle("/time/rfc1123", th1123)
th3339 := &timeHandler{format: time.RFC3339}
mux.Handle("/time/rfc3339", th3339)
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
Functions as Handlers
For simple cases (like the example above) defining new custom types and ServeHTTP methods feels a bit verbose. Let's look at an alternative approach, where we leverage Go's http.HandlerFunc
type to coerce a normal function into satisfying the Handler interface.
Any function which has the signature func(http.ResponseWriter, *http.Request)
can be converted into a HandlerFunc type. This is useful because HandleFunc objects come with an inbuilt ServeHTTP
method which – rather cleverly and conveniently – executes the content of the original function.
If that sounds confusing, try taking a look at the relevant source code. You'll see that it's a very succinct way of making a function satisfy the Handler interface.
Let's reproduce the timeHandler application using this technique:
File: main.go
package main
import (
"log"
"net/http"
"time"
)
func timeHandler(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(time.RFC1123)
w.Write([]byte("The time is: " + tm))
}
func main() {
mux := http.NewServeMux()
// Convert the timeHandler function to a HandlerFunc type
th := http.HandlerFunc(timeHandler)
// And add it to the ServeMux
mux.Handle("/time", th)
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
In fact, converting a function to a HandlerFunc type and then adding it to a ServeMux like this is so common that Go provides a shortcut: the mux.HandleFunc
method.
This is what the main()
function would have looked like if we'd used this shortcut instead:
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/time", timeHandler)
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
Most of the time using a function as a handler like this works well. But there is a bit of a limitation when things start getting more complex.
You've probably noticed that, unlike the method before, we've had to hardcode the time format in the timeHandler
function. What happens when we want to pass information or variables from main()
to a handler?
A neat approach is to put our handler logic into a closure, and close over the variables we want to use:
File: main.go
package main
import (
"log"
"net/http"
"time"
)
func timeHandler(format string) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(format)
w.Write([]byte("The time is: " + tm))
}
return http.HandlerFunc(fn)
}
func main() {
mux := http.NewServeMux()
th := timeHandler(time.RFC1123)
mux.Handle("/time", th)
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
The timeHandler
function now has a subtly different role. Instead of coercing the function into a handler (like we did previously), we are now using it to return a handler. There's two key elements to making this work.
First it creates fn
, an anonymous function which accesses ‐ or closes over – the format
variable forming a closure. Regardless of what we do with the closure it will always be able to access the variables that are local to the scope it was created in – which in this case means it'll always have access to the format
variable.
Secondly our closure has the signature func(http.ResponseWriter, *http.Request)
. As you may remember from earlier, this means that we can convert it into a HandlerFunc type (so that it satisfies the Handler interface). Our timeHandler
function then returns this converted closure.
In this example we've just been passing a simple string to a handler. But in a real-world application you could use this method to pass database connection, template map, or any other application-level context. It's a good alternative to using global variables, and has the added benefit of making neat self-contained handlers for testing.
You might also see this same pattern written as:
func timeHandler(format string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(format)
w.Write([]byte("The time is: " + tm))
})
}
Or using an implicit conversion to the HandlerFunc type on return:
func timeHandler(format string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(format)
w.Write([]byte("The time is: " + tm))
}
}
The DefaultServeMux
You've probably seen DefaultServeMux mentioned in lots of places, from the simplest Hello World examples to the Go source code.
It took me a long time to realise it isn't anything special. The DefaultServeMux is just a plain ol' ServeMux like we've already been using, which gets instantiated by default when the HTTP package is used. Here's the relevant line from the Go source:
var DefaultServeMux = NewServeMux()
Generally you shouldn't use the DefaultServeMux because it poses a security risk.
Because the DefaultServeMux is stored in a global variable, any package is able to access it and register a route – including any third-party packages that your application imports. If one of those third-party packages is compromised, they could use the DefaultServeMux to expose a malicious handler to the web.
So as a rule of thumb it's a good idea to avoid the DefaultServeMux, and instead use your own locally-scoped ServeMux, like we have been so far. But if you did decide to use it...
The HTTP package provides a couple of shortcuts for working with the DefaultServeMux: http.Handle and http.HandleFunc. These do exactly the same as their namesake functions we've already looked at, with the difference that they add handlers to the DefaultServeMux instead of one that you've created.
Additionally, ListenAndServe will fall back to using the DefaultServeMux if no other handler is provided (that is, the second parameter is set to nil
).
So as a final step, let's update our timeHandler application to use the DefaultServeMux instead:
File: main.go
package main
import (
"log"
"net/http"
"time"
)
func timeHandler(format string) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(format)
w.Write([]byte("The time is: " + tm))
}
return http.HandlerFunc(fn)
}
func main() {
// Note that we skip creating the ServeMux...
var format string = time.RFC1123
th := timeHandler(format)
// We use http.Handle instead of mux.Handle...
http.Handle("/time", th)
log.Println("Listening...")
// And pass nil as the handler to ListenAndServe.
http.ListenAndServe(":3000", nil)
}
相關推薦
golang http 中介軟體
Processing HTTP requests with Go is primarily about two things: ServeMuxes and Handlers. A ServeMux is essentially a HTTP request route
golang gin 中介軟體,返回結果
package main import ( "net/http" "github.com/gin-gonic/gin" ) func response() gin.HandlerFunc { return func(c *gin.Context) {
node express 中介軟體 http-proxy-middleware 和 express-http-proxy 轉發 搞定 post 超時
2018-11-14 總結: http-proxy-middleware 轉發 post 請求 有問題,沒找到問題所在,換 express-http-proxy 代理。 前後端獨立開發,靜態檔案、模板等 前端express服務提供。後端負責介面。前端開發 轉發 ajax 到 測試伺服器或者開發伺服器。
golang 中介軟體、json返回、302跳轉
package main import ( "fmt" "net/http" ) func middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r
golangWeb框架---github.com/gin-gonic/gin學習七(重定向、中介軟體Goroutines、http自定義配置)
重定向 package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { router := gin.Default() router.GET("/raw",
9. http協議_響應狀態碼_頁面渲染流程_路由_中介軟體
1. http協議 超文字傳輸協議 協議詳細規定了 瀏覽器 和 全球資訊網伺服器 之間互相通訊的規則 客戶端與服務端通訊時傳輸的內容我們稱之為報文(請求報文、響應報文) 常見的傳送 get 請求方式 在瀏覽器位址列輸入 url 地址訪問 所有的標籤預設傳送的是 get 請求:如 scri
利用express中介軟體http-proxy-middleware代理前端跨域請求(包含get和post)
1,建立app.js var express = require('express'); var path = require('path'); var favicon = require('serve-favicon'); var logger = require('mo
golang學習之negroni對於第三方中介軟體的使用分析
negroni本身是一箇中間件,不過它也提供了拓展,以幫助我們更簡單地實現自己想要的功能的中介軟體。那麼,我們究竟該如何實現中介軟體,才能能夠封裝到negroni中,從而通過negroni來呼叫呢?可以通過一個簡單的例子分析: package main
golang 根據啟動命令切換不同資料庫以及在中介軟體中捕獲異常記錄日誌
dataBase := flag.Bool("MySql",false,"true :線上,false: 線下 預設:false") flag.Parse() //*dataBase=true db.InitDB(*dataBase) //初始化資料庫 根據 data
ASP.NET Core中介軟體計算Http請求時間
ASP.NET Core通過RequestDelegate這個委託型別來定義中介軟體 public delegate Task RequestDelegate(HttpContext context); 可將一個單獨的請求委託並行指定為匿名方法(稱為並行中介軟體),或在類中對其進行定義。可通過Use,或在Mi
Golang Web入門(3):如何優雅的設計中介軟體
摘要 在上一篇文章中,我們已經可以實現一個性能較高,且支援RESTful風格的路由了。但是,在Web應用的開發中,我們還需要一些可以被擴充套件的功能。 因此,在設計框架的過程中,應該留出可以擴充套件的空間,比如:日誌記錄、故障恢復等功能,如果我們把這些業務邏輯全都塞進Controller/Handler中,會
基於gin的golang web開發:中介軟體
gin中介軟體(middleware)提供了類似於面向切面程式設計或路由攔截器的功能,可以在請求前和請求之後新增一些自定義邏輯。實際開發中有很多場景會用到中介軟體,例如:許可權驗證,快取,錯誤處理,日誌,事務等。 #### 使用中介軟體 gin的中介軟體分為三類:全域性中介軟體、路由中介軟體、分組路由中介
golang http服務器跨域問題解決
font and http服務器 -s 客戶 run style header() client func main() { openHttpListen() } func openHttpListen() { http.HandleFunc("/
Redis實現中介軟體(訂閱)
什麼是訊息中介軟體 釋出訂閱 點對點 訊息中介軟體本身是非同步的通訊 案例:使用redis實現釋出訂閱功能 Redis釋出訂閱 Redis 釋出訂閱(pub/sub)是一種訊息通訊模式:傳送者(pub)傳送訊息,訂閱者(sub)接收訊息。 Redis 客戶端可以
資料庫路由中介軟體MyCat - 使用篇(3)上篇
此文已由作者張鎬薪授權網易雲社群釋出。 歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。 全域性序列號 資料切分後,原有的關係資料庫中的主鍵約束在分散式條件下將無法使用,因此需要引入外部機制保證資料唯一性標識,這種保證全域性性的資料唯一標識的機制就是全域性序列號(sequence)。 1. 本地檔案方式 c
資料庫路由中介軟體MyCat - 使用篇(3)下篇
此文已由作者張鎬薪授權網易雲社群釋出。 歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。 2. 配置conf/server.xml server.xml幾乎儲存了所有mycat需要的系統配置資訊。其在程式碼內直接的對映類為SystemConfig類。 參考完整配置: <?xml versi
資料庫路由中介軟體MyCat - 使用篇(6)
此文已由作者張鎬薪授權網易雲社群釋出。 歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。 配置MyCat 4. 配置schema.xml schema.xml裡面管理著MyCat的邏輯庫、表,每張表使用的分片規則、分佈在哪個DataNode以及DataSource上。 之前的例子: ```xml<
資料庫路由中介軟體MyCat - 原始碼篇(2)
此文已由作者張鎬薪授權網易雲社群釋出。 歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。 2. 前端連線建立與認證 Title:MySql連線建立以及認證過程client->MySql:1.TCP連線請求 MySql->client:2.接受TCP連線client->MySq
MyCat:開源分散式資料庫中介軟體
為什麼需要MyCat? 雖然雲端計算時代,傳統資料庫存在著先天性的弊端,但是NoSQL資料庫又無法將其替代。如果傳統資料易於擴充套件,可切分,就可以避免單機(單庫)的效能缺陷。 MyCat的目標就是:低成本地將現有的單機資料庫和應用平滑遷移到“雲”端,解決資料儲存和業務規模迅速
Django元件 中介軟體
中介軟體的概念 中介軟體是介於request與response處理之間的一道處理過程,相對比較輕量級,並且在全域性上改變django的輸入與輸出。因為改變的是全域性,所以需要謹慎使用,用不好會影響到效能。 如果你想修改請求,例如被傳送到view中的HttpRequest物件。或者你想修改view返回的Ht