## 1.5. 獲取URL
對于很多現代應用來說,訪問互聯網上的信息和訪問本地文件系統一樣重要。Go語言在net這個強大package的幫助下提供了一系列的package來做這件事情,使用這些包可以更簡單地用網絡收發信息,還可以建立更底層的網絡連接,編寫服務器程序。在這些情景下,Go語言原生的并發特性(在第八章中會介紹)顯得尤其好用。
為了最簡單地展示基于HTTP獲取信息的方式,下面給出一個示例程序fetch,這個程序將獲取對應的url,并將其源文本打印出來;這個例子的靈感來源于curl工具(譯注:unix下的一個用來發http請求的工具,具體可以man curl)。當然,curl提供的功能更為復雜豐富,這里只編寫最簡單的樣例。這個樣例之后還會多次被用到。
<u><i>gopl.io/ch1/fetch</i></u>
```go
// Fetch prints the content found at a URL.
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
)
func main() {
for _, url := range os.Args[1:] {
resp, err := http.Get(url)
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
os.Exit(1)
}
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
os.Exit(1)
}
fmt.Printf("%s", b)
}
}
```
這個程序從兩個package中導入了函數,net/http和io/ioutil包,http.Get函數是創建HTTP請求的函數,如果獲取過程沒有出錯,那么會在resp這個結構體中得到訪問的請求結果。resp的Body字段包括一個可讀的服務器響應流。ioutil.ReadAll函數從response中讀取到全部內容;將其結果保存在變量b中。resp.Body.Close關閉resp的Body流,防止資源泄露,Printf函數會將結果b寫出到標準輸出流中。
```
$ go build gopl.io/ch1/fetch
$ ./fetch http://gopl.io
<html>
<head>
<title>The Go Programming Language</title>title>
...
```
HTTP請求如果失敗了的話,會得到下面這樣的結果:
```
$ ./fetch http://bad.gopl.io
fetch: Get http://bad.gopl.io: dial tcp: lookup bad.gopl.io: no such host
```
譯注:在大天朝的網絡環境下很容易重現這種錯誤,下面是Windows下運行得到的錯誤信息:
```
$ go run main.go http://gopl.io
fetch: Get http://gopl.io: dial tcp: lookup gopl.io: getaddrinfow: No such host is known.
```
無論哪種失敗原因,我們的程序都用了os.Exit函數來終止進程,并且返回一個status錯誤碼,其值為1。
**練習 1.7:** 函數調用io.Copy(dst, src)會從src中讀取內容,并將讀到的結果寫入到dst中,使用這個函數替代掉例子中的ioutil.ReadAll來拷貝響應結構體到os.Stdout,避免申請一個緩沖區(例子中的b)來存儲。記得處理io.Copy返回結果中的錯誤。
**練習 1.8:** 修改fetch這個范例,如果輸入的url參數沒有 `http://` 前綴的話,為這個url加上該前綴。你可能會用到strings.HasPrefix這個函數。
**練習 1.9:** 修改fetch打印出HTTP協議的狀態碼,可以從resp.Status變量得到該狀態碼。
- 前言
- Go語言起源
- Go語言項目
- 本書的組織
- 更多的信息
- 致謝
- 入門
- Hello, World
- 命令行參數
- 查找重復的行
- GIF動畫
- 獲取URL
- 并發獲取多個URL
- Web服務
- 本章要點
- 程序結構
- 命名
- 聲明
- 變量
- 賦值
- 類型
- 包和文件
- 作用域
- 基礎數據類型
- 整型
- 浮點數
- 復數
- 布爾型
- 字符串
- 常量
- 復合數據類型
- 數組
- Slice
- Map
- 結構體
- JSON
- 文本和HTML模板
- 函數
- 函數聲明
- 遞歸
- 多返回值
- 錯誤
- 函數值
- 匿名函數
- 可變參數
- Deferred函數
- Panic異常
- Recover捕獲異常
- 方法
- 方法聲明
- 基于指針對象的方法
- 通過嵌入結構體來擴展類型
- 方法值和方法表達式
- 示例: Bit數組
- 封裝
- 接口
- 接口是合約
- 接口類型
- 實現接口的條件
- flag.Value接口
- 接口值
- sort.Interface接口
- http.Handler接口
- error接口
- 示例: 表達式求值
- 類型斷言
- 基于類型斷言識別錯誤類型
- 通過類型斷言查詢接口
- 類型分支
- 示例: 基于標記的XML解碼
- 補充幾點
- Goroutines和Channels
- Goroutines
- 示例: 并發的Clock服務
- 示例: 并發的Echo服務
- Channels
- 并發的循環
- 示例: 并發的Web爬蟲
- 基于select的多路復用
- 并發的退出
- 示例: 聊天服務
- 基于共享變量的并發
- 競爭條件
- sync.Mutex互斥鎖
- sync.RWMutex讀寫鎖
- 內存同步
- 競爭條件檢測
- 示例: 并發的非阻塞緩存
- Goroutines和線程
- 包和工具
- 包簡介
- 導入路徑
- 包聲明
- 導入聲明
- 包的匿名導入
- 包和命名
- 工具
- 測試
- go test
- 測試函數
- 測試覆蓋率
- 基準測試
- 剖析
- 示例函數
- 反射
- 為何需要反射?
- reflect.Type和reflect.Value
- Display遞歸打印
- 示例: 編碼S表達式
- 通過reflect.Value修改值
- 示例: 解碼S表達式
- 顯示一個類型的方法集
- 幾點忠告
- 底層編程
- unsafe.Sizeof, Alignof 和 Offsetof
- unsafe.Pointer
- 示例: 深度相等判斷
- 通過cgo調用C代碼
- 幾點忠告
- 附錄
- 附錄A:原文勘誤
- 附錄B:作者譯者
- 附錄C:譯文授權
- 附錄D:其它語言