整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

http 页面重定向 redirect

http 页面重定向 redirect

页面的重定向想必大家都知道,如果你是用过 koa ,那么通过如下代码,就能让网页跳转至指定页面:

或者你用过 java 的重定向:

那么框架内部到底是怎么实现页面重定向的呢?这篇小短文可能会让你更加清楚其中的一些原理。

重定向


概念

当你在浏览器访问 A 地址时,你会看到浏览器会刷新进度条,地址栏被更新为 B 地址。这就是 URL 重定向。

这样类似的技术,就是由 Http 协议定义的。重定向操作是由服务端向客户端发送特定的响应状态来触发,这类状态有个专门的状态码:3xx ,当浏览器接收后,将会进行页面的重定向,即地址发生了跳转。


几种状态码


302 和 301 的区别

上面列了许多 3xx 的状态码,但平时我们最多用的是 301 和 302,这里就详细解释这两个的区别:

301

永久重定向。如果 A 地址被 301 到 B 地址后,后续再次请求 A 地址的话,浏览器就会默认首次请求 B 地址,不会再有 A 地址的请求。

等看到第一次访问 /api/books/111 时,页面被重定向到 /api/books,浏览器发送两次请求。但后续再次请求 /api/books/111 时,直接请求了 /api/books 。

所以通常该状态码用于网站重构,告知搜索引擎你以后访问我 301 重定向的地址。

302

相反,302 就是临时重定向。

平时我们登陆页面的授权跳转都是基于此状态码。因为客户端访问的页面是临时不可用,满足了某些条件后,可以继续使用。

对比 301 的请求,能看到两次 /api/books/222 的请求都被“记录在案”。

koa 中的重定向

来看下 koa 中 response 的 redirect 的重定向源码:

能看到在这段重定向的代码中,分别设置了 location 和 状态码,依靠他们来完成重定向的功能。

当然我们可以自己简单的实现一个服务器重定向功能:

总结

如果你只是单纯的使用框架的 redirect api,而不清楚其内部的原理,可能这篇会帮助你了解更多些。

毕竟这是 Http 的基础,会让你对浏览器的重定向有个概念。

关于我

一位“前端工程师”,乐于实践,并分享前端开发经验。

如果有问题或者想法,欢迎各位评论留言,愿大家共同进步。

关注【前端雨爸】,查阅更多前端技术心得。

package main
import (
    "crypto/sha1"
    "encoding/hex"
    "fmt"
    "io"
    "io/ioutil"
    "net/http"
    "os"
    "strconv"
)
//加载首页的页面
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))
}
//加载首页的第二种方式(页面跳转)
func homeHandler(w http.ResponseWriter, r *http.Request) {
    http.Redirect(w, r, "/static/index.html", http.StatusFound)
    return
}
//接收表单参数
func userHandler(w http.ResponseWriter, r *http.Request) {
    r.ParseForm() //解析参数,默认是不会解析的
    if r.Method=="GET" { //GET请求的方法
        username :=r.Form.Get("username") //必须使用双引号, 注意: 这里的Get 不是指只接收GET请求的参数, 而是获取参数
        password :=r.Form.Get("password")
        // limitCnt, _ :=strconv.Atoi(r.Form.Get("limit")) //字符串转整数的接收方法
        //io.WriteString(w, username+":"+password)
        w.Write([]byte(username + ":" + password))
    } else if r.Method=="POST" { //POST请求的方法
        username :=r.Form.Get("username") //必须使用双引号, 注意: POST请求, 也是用Get方法来接收
        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() //解析参数,默认是不会解析的
    if r.Method=="GET" { //GET请求的方法
    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请求的方法
    // 接收文件流及存储到本地目录
    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) //创建文件
    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转换成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读取数据直到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() //返回一个新的使用SHA1校验的hash.Hash接口
    io.Copy(_sha1, file) //将src的数据拷贝到dst,直到在src上到达EOF或发生错误。返回拷贝的字节数和遇到的第一个错误。
    return hex.EncodeToString(_sha1.Sum(nil)) //nil 等同于 []byte("")
}
func main() {
    // 静态资源处理
    http.Handle("/static/",
    http.StripPrefix("/static/",
    http.FileServer(http.Dir("./static"))))
    // 动态接口路由设置
    http.HandleFunc("/index", indexHandler)
    http.HandleFunc("/home", homeHandler)
    http.HandleFunc("/user", userHandler)
    http.HandleFunc("/upload", uploadHandler)
    http.HandleFunc("/download", downloadHandler)
    // 监听端口
    fmt.Println("上传服务正在启动, 监听端口: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>这是首页的内容</p>
</body>
</html>
D:\work\src>go run main.go
上传服务正在启动, 监听端口:8080...

http://localhost:8080/download?filename=redis.png