Creating and using the HTTP middle tier (Making and using HTTP middleware)

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed. When you build your Web app, you may need to run some common functions for many (even all) HTTP requests. You may need to log each request, compress each response, or check the cache before performing some important processing. One way to organize this common function is to set it to the middle tier-self-contained code, which processes the request independently before or after the normal application handler. In Go, the common location for using the middle tier is between Servemux and application processing, so the usual flow of control over HTTP requests is as follows: ' Servemux = middleware Handler = Application Handler ' In this article, I'll explain how to make a custom middle tier work in this mode, and some concrete examples of how to use third-party middle-tier packages. # # Fundamentals (the Basic Principles) making and using the middle tier in Go is simple. We can envision: implement our own middle tier to make it meet HTTP. Handler interface. Build a processing chain that includes our middle-tier handlers and our normal application handlers, which we can use to register HTTP. Servemux. I'll explain how to do it. I hope you are familiar with the following method of constructing a handler (if not, it is best to look at the underlying program before continuing reading). "' Gofunc messagehandler (message string) http. Handler {return HTTP. Handlerfunc (Func (w http. Responsewriter, R *http. Request) {W.write ([]byte (Message)})} ' in this handler, we place our logic (a simple ' w.write ') in an anonymous function and encapsulate the ' message ' variable to form a closure. Then we use HTTP. Handlerfunc the adapter and returns it, converting the closure into a handler. We can use this same method to create a series of handlers. We pass the next handler in the chain as a variable to the closure (as opposed to the above), and then transfer control to the next handler by calling the Servehttp () method. This gives us a complete pattern of building the middle tier: "' Gofunc examplemiddleware (next http. Handler) http. Handler {return HTTP. HandlerFunc (Func (w http. Responsewriter, R *http. Request) {//Our middleware logic goes here ... next. Servehttp (W, R)})} ' you will notice that this middle-tier function has a ' func (http. Handler) http. Handler ' signature. It takes a handler as a parameter and returns a handler. This is useful for two reasons: because it returns a handler, we can register the middle tier function directly using the standard SERVEMUX provided by the Net/http package. By nesting the middle-tier functions together, we can create an arbitrarily long handler chain. For example: ' HTTP. Handle ("/", Middlewareone (Middlewaretwo (Finalhandler)) ' # # Control Flow description (illustrating the flow of control) Let's look at a simple example, It comes with some middle-tier functions that simply write log messages to standard output: ' File:main.go ' ' Gopackage mainimport ("Log" "Net/http") func Middlewareone (next http. Handler) http. Handler {return HTTP. Handlerfunc (Func (w http. Responsewriter, R *http. Request) {log. Println ("Executing Middlewareone") next. Servehttp (W, R) log. Println ("Executing Middlewareone Again")})}func Middlewaretwo (next http. Handler) http. Handler {return HTTP. Handlerfunc (Func (w http. Responsewriter, R *http. Request) {log. Println ("Executing middlewaretwo") if R.url. Path! = "/" {return} next. Servehttp (W, R) log. Println ("Executing Middlewaretwo Again")})}func Final (W http. Responsewriter, R *http. Request) {log. Println ("Executing Finalhandler") w.write ([]byte ("OK"))}func main () {finalhandler: = http. Handlerfunc (final) http. Handle ("/", Middlewareone (Middlewaretwo (Finalhandler))) HTTP. Listenandserve (":", Nil)} "runs the application and makes a request to ' http://localhost:3000 '. You should get a log output like this: ' $ go run MAIN.GO2014/10/13 20:27:36 executing MIDDLEWAREONE2014/10/13 20:27:36 executing MIDDLEWARETWO2014/10/13 20:27:36 executing FINALHANDLER2014/10/13 20:27:36 executing middlewareTwo AGAIN2014/10/13 20:27:36 executing Middlewareone Again "Obviously, you can see how to pass control in a nested order by a handler chain, and then return in the opposite direction. At any time, we can stop the chain-passing control by returning in the middle-layer handler. In the example above, I included a conditional return function in the middle tier. Try again by visiting ' Http://localhost:3000/foo ' and check the logs-you will find that this request is not further passed through the middle tier to the backup chain. # # See how it's done with an appropriate example? (understood. How about a Proper Example?) All right. Suppose we are building a service that contains XML requests in the body of processing. We want to create some middle layers, they a) to check if the request body exists, B) sniff to make sure it is XML (format). If any of these checks fail, we want our middle tier to write an error message and stop passing the request to our app handler. ' File:main.go ' ' gopackage mainimport ("bytes" "Net/http") func EnforcexmlhanDler (next http. Handler) http. Handler {return HTTP. Handlerfunc (Func (w http. Responsewriter, R *http. Request) {//Check for a request body if R.contentlength = = 0 {http. Error (W, http. StatusText (+), (+) return}//Check its MIME type buf: = new (bytes. Buffer) buf. Readfrom (r.body) if HTTP. Detectcontenttype (BUF. Bytes ())! = "Text/xml; Charset=utf-8 "{http. Error (W, http. StatusText (415), 415) return} next. Servehttp (W, R)})}func Main () {finalhandler: = http. Handlerfunc (final) http. Handle ("/", Enforcexmlhandler (Finalhandler)) http. Listenandserve (":", nil)}func final (w http. Responsewriter, R *http. Request) {W.write ([]byte ("OK")} "" This looks good. We test it by creating a simple XML file: "$ cat > Books.xml<?xml version=" 1.0 "?><books> <book> <author>h. G. Wells</author> <title>the time machine</title> <price>8.50</price> </book></ Books> ' and use the Curl command to make some requests: ' ' $ curl-i localhost:3000http/1.1-Bad requestcontent-type:text/plain; charset=Utf-8content-length:12bad request$ Curl-i-D "This is not XML" localhost:3000http/1.1 415 unsupported Media TypeContent- Type:text/plain; charset=utf-8content-length:23unsupported Media type$ curl-i-D @books. xml localhost:3000http/1.1 Okdate:fri, + Oc T 13:42:10 Gmtcontent-length:2content-type:text/plain; Charset=utf-8ok ' # # using a third-party middle tier (using Third-party middleware) basically you want to use a third-party package directly instead of writing the middle tier yourself. Here we will see a pair of (third-party packages): [Goji/httpauth] (http://elithrar.github.io/article/httpauth-basic-auth-for-go/) and Gorilla [ Logginghandler] (Http://www.gorillatoolkit.org/pkg/handlers#LoggingHandler). The Goji/httpauth package provides HTTP Basic authentication functionality. It has a [Simplebasicauth] (Https://godoc.org/github.com/goji/httpauth#SimpleBasicAuth) helper that returns a signed ' Func (http. Handler) http. Handler ' function. This means that we can use it just like our custom middle tier (in the same way). ' $ go get github.com/goji/httpauth ' ' ' File:main.go ' gopackage mainimport ("Github.com/goji/httpauth" "net/ HTTP ") Func main () {finalhandler: = http. Handlerfunc (Final) Authhandler: = Httpauth. SimplebAsicauth ("username", "password") http. Handle ("/", Authhandler (Finalhandler)) http. Listenandserve (":", nil)}func final (w http. Responsewriter, R *http. Request) {W.write ([]byte ("OK")} "" If you run this example, you should get your response to valid and invalid credentials: ' $ curl-i username:password@localhost : 3000http/1.1 Okcontent-length:2content-type:text/plain; charset=utf-8ok$ curl-i username:wrongpassword@localhost:3000http/1.1 401 Unauthorizedcontent-type:text/plain; Charset=utf-8www-authenticate:basic realm= "" Restricted "" content-length:13unauthorized "Gorilla Logginghandler- It records [Apache-style logs] (Http://httpd.apache.org/docs/1.3/logs.html#common)-a bit different. It uses the signature ' func ' (out IO. Writer, H http. Handler) http. Handler ', so it not only needs the next handler, but also writes the log to IO. Writer. Here is a simple example where we write the log to the ' server.log ' file: ' Bashgo get Github.com/gorilla/handlers ' "" File:main.go "" Gopackage Mainimport ("Github.com/gorilla/handlers" "Net/http" "OS") Func main () {finalhandler: = http. Handlerfunc (Final) logFile, err: = OS. OpenFile ("Server.log", OS. O_wronlY|os. O_create|os. O_append, 0666) if err! = Nil {panic (ERR)} http. Handle ("/", handlers. Logginghandler (LogFile, Finalhandler)) http. Listenandserve (":", nil)}func final (w http. Responsewriter, R *http. Request) {W.write ([]byte ("OK")} "" In this small example, our code is very clear. But what happens if we want to use Logginghandler as part of a larger middle-tier chain? We can easily get a statement that looks like this ... ' http. Handle ("/", handlers. Logginghandler (LogFile, Authhandler (Enforcexmlhandler (Finalhandler)))) ' ... That gives me a headache! One way to know is by creating a constructor (let's call it Mylogginghandler) and signing ' Func ' (http. Handler) http. Handler '. This will enable us to nest together more succinctly with the other middle layers: "' gofunc mylogginghandler (H http. Handler) http. Handler {logFile, err: = OS. OpenFile ("Server.log", OS. O_wronly|os. O_create|os. O_append, 0666) if err! = Nil {panic (err)} return handlers. Logginghandler (LogFile, h)}func main () {finalhandler: = http. Handlerfunc (final) http. Handle ("/", Mylogginghandler (Finalhandler)) http. Listenandserve (":", Nil)} "If you run this application and send some requests, your Server.log file should look like this:" ' $ cat server.log127.0.0.1--[21/oct/ 2014:18:56:43 +0100] "get/http/1.1" 2127.0.0.1--[21/oct/2014:18:56:36 +0100] "post/http/1.1" 2127.0.0.1--[21/oct/2014:18:5 6:43 +0100] "put/http/1.1" 200 2 "If you are interested, you can refer to the three middle tier handlers in this article. Note: Be aware that Gorilla Logginghandler is logging the response status (' 200 ') and response length (' 2 ') in the log. This is interesting. How does the upstream logging middle tier know the response written by our application handler? It wraps ' HTTP ' by defining its own ' Responselogger ' class. Responsewriter ', and create Custom ' reponselogger.write () ' and ' Reponselogger.writeheader () ' methods. These methods can not only write a response, but also store the size and state for later inspection. Gorilla's Logginghandler passes ' Reponselogger ' to the next handler in the chain, not the normal ' http. Responsewriter '. # # Add-on Tools (Additional tools) [Alice] written by Justinas Stankevičius (Https://github.com/justinas/alice) is a very smart and lightweight package, It provides some syntactic sugars for connecting the middle-tier handlers. On the most basic side, Alice allows you to rewrite this: ' http. Handle ("/", Mylogginghandler (Authhandler (Enforcexmlhandler (Finalhandler)))) ' For this: ' HTTP. Handle ("/", Alice. New (Mylogginghandler, Authhandler, Enforcexmlhandler). Then (Finalhandler)) ' At least in my opinion, the code can see this at a glance. But the real benefit of Alice is that it allows you to specify a handler chain and reuse it for multiple routes. Like this: "' Gostdchain: = Alice. New (Mylogginghandler, Authhandler, Enforcexmlhandler) httP.handle ("/foo", Stdchain.then (Foohandler)) http. Handle ("/bar", Stdchain.then (Barhandler)) "

Via:http://www.alexedwards.net/blog/making-and-using-middleware

Author: TIAGO Katcipis Translator: gogeof proofreading: polaris1119

This article by GCTT original compilation, go language Chinese network honor launches

This article was originally translated by GCTT and the Go Language Chinese network. Also want to join the ranks of translators, for open source to do some of their own contribution? Welcome to join Gctt!
Translation work and translations are published only for the purpose of learning and communication, translation work in accordance with the provisions of the CC-BY-NC-SA agreement, if our work has violated your interests, please contact us promptly.
Welcome to the CC-BY-NC-SA agreement, please mark and keep the original/translation link and author/translator information in the text.
The article only represents the author's knowledge and views, if there are different points of view, please line up downstairs to spit groove

334 Reads
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.