Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
頁(yè)面的重定向想必大家都知道,如果你是用過(guò) koa ,那么通過(guò)如下代碼,就能讓網(wǎng)頁(yè)跳轉(zhuǎn)至指定頁(yè)面:
或者你用過(guò) java 的重定向:
那么框架內(nèi)部到底是怎么實(shí)現(xiàn)頁(yè)面重定向的呢?這篇小短文可能會(huì)讓你更加清楚其中的一些原理。
概念
當(dāng)你在瀏覽器訪問(wèn) A 地址時(shí),你會(huì)看到瀏覽器會(huì)刷新進(jìn)度條,地址欄被更新為 B 地址。這就是 URL 重定向。
這樣類(lèi)似的技術(shù),就是由 Http 協(xié)議定義的。重定向操作是由服務(wù)端向客戶(hù)端發(fā)送特定的響應(yīng)狀態(tài)來(lái)觸發(fā),這類(lèi)狀態(tài)有個(gè)專(zhuān)門(mén)的狀態(tài)碼:3xx ,當(dāng)瀏覽器接收后,將會(huì)進(jìn)行頁(yè)面的重定向,即地址發(fā)生了跳轉(zhuǎn)。
幾種狀態(tài)碼
302 和 301 的區(qū)別
上面列了許多 3xx 的狀態(tài)碼,但平時(shí)我們最多用的是 301 和 302,這里就詳細(xì)解釋這兩個(gè)的區(qū)別:
301
永久重定向。如果 A 地址被 301 到 B 地址后,后續(xù)再次請(qǐng)求 A 地址的話(huà),瀏覽器就會(huì)默認(rèn)首次請(qǐng)求 B 地址,不會(huì)再有 A 地址的請(qǐng)求。
等看到第一次訪問(wèn) /api/books/111 時(shí),頁(yè)面被重定向到 /api/books,瀏覽器發(fā)送兩次請(qǐng)求。但后續(xù)再次請(qǐng)求 /api/books/111 時(shí),直接請(qǐng)求了 /api/books 。
所以通常該狀態(tài)碼用于網(wǎng)站重構(gòu),告知搜索引擎你以后訪問(wèn)我 301 重定向的地址。
302
相反,302 就是臨時(shí)重定向。
平時(shí)我們登陸頁(yè)面的授權(quán)跳轉(zhuǎn)都是基于此狀態(tài)碼。因?yàn)榭蛻?hù)端訪問(wèn)的頁(yè)面是臨時(shí)不可用,滿(mǎn)足了某些條件后,可以繼續(xù)使用。
對(duì)比 301 的請(qǐng)求,能看到兩次 /api/books/222 的請(qǐng)求都被“記錄在案”。
來(lái)看下 koa 中 response 的 redirect 的重定向源碼:
能看到在這段重定向的代碼中,分別設(shè)置了 location 和 狀態(tài)碼,依靠他們來(lái)完成重定向的功能。
當(dāng)然我們可以自己簡(jiǎn)單的實(shí)現(xiàn)一個(gè)服務(wù)器重定向功能:
如果你只是單純的使用框架的 redirect api,而不清楚其內(nèi)部的原理,可能這篇會(huì)幫助你了解更多些。
畢竟這是 Http 的基礎(chǔ),會(huì)讓你對(duì)瀏覽器的重定向有個(gè)概念。
一位“前端工程師”,樂(lè)于實(shí)踐,并分享前端開(kāi)發(fā)經(jīng)驗(yàn)。
如果有問(wèn)題或者想法,歡迎各位評(píng)論留言,愿大家共同進(jìn)步。
關(guān)注【前端雨爸】,查閱更多前端技術(shù)心得。
package main
import (
"crypto/sha1"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"strconv"
)
//加載首頁(yè)的頁(yè)面
func indexHandler(w http.ResponseWriter, r *http.Request) {
data, err :=ioutil.ReadFile("./static/index.html")
if err !=nil {
io.WriteString(w, "internel server error")
return
}
io.WriteString(w, string(data))
}
//加載首頁(yè)的第二種方式(頁(yè)面跳轉(zhuǎn))
func homeHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/static/index.html", http.StatusFound)
return
}
//接收表單參數(shù)
func userHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm() //解析參數(shù),默認(rèn)是不會(huì)解析的
if r.Method=="GET" { //GET請(qǐng)求的方法
username :=r.Form.Get("username") //必須使用雙引號(hào), 注意: 這里的Get 不是指只接收GET請(qǐng)求的參數(shù), 而是獲取參數(shù)
password :=r.Form.Get("password")
// limitCnt, _ :=strconv.Atoi(r.Form.Get("limit")) //字符串轉(zhuǎn)整數(shù)的接收方法
//io.WriteString(w, username+":"+password)
w.Write([]byte(username + ":" + password))
} else if r.Method=="POST" { //POST請(qǐng)求的方法
username :=r.Form.Get("username") //必須使用雙引號(hào), 注意: POST請(qǐng)求, 也是用Get方法來(lái)接收
password :=r.Form.Get("password")
//io.WriteString(w, username+":"+password)
w.Write([]byte(username + ":" + password))
}
}
//文件上傳
func uploadHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm() //解析參數(shù),默認(rèn)是不會(huì)解析的
if r.Method=="GET" { //GET請(qǐng)求的方法
data, err :=ioutil.ReadFile("./static/upload.html")
if err !=nil {
io.WriteString(w, "internel server error")
return
}
io.WriteString(w, string(data))
} else if r.Method=="POST" { //POST請(qǐng)求的方法
// 接收文件流及存儲(chǔ)到本地目錄
file, head, err :=r.FormFile("image") //接收文件域的方法
if err !=nil {
fmt.Printf("Failed to get data, err:%s\n", err.Error())
return
}
defer file.Close()
newFile, err :=os.Create("C:/tmp/" + head.Filename) //創(chuàng)建文件
if err !=nil {
fmt.Printf("Failed to create file, err:%s\n", err.Error())
return
}
defer newFile.Close()
FileSize, err :=io.Copy(newFile, file) //拷貝文件
if err !=nil {
fmt.Printf("Failed to save data into file, err:%s\n", err.Error())
//http.Redirect(w, r, "/static/index.html", http.StatusFound) //重定向的方法
return
}
//文件的sha1的值
io.WriteString(w, "上傳成功"+"\r\n文件的大小:"+strconv.FormatInt(FileSize, 10)+"\r\n文件的sha1:"+fileSha1(newFile)) //int64轉(zhuǎn)換成string
}
}
// downloadHandler : 文件下載接口
func downloadHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
filename :=r.Form.Get("filename")
f, err :=os.Open("C:/tmp/" + filename)
if err !=nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
defer f.Close()
data, err :=ioutil.ReadAll(f) //ReadAll從r讀取數(shù)據(jù)直到EOF或遇到error
if err !=nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/octect-stream")
w.Header().Set("content-disposition", "attachment; filename=\""+filename+"\"")
w.Write(data)
}
//獲取文件的sha1值
func fileSha1(file *os.File) string {
_sha1 :=sha1.New() //返回一個(gè)新的使用SHA1校驗(yàn)的hash.Hash接口
io.Copy(_sha1, file) //將src的數(shù)據(jù)拷貝到dst,直到在src上到達(dá)EOF或發(fā)生錯(cuò)誤。返回拷貝的字節(jié)數(shù)和遇到的第一個(gè)錯(cuò)誤。
return hex.EncodeToString(_sha1.Sum(nil)) //nil 等同于 []byte("")
}
func main() {
// 靜態(tài)資源處理
http.Handle("/static/",
http.StripPrefix("/static/",
http.FileServer(http.Dir("./static"))))
// 動(dòng)態(tài)接口路由設(shè)置
http.HandleFunc("/index", indexHandler)
http.HandleFunc("/home", homeHandler)
http.HandleFunc("/user", userHandler)
http.HandleFunc("/upload", uploadHandler)
http.HandleFunc("/download", downloadHandler)
// 監(jiān)聽(tīng)端口
fmt.Println("上傳服務(wù)正在啟動(dòng), 監(jiān)聽(tīng)端口:8080...")
err :=http.ListenAndServe(":8080", nil)
if err !=nil {
fmt.Printf("Failed to start server, err:%s", err.Error())
}
}
tatic/upload.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<form action="/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="image" value="upload" />
<input type="submit" value="上傳"/>
</form>
</body>
</html>
static/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<p>這是首頁(yè)的內(nèi)容</p>
</body>
</html>
D:\work\src>go run main.go
上傳服務(wù)正在啟動(dòng), 監(jiān)聽(tīng)端口:8080...
http://localhost:8080/download?filename=redis.png
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。