1.前端代码:
<!DOCTYPE html>
<html><head><meta name="viewport" content="device-width,initial-scale=1.0,user-scalable=no,maximum-scale=5.0" charset="UTF-8"><title>无刷新前后端交互设计</title><link rel="icon" href="image/restar.ico"><script type="text/javascript" src="js/jquery-3.6.0.js"></script><!-- 以下是基于jquery的自定义脚本 --><script>function login() {$.ajax({async: true, //是否异步cache: false, //是否缓存type: "POST", //GET请求 POST请求方式// 注意js函数通过div中各组件的id属性读取数据dataType: "text",data: {//获取用户输入的信息,向服务器传送,注意字段名的前后端一致userid: $("#userid").val(),passwd: $("#passwd").val()},url: "/login", //后端服务接口// 回调成功的操作success: function (result) {//alert(result);$("#msg").html(result);},// 回调失败的操作error: function () {// alert("操作异常");$("#msg").html("操作异常");}});}</script><script>// 页面时钟效果,可用于实时更新数据setInterval(() => {fetch("/stime") // 后端服务接口.then(res => res.text()).then(msg => {// 把数据显示到页面组件上document.querySelector("#svrtime").innerHTML = "服务器实时时间:" + msg;$("#labtime").html("这是标签组件显示:" + msg);})}, 1000) // 1000表示1秒间隔</script><script>function getqr() {// 组装img组件属性,调用后端接口$("#qrimg").html('<img src="/qrimg?kwd=' + $("#qrc").val() + '"alt" = "二维码" /> ');
}</script>
</head><body><img src="images/top.png" height="100px" width="100%" alt="top image" /><!-- 这里div显示服务器实时时间 --><div id="svrtime"></div><!-- 这里label显示服务器实时时间 --><label id="labtime"></label><!-- 这里登录不使用form --><hr><div id="login">用户: <input type="text" id="userid" required="required" autofocus>密码: <input type="password" id="passwd" required="required"><input type="button" id="btnLogin" value="登录" onclick="login()"></div><br><hr><!-- 这里显示服务器返回的信息 --><div id="msg"></div><br><hr>输入二维码内容: <input type="text" id="qrc" style="width: 300px;" required="required"><input type="button" id="btnqr" value="生成二维码" onclick="getqr()"><br><div id="qrimg"></div>
</body></html>
2.后端代码:
package mainimport ("encoding/json""fmt""net/http""time""github.com/skip2/go-qrcode"
)type UserInfo struct {UserId string `jason:"userid"`Passwd string `jason:"pwd"`
}
type Result struct {Code int `jason:"code"`Message string `jason:"message"`
}func main() {fmt.Println("server is running....")http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir("./tpl/"))))http.HandleFunc("/login", Login)http.HandleFunc("/jsonlogin", JsonLogin) // json格式的登录验证处理http.HandleFunc("/stime", SrvTime) // 返回时间数据http.HandleFunc("/qrimg", qrimg) // 函数即服务err := http.ListenAndServe(":8080", nil) // 设置监听端口并启动服务if err != nil {fmt.Println(err.Error())}
}func SrvTime(w http.ResponseWriter, r *http.Request) {// fmt.Fprint(w, time.Now().Format("2006-01-02 15:04:05"))// go1.20+可以使用系统常量fmt.Fprint(w, time.Now().Format(time.DateTime))}func Login(w http.ResponseWriter, r *http.Request) {r.ParseForm()userid := r.FormValue("userid")passwd := r.FormValue("passwd")// 以下为json输出msg := Result{Code: http.StatusOK,Message: "账号密码验证通过,登录成功",}w.Header().Set("Content-Type", "application/json")// 模拟登录判断if userid != "admin" || passwd != "123456" {msg.Message = "用户名或密码错误,登录失败"err := json.NewEncoder(w).Encode(&msg) // json编码输出if err != nil {fmt.Fprint(w, err.Error())}return}err := json.NewEncoder(w).Encode(&msg) // json编码输出if err != nil {fmt.Fprint(w, err.Error())}
}
func JsonLogin(w http.ResponseWriter, r *http.Request) {var user UserInfo// 解析客户端发来的json数据err := json.NewDecoder(r.Body).Decode(&user)if err != nil {http.Error(w, err.Error(), http.StatusBadRequest)return}msg := Result{Code: http.StatusOK,Message: "账号密码验证通过,登录成功",}w.Header().Set("Content-Type", "application/json")// 模拟登录判断if user.UserId != "admin" || user.Passwd != "123456" {msg.Message = "用户名或密码错误,登录失败"err = json.NewEncoder(w).Encode(&msg) // json编码输出if err != nil {fmt.Fprint(w, err.Error())}return
}
err = json.NewEncoder(w).Encode(&msg) // json编码输出
if err != nil {
fmt.Fprint(w, err.Error())
}
}
3.关键在于后端挂载路由这块
func main() {fmt.Println("server is running....")http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir("./tpl/"))))http.HandleFunc("/login", Login)http.HandleFunc("/jsonlogin", JsonLogin) // json格式的登录验证处理http.HandleFunc("/stime", SrvTime) // 返回时间数据http.HandleFunc("/qrimg", qrimg) // 函数即服务err := http.ListenAndServe(":8080", nil) // 设置监听端口并启动服务if err != nil {fmt.Println(err.Error())}
}
关于挂载静态路由的解释:
Go语言中 http.Handle
和 http.StripPrefix
结合挂载静态文件路由的用法
在Go语言的标准库中,http.Handle
是用来注册 HTTP 路由处理器的方法之一。当需要提供静态文件访问功能时,通常会结合以下几个组件来完成:
1. http.FileServer
-
这是一个函数,它接受一个实现了
http.FileSystem
接口的对象作为参数,并返回一个可以处理文件请求的 HTTP 处理程序。 -
示例代码中的
filesystem := http.Dir("static/template")
表示将本地目录"static/template"
映射为可被 Web 访问的资源。
2. http.StripPrefix
-
此函数的作用是从 URL 中移除指定的前缀部分后再传递给下一个处理器。
-
在实际应用中,如果希望
/static/file.txt
对应的是物理路径下的某个文件,则可以通过此方法去掉/static
前缀以便后续匹配真实文件位置。
3. 组合使用
下面是一段完整的例子展示如何利用这些工具设置静态资源服务:
package mainimport ("net/http"
)func main() {prefix := "/static" filesystem := http.Dir("static/template") // 创建一个新的文件服务器实例并剥离指定URL路径开头的部分.fileserve := http.StripPrefix(prefix, http.FileServer(filesystem))// 注册该自定义处理器至全局默认mux上对应特定模式的位置处。http.Handle("/static/", fileserve)// 启动监听等待连接到来...http.ListenAndServe(":8080", nil)
}
在这个案例里:
- 当客户端尝试通过浏览器或者其它方式访问形如
http://localhost:8080/static/somefile.html
地址时, - 首先会被导向到我们刚刚建立起来的那个复合型处理器那里;
- 然后经过
StripPrefix
操作之后变成单纯寻找名为somefile.html
的文档而已——而不再带有前面那段固定的子串了; - 最终定位到了磁盘上的确切目标所在之处即
"./static/template/somefile.html"
文件本身得以呈现出来供外界查阅之便。
这种设计既简洁又高效,非常适合那些只需要简单暴露内部资料而不涉及复杂逻辑交互的小规模项目场景下采用。
关键点解析
-
为什么需要
http.StripPrefix
? 因为如果不做任何修改就直接把原始请求转发过去的话,那么最终找寻的目标就会带上多余的字符串成分比如这里的 “/static”,这显然不符合实际情况也不利于维护管理等工作开展下去。 -
关于安全性考量 虽然上述做法能够满足基本需求但是缺乏必要的防护措施比如说验证身份合法性等等重要环节尚未加入其中因此建议开发者们根据具体应用场景酌情增添额外的安全机制确保整个系统的稳健可靠运行状态良好无虞。
总结
综上所述,在Go语言开发过程中合理运用标准库所提供的各类便捷手段可以帮助我们快速搭建起所需的功能模块诸如本文提到过的有关于静态网页内容分发方面的解决方案就是如此典型的一个范例值得大家深入学习借鉴吸收转化为己所长加以灵活运用从而提高工作效率创造更大价值。