Disadvantages:
https://www.quora.com/What-are-the-advantages-and-disadvantages-of-Golang
too many boilerplate code
len("a") vs "a".length(), the latter is much IDE friendly, easier to code.
go build aSourceFile.go - create a statically linked executable file
There is another way to execute your Go code that does not create any permanent executable files—it just generates some intermediate files that are automatically deleted afterwards.
go run aSourceFile.go
- You either use a Go package or do not include it
Go has strict rules about package usage. Therefore, you cannot just include any package that you might think you will need and not use it afterwards
Using an underscore character in front of a package name in the import list will not create an error message in the compilation process, even if that package is not used in the program:
_ "os"
You can delete the intermediate files of a downloaded Go package as follows:
$ go clean -i -v -x github.com/mactsouk/go/simpleGitHub
Additionally, file descriptor 0 can be accessed as /dev/fd/0 on a macOS machine
os.Stdin, os.Stdout, and os.Stderr
The official name for := is the short assignment statement. The short assignment statement can be used in place of a var declaration with an implicit type.
The var keyword is mostly used for declaring global variables in Go programs as well as for declaring variables without an initial value. The reason for the former is that every statement that exists outside of the code of a function must begin with a keyword, such as func or var. This means that the short assignment statement cannot be used outside of a function because it is not available there.
var f *os.File
f = os.Stdin
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
fmt.Println(">", scanner.Text())
}
On macOS machines, the name of the process is syslogd(8). On the other hard, most Linux machines use rsyslogd(8)
https://www.jetbrains.com/help/go/install-and-set-up-product.html
Created a workspace where you keep projects.
Use the following layout:
https://www.quora.com/What-is-the-best-IDE-to-use-for-Go
Gofmt is a tool that automatically formats Go source code, so your code’ll look like others. Pretty convenient to read go code.
func main() {
http.HandleFunc(
"/",
func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, gophers!")
},
)
http.ListenAndServe(":8080", nil)
}
Great package management
For example, if we need a dependency, we just need to put it on top like this:
package main
import (
“encoding/json”
“net/http”
“github.com/gorilla/mux”
)
Just run “go get” and it’s over. Really. No more Maven, Gradle, Ant build fails. No makefile, build.xml and horrible stuff like that.
http://denis.papathanasiou.org/posts/2015.12.26.post.html
http://www.codeceo.com/article/go-create-window.html
https://github.com/a8m/go-lang-cheat-sheet
https://www.golang-book.com/books/intro/9
The Go Programming Language
https://github.com/adonovan/gopl.io
Package main is special. It defines a standalone executable program, not a library. Within package main the function main is also special—it’s where execution of the program begins. Whatever main does is what the program does.
GOROOT=/usr/local/go
go run helloworld.go
go build helloworld.go
program will not compile if there are missing imports or if there are unnecessary ones. This strict requirement prevents references to unused packages from accumulating as programs evolve.
Go takes a strong stance on code formatting. The gofmt tool rewrites code into the standard format, and the go tool’s fmt subcommand applies gofmt to all the files in the specified package, or the ones in the current directory by default.
A related tool, goimports, additionally manages the insertion and removal of import declarations as needed
package main
import (
"fmt"
"os"
)
func main() {
var s, sep string
for i := 1; i < len(os.Args); i++ {
s += sep + os.Args[i]
sep = " "
}
fmt.Println(s)
}
If it is not explicitly initialized, it is implicitly initialized to the zero value for its type
The := symbol is part of a short variable declaration, a statement that declares one or more variables and gives them appropriate types based on the initializer values;
func main() {
s, sep := "", "" // The first form, a short variable declaration, is the most compact, but it may be used only within a function, not for package-level variables.
for _, arg := range os.Args[1:] {
s += sep + arg
sep = " "
}
fmt.Println(s)
}
var s string
fmt.Println(strings.Join(os.Args[1:], " "))
fmt.Println(os.Args[1:])
counts := make(map[string]int)
input := bufio.NewScanner(os.Stdin)
for input.Scan() {
counts[input.Text()]++
}
// NOTE: ignoring potential errors from input.Err()
for line, n := range counts {
if n > 1 {
fmt.Printf("%d\t%s\n", n, line)
}
}
files := os.Args[1:]
if len(files) == 0 {
countLines(os.Stdin, counts)
} else {
for _, arg := range files {
f, err := os.Open(arg)
if err != nil {
fmt.Fprintf(os.Stderr, "dup2: %v\n", err)
continue
}
countLines(f, counts)
f.Close()
}
}
func countLines(f *os.File, counts map[string]int) {
input := bufio.NewScanner(f)
for input.Scan() {
counts[input.Text()]++
}
// NOTE: ignoring potential errors from input.Err()
}
import "io/ioutil"
func countLines(f *os.File, counts map[string]int) {
input := bufio.NewScanner(f)
for input.Scan() {
counts[input.Text()]++
}
// NOTE: ignoring potential errors from input.Err()
}
Goroutines and Channels
communicating sequential processes or CSP, a model of concurrency in which values are passed between independent activities (goroutines) but variables are for the most part confined to a single activity.
func main() {
go spinner(100 * time.Millisecond)
const n = 45
fibN := fib(n) // slow
fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN)
}
func spinner(delay time.Duration) {
for {
for _, r := range `-\|/` {
fmt.Printf("\r%c", r)
time.Sleep(delay)
}
}
}
func fib(x int) int {
if x < 2 {
return x
}
return fib(x-1) + fib(x-2)
}
func handleConn(c net.Conn) {
defer c.Close()
for {
_, err := io.WriteString(c, time.Now().Format("15:04:05\n"))
if err != nil {
return // e.g., client disconnected
}
time.Sleep(1 * time.Second)
}
}
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
//!+
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err) // e.g., connection aborted
continue
}
go handleConn(conn) // handle connections concurrently
}
//!-
}
Channels
If goroutines are the activities of a concurrent Go program, channels are the connections between them. A channel is a communication mechanism that lets one goroutine send values to another goroutine. Each channel is a conduit for values of a particular type, called the channel’s element type.
ch <- x // a send statement
x = <-ch // a receive expression in an assignment statement
<-ch // a receive statement; result is discarded
UNBUFFERED CHANNELS
A send operation on an unbuffered channel blocks the sending goroutine until another goroutine executes a corresponding receive on the same channel, at which point the value is transmitted and both goroutines may continue. Conversely, if the receive operation was attempted first, the receiving goroutine is blocked until another goroutine performs a send on the same channel.
func main() {
conn, err := net.Dial("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
done := make(chan struct{})
go func() {
io.Copy(os.Stdout, conn) // NOTE: ignoring errors
log.Println("done")
done <- struct{}{} // signal the main goroutine
}()
mustCopy(conn, os.Stdin)
conn.Close()
<-done // wait for background goroutine to finish
}
PIPELINES
func main() {
naturals := make(chan int)
squares := make(chan int)
// Counter
go func() {
for x := 0; ; x++ {
naturals <- x
}
}()
// Squarer
go func() {
for {
x := <-naturals
squares <- x * x
}
}()
// Printer (in main goroutine)
for {
fmt.Println(<-squares)
}
}
func main() {
naturals := make(chan int)
squares := make(chan int)
// Counter
go func() {
for x := 0; x < 100; x++ {
naturals <- x
}
close(naturals)
}()
// Squarer
go func() {
for x := range naturals {
squares <- x * x
}
close(squares)
}()
// Printer (in main goroutine)
for x := range squares {
fmt.Println(x)
}
}
The type chan<- int, a send-only channel of int, allows sends but not receives. Conversely, the type <-chan int, a receive-only channel of int, allows receives but not sends. (The position of the <- arrow relative to the chan keyword is a mnemonic.)
func mirroredQuery() string {
responses := make(chan string, 3)
go func() { responses <- request("asia.gopl.io") }()
go func() { responses <- request("europe.gopl.io") }()
go func() { responses <- request("americas.gopl.io") }()
return <-responses // return the quickest response
}
func request(hostname string) (response string) { /* ... */ }
Had we used an unbuffered channel, the two slower goroutines would have gotten stuck trying to send their responses on a channel from which no goroutine will ever receive. This situation, called a goroutine leak, would be a bug. Unlike garbage variables, leaked goroutines are not automatically collected, so it is important to make sure that goroutines terminate themselves when no longer needed.
func makeThumbnails3(filenames []string) {
ch := make(chan struct{})
for _, f := range filenames {
go func(f string) {
thumbnail.ImageFile(f) // NOTE: ignoring errors
ch <- struct{}{}
}(f)
}
// Wait for goroutines to complete.
for range filenames {
<-ch
}
}
Level Up Your Web Apps With Go
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Server", "Go Server")
fmt.Fprintf(w, `
<html>
<body>
Hello Gopher
</body>
</html>`)
})
http.ListenAndServe(":3000", nil)
}
ServeMux is a request multiplexer, which handles matching an incoming request’s URL against the registered handlers and calling the handler for the pattern that most closely matches the URL. The http package creates a default ServeMux instance for you, and the http.HandleFunc function is actually a shortcut to the (*ServeMux) HandleFunc method on that default ServeMux.
There are two types of patterns: a path and a subtree. A path is defined without a trailing backslash (/), and refers to an explicit path. Subtrees are designed to match the start of a path, and include the trailing /
THE HANDLER INTERFACE
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
// UptimeHandler writes the number of seconds since starting to the response.
type UptimeHandler struct {
Started time.Time
}
func NewUptimeHandler() UptimeHandler {
return UptimeHandler{Started: time.Now()}
}
func (h UptimeHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(
w,
fmt.Sprintf("Current Uptime: %s", time.Since(h.Started)),
)
}
// SecretTokenHandler secures a request with a secret token.
type SecretTokenHandler struct {
next http.Handler
secret string
}
// ServeHTTP makes SecretTokenHandler implement the http.Handler interface.
func (h SecretTokenHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Check the query string for the secret token
if req.URL.Query().Get("secret_token") == h.secret {
// The secret token matched, call the next handler
h.next.ServeHTTP(w, req)
} else {
// No match, return a 404 Not Found response
http.NotFound(w, req)
}
}
func main() {
http.Handle("/", SecretTokenHandler{
next: NewUptimeHandler(),
secret: "MySecret",
})
http.ListenAndServe(":3000", nil)
}
https://www.quora.com/What-are-the-advantages-and-disadvantages-of-Golang
- Built-in concurrency. There are no heavy threads but channels.
Compiled
- There is no VM. It compiles directly to the machine code (if we exclude Go’s intermediary assembly) which is fast, fast and fast (did I say fast?).
- Fast compilation. The programming language design is built for fast compilation in mind from the beginning.
- Compiles cross-platform to OS X, Linux, Windows, and many others.
- Creates only one executable file output after the compilation without any dependencies, so that you can upload it anywhere which Go supports and just run it. Or just compile it there after you upload the code. No dependency hell.
Safe
- Strong and static typed.
- Err everywhere. You need to check errors for each of the error producing function in your code explicitly. However, I love the explicitness of Go programs. In the upcoming years maybe we’d find an elegant solution for this. There are some proposals to change error handling.
len("a") vs "a".length(), the latter is much IDE friendly, easier to code.
go build aSourceFile.go - create a statically linked executable file
There is another way to execute your Go code that does not create any permanent executable files—it just generates some intermediate files that are automatically deleted afterwards.
go run aSourceFile.go
- You either use a Go package or do not include it
Go has strict rules about package usage. Therefore, you cannot just include any package that you might think you will need and not use it afterwards
Using an underscore character in front of a package name in the import list will not create an error message in the compilation process, even if that package is not used in the program:
_ "os"
Go requires the use of semicolons as statement terminators in many contexts, and the compiler automatically inserts the required semicolons when it thinks that they are necessary. Therefore, putting the opening brace ({) in its own line will make the Go compiler insert a semicolon at the end of the previous line (func main()), which produces the error message.
You can delete the intermediate files of a downloaded Go package as follows:
$ go clean -i -v -x github.com/mactsouk/go/simpleGitHub
Additionally, file descriptor 0 can be accessed as /dev/fd/0 on a macOS machine
os.Stdin, os.Stdout, and os.Stderr
The official name for := is the short assignment statement. The short assignment statement can be used in place of a var declaration with an implicit type.
The var keyword is mostly used for declaring global variables in Go programs as well as for declaring variables without an initial value. The reason for the former is that every statement that exists outside of the code of a function must begin with a keyword, such as func or var. This means that the short assignment statement cannot be used outside of a function because it is not available there.
var f *os.File
f = os.Stdin
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
fmt.Println(">", scanner.Text())
}
On macOS machines, the name of the process is syslogd(8). On the other hard, most Linux machines use rsyslogd(8)
https://www.jetbrains.com/help/go/install-and-set-up-product.html
Created a workspace where you keep projects.
Use the following layout:
- work
- bin
- pkg
- src
- github.com
- user_name
- project1
- project2
- user_name
- github.com
- Create a main .go file and make sure package name is main, and a main function exists.
I think it's easier to have one
$GOPATH
per project, that way you can have different versions of the same package for different projects, and update the packages as needed.
With a central repository, it's difficult to update a package as you might break an unrelated project when doing so (if the package update has breaking changes or new bugs).
- Microsoft’s Visual Studio Code with the vscode-go extension, it features:
TL;DR: We recommend just using the
https://medium.com/@PierreZ/why-you-really-should-give-golang-a-try-6b577092d725#.65mg3r3h3net/http
package in the standard library to start. And if you want help with request routing we recommend looking at Gorilla and Gocraft/web. Both Revel andMartini have too much dependency injection and other magic to make us feel comfortable. Gorilla is the most minimal.Gofmt is a tool that automatically formats Go source code, so your code’ll look like others. Pretty convenient to read go code.
func main() {
http.HandleFunc(
"/",
func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, gophers!")
},
)
http.ListenAndServe(":8080", nil)
}
Great package management
For example, if we need a dependency, we just need to put it on top like this:
package main
import (
“encoding/json”
“net/http”
“github.com/gorilla/mux”
)
Just run “go get” and it’s over. Really. No more Maven, Gradle, Ant build fails. No makefile, build.xml and horrible stuff like that.
I recently participated in the following programming challenge:
Write a program to test user reflexes: when it runs, it waits a random amount of time between 1 and 10 seconds, and prints "GO!" as a prompt.
It then expects the user to hit the enter key, and times how fast enter is pressed, in milliseconds.
If the enter key is pressed before the "GO!" prompt appears, the program prints "FAIL".
It then expects the user to hit the enter key, and times how fast enter is pressed, in milliseconds.
If the enter key is pressed before the "GO!" prompt appears, the program prints "FAIL".
package main | |
import ( | |
"bufio" | |
"fmt" | |
"math/rand" | |
"os" | |
"time" | |
) | |
func init() { | |
rand.Seed(time.Now().UnixNano()) | |
} | |
func main() { | |
channel := make(chan time.Time) | |
go func() { | |
reader := bufio.NewReader(os.Stdin) | |
reader.ReadString('\n') | |
channel <- time.Now() | |
}() | |
time.Sleep(time.Second * time.Duration(rand.Intn(11))) | |
paused := time.Now() | |
fmt.Println("GO!") | |
entered := <- channel | |
if paused.Sub(entered) > 0 { | |
fmt.Println("FAIL") | |
} else { | |
fmt.Printf("%v\n", time.Since(entered)) | |
} | |
} |
Go语言富有表现力,简洁,干净,高效。它的并发机制使得它可以很容易地编写让多核和网络化的计算机发挥最大作用的程序,同时其新型的类型系统使得灵活且模块化的程序结构变得可能。Go语言能够快速编译为机器码,并具备垃圾回收的便利以及运行时反射的能力。这是一个快速、静态类型的编译语言,并且让人感觉像动态类型和解释型的语言。
Go源文件中的第一条语句必须是包的名称。由于我们要写的程序是一个可执行程序,所以包的名称必须是main。它告诉Go编译器这个包应编译为可执行程序,而不是一个共享库:
package main
https://github.com/a8m/go-lang-cheat-sheet
Functions As Values And Closures
func main() {
// assign a function to a name
add := func(a, b int) int {
return a + b
}
// use the name to call the function
fmt.Println(add(3, 4))
}
// Closures, lexically scoped: Functions can access values that were
// in scope when defining the function
func scope() func() int{
outer_var := 2
foo := func() int { return outer_var}
return foo
}
func another_scope() func() int{
// won't compile because outer_var and foo not defined in this scope
outer_var = 444
return foo
}
// Closures: don't mutate outer vars, instead redefine them!
func outer() (func() int, int) {
outer_var := 2
inner := func() int {
outer_var += 99 // attempt to mutate outer_var from outer scope
return outer_var // => 101 (but outer_var is a newly redefined
// variable visible only inside inner)
}
return inner, outer_var // => 101, 2 (outer_var is still 2, not mutated by foo!)
}
A struct is a type which contains named fields. For example we could represent a Circle like this:
type Circle struct { x float64 y float64 r float64 }
The
Methodtype
keyword introduces a new type. It's followed by the name of the type (Circle
), the keyword struct
to indicate that we are defining a struct
type and a list of fields inside of curly braces.func (c *Circle) area() float64 { return math.Pi * c.r*c.r }
In between the keyword
Embedded Typesfunc
and the name of the function we've added a “receiver”. The receiver is like a parameter – it has a name and a type.
This would work, but we would rather say an Android is a Person, rather than an Android has a Person. Go supports relationships like this by using an embedded type. Also known as anonymous fields, embedded types look like this:
type Android struct { Person Model string }
We use the type (
Person
) and don't give it a name. When defined this way the Person
struct can be accessed using the type name:a := new(Android) a.Person.Talk()
But we can also call any
Person
methods directly on the Android
:a := new(Android) a.Talk()
type Shape interface { area() float64 }
https://github.com/adonovan/gopl.io
Package main is special. It defines a standalone executable program, not a library. Within package main the function main is also special—it’s where execution of the program begins. Whatever main does is what the program does.
GOROOT=/usr/local/go
go run helloworld.go
go build helloworld.go
program will not compile if there are missing imports or if there are unnecessary ones. This strict requirement prevents references to unused packages from accumulating as programs evolve.
Go takes a strong stance on code formatting. The gofmt tool rewrites code into the standard format, and the go tool’s fmt subcommand applies gofmt to all the files in the specified package, or the ones in the current directory by default.
A related tool, goimports, additionally manages the insertion and removal of import declarations as needed
package main
import (
"fmt"
"os"
)
func main() {
var s, sep string
for i := 1; i < len(os.Args); i++ {
s += sep + os.Args[i]
sep = " "
}
fmt.Println(s)
}
If it is not explicitly initialized, it is implicitly initialized to the zero value for its type
The := symbol is part of a short variable declaration, a statement that declares one or more variables and gives them appropriate types based on the initializer values;
func main() {
s, sep := "", "" // The first form, a short variable declaration, is the most compact, but it may be used only within a function, not for package-level variables.
for _, arg := range os.Args[1:] {
s += sep + arg
sep = " "
}
fmt.Println(s)
}
var s string
fmt.Println(strings.Join(os.Args[1:], " "))
fmt.Println(os.Args[1:])
counts := make(map[string]int)
input := bufio.NewScanner(os.Stdin)
for input.Scan() {
counts[input.Text()]++
}
// NOTE: ignoring potential errors from input.Err()
for line, n := range counts {
if n > 1 {
fmt.Printf("%d\t%s\n", n, line)
}
}
files := os.Args[1:]
if len(files) == 0 {
countLines(os.Stdin, counts)
} else {
for _, arg := range files {
f, err := os.Open(arg)
if err != nil {
fmt.Fprintf(os.Stderr, "dup2: %v\n", err)
continue
}
countLines(f, counts)
f.Close()
}
}
func countLines(f *os.File, counts map[string]int) {
input := bufio.NewScanner(f)
for input.Scan() {
counts[input.Text()]++
}
// NOTE: ignoring potential errors from input.Err()
}
import "io/ioutil"
func countLines(f *os.File, counts map[string]int) {
input := bufio.NewScanner(f)
for input.Scan() {
counts[input.Text()]++
}
// NOTE: ignoring potential errors from input.Err()
}
Goroutines and Channels
communicating sequential processes or CSP, a model of concurrency in which values are passed between independent activities (goroutines) but variables are for the most part confined to a single activity.
func main() {
go spinner(100 * time.Millisecond)
const n = 45
fibN := fib(n) // slow
fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN)
}
func spinner(delay time.Duration) {
for {
for _, r := range `-\|/` {
fmt.Printf("\r%c", r)
time.Sleep(delay)
}
}
}
func fib(x int) int {
if x < 2 {
return x
}
return fib(x-1) + fib(x-2)
}
func handleConn(c net.Conn) {
defer c.Close()
for {
_, err := io.WriteString(c, time.Now().Format("15:04:05\n"))
if err != nil {
return // e.g., client disconnected
}
time.Sleep(1 * time.Second)
}
}
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
//!+
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err) // e.g., connection aborted
continue
}
go handleConn(conn) // handle connections concurrently
}
//!-
}
Channels
If goroutines are the activities of a concurrent Go program, channels are the connections between them. A channel is a communication mechanism that lets one goroutine send values to another goroutine. Each channel is a conduit for values of a particular type, called the channel’s element type.
ch <- x // a send statement
x = <-ch // a receive expression in an assignment statement
<-ch // a receive statement; result is discarded
UNBUFFERED CHANNELS
A send operation on an unbuffered channel blocks the sending goroutine until another goroutine executes a corresponding receive on the same channel, at which point the value is transmitted and both goroutines may continue. Conversely, if the receive operation was attempted first, the receiving goroutine is blocked until another goroutine performs a send on the same channel.
func main() {
conn, err := net.Dial("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
done := make(chan struct{})
go func() {
io.Copy(os.Stdout, conn) // NOTE: ignoring errors
log.Println("done")
done <- struct{}{} // signal the main goroutine
}()
mustCopy(conn, os.Stdin)
conn.Close()
<-done // wait for background goroutine to finish
}
PIPELINES
func main() {
naturals := make(chan int)
squares := make(chan int)
// Counter
go func() {
for x := 0; ; x++ {
naturals <- x
}
}()
// Squarer
go func() {
for {
x := <-naturals
squares <- x * x
}
}()
// Printer (in main goroutine)
for {
fmt.Println(<-squares)
}
}
func main() {
naturals := make(chan int)
squares := make(chan int)
// Counter
go func() {
for x := 0; x < 100; x++ {
naturals <- x
}
close(naturals)
}()
// Squarer
go func() {
for x := range naturals {
squares <- x * x
}
close(squares)
}()
// Printer (in main goroutine)
for x := range squares {
fmt.Println(x)
}
}
The type chan<- int, a send-only channel of int, allows sends but not receives. Conversely, the type <-chan int, a receive-only channel of int, allows receives but not sends. (The position of the <- arrow relative to the chan keyword is a mnemonic.)
func mirroredQuery() string {
responses := make(chan string, 3)
go func() { responses <- request("asia.gopl.io") }()
go func() { responses <- request("europe.gopl.io") }()
go func() { responses <- request("americas.gopl.io") }()
return <-responses // return the quickest response
}
func request(hostname string) (response string) { /* ... */ }
Had we used an unbuffered channel, the two slower goroutines would have gotten stuck trying to send their responses on a channel from which no goroutine will ever receive. This situation, called a goroutine leak, would be a bug. Unlike garbage variables, leaked goroutines are not automatically collected, so it is important to make sure that goroutines terminate themselves when no longer needed.
func makeThumbnails3(filenames []string) {
ch := make(chan struct{})
for _, f := range filenames {
go func(f string) {
thumbnail.ImageFile(f) // NOTE: ignoring errors
ch <- struct{}{}
}(f)
}
// Wait for goroutines to complete.
for range filenames {
<-ch
}
}
Level Up Your Web Apps With Go
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Server", "Go Server")
fmt.Fprintf(w, `
<html>
<body>
Hello Gopher
</body>
</html>`)
})
http.ListenAndServe(":3000", nil)
}
ServeMux is a request multiplexer, which handles matching an incoming request’s URL against the registered handlers and calling the handler for the pattern that most closely matches the URL. The http package creates a default ServeMux instance for you, and the http.HandleFunc function is actually a shortcut to the (*ServeMux) HandleFunc method on that default ServeMux.
There are two types of patterns: a path and a subtree. A path is defined without a trailing backslash (/), and refers to an explicit path. Subtrees are designed to match the start of a path, and include the trailing /
THE HANDLER INTERFACE
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
// UptimeHandler writes the number of seconds since starting to the response.
type UptimeHandler struct {
Started time.Time
}
func NewUptimeHandler() UptimeHandler {
return UptimeHandler{Started: time.Now()}
}
func (h UptimeHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(
w,
fmt.Sprintf("Current Uptime: %s", time.Since(h.Started)),
)
}
// SecretTokenHandler secures a request with a secret token.
type SecretTokenHandler struct {
next http.Handler
secret string
}
// ServeHTTP makes SecretTokenHandler implement the http.Handler interface.
func (h SecretTokenHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Check the query string for the secret token
if req.URL.Query().Get("secret_token") == h.secret {
// The secret token matched, call the next handler
h.next.ServeHTTP(w, req)
} else {
// No match, return a 404 Not Found response
http.NotFound(w, req)
}
}
func main() {
http.Handle("/", SecretTokenHandler{
next: NewUptimeHandler(),
secret: "MySecret",
})
http.ListenAndServe(":3000", nil)
}