- Go Programming Blueprints(Second Edition)
- Mat Ryer
- 644字
- 2021-07-08 10:40:01
Handlers all the way down
For our chat application, we implemented our own http.Handler
type (the room) in order to easily compile, execute, and deliver HTML content to browsers. Since this is a very simple but powerful interface, we are going to continue to use it wherever possible when adding functionality to our HTTP processing.
In order to determine whether a user is allowed to proceed, we will create an authorization wrapper handler that will perform the check and pass the execution on to the inner handler only if the user is authorized.
Our wrapper handler will satisfy the same http.Handler
interface as the object inside it, allowing us to wrap any valid handler. In fact, even the authentication handler we are about to write could be later encapsulated inside a similar wrapper if required.

Chaining pattern when applied to HTTP handlers
The preceding diagram shows how this pattern could be applied in a more complicated HTTP handler scenario. Each object implements the http.Handler
interface. This means that an object could be passed to the http.Handle
method to directly handle a request, or it can be given to another object, which could add some kind of extra functionality. The Logging
handler may write to a log file before and after the ServeHTTP
method is called on the inner handler. Because the inner handler is just another http.Handler
, any other handler can be wrapped in (or decorated with) the Logging
handler.
It is also common for an object to contain logic that decides which inner handler should be executed. For example, our authentication handler will either pass the execution to the wrapped handler, or handle the request itself by issuing a redirect to the browser.
That's plenty of theory for now; let's write some code. Create a new file called auth.go
in the chat
folder:
package main import ("net/http") type authHandler struct { next http.Handler } func (h *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { _, err := r.Cookie("auth") if err == http.ErrNoCookie { // not authenticated w.Header().Set("Location", "/login") w.WriteHeader(http.StatusTemporaryRedirect) return } if err != nil { // some other error http.Error(w, err.Error(), http.StatusInternalServerError) return } // success - call the next handler h.next.ServeHTTP(w, r) } func MustAuth(handler http.Handler) http.Handler { return &authHandler{next: handler} }
The authHandler
type not only implements the ServeHTTP
method (which satisfies the http.Handler
interface), but also stores (wraps) http.Handler
in the next
field. Our MustAuth
helper function simply creates authHandler
that wraps any other http.Handler
. This is the pattern that allows us to easily add authorization to our code in main.go
.
Let's tweak the following root mapping line:
http.Handle("/", &templateHandler{filename: "chat.html"})
Let's change the first argument to make it explicit about the page meant for chatting. Next, let's use the MustAuth
function to wrap templateHandler
for the second argument:
http.Handle("/chat", MustAuth(&templateHandler{filename: "chat.html"}))
Wrapping templateHandler
with the MustAuth
function will cause the execution to run through authHandler
first; it will run only to templateHandler
if the request is authenticated.
The ServeHTTP
method in authHandler
will look for a special cookie called auth
, and it will use the Header
and WriteHeader
methods on http.ResponseWriter
to redirect the user to a login page if the cookie is missing. Notice that we discard the cookie itself using the underscore character and capture only the returning error; this is because we only care about whether the cookie is present at this point.
Build and run the chat application and try to hit http://localhost:8080/chat
:
go build -o chat ./chat -host=":8080"
Tip
You need to delete your cookies to clear out previous authentication tokens or any other cookies that might be left over from other development projects served through the localhost.
If you look in the address bar of your browser, you will notice that you are immediately redirected to the /login
page. Since we cannot handle that path yet, you'll just get a 404 page not found error.
- 青少年P(guān)ython編程入門
- INSTANT Passbook App Development for iOS How-to
- Swift語言實(shí)戰(zhàn)精講
- 硅谷Python工程師面試指南:數(shù)據(jù)結(jié)構(gòu)、算法與系統(tǒng)設(shè)計
- 西門子S7-200 SMART PLC編程從入門到實(shí)踐
- Java SE實(shí)踐教程
- 精通Spring:Java Web開發(fā)與Spring Boot高級功能
- Building UIs with Wijmo
- 計算機(jī)程序的構(gòu)造和解釋(JavaScript版)
- Building Web and Mobile ArcGIS Server Applications with JavaScript(Second Edition)
- 詩意的邊緣
- Apache Kafka 1.0 Cookbook
- micro:bit軟件指南
- 情境微課開發(fā)(第2版)
- 基于Docker的Redis入門與實(shí)戰(zhàn)