Golang Web表单问题解决方法探讨

Golang Web表单问题解决方法探讨

func main() {
http.HandleFunc("/", form)
http.ListenAndServe(":8080", nil)
}
func form(res http.ResponseWriter, req *http.Request) {
value := req.FormValue(“text”)
res.Header().Set(“Content-Type”, “text/html; charset=utf-8”)
io.WriteString(res, `     <form method="POST">        <input type="text" name="text">      <input type="submit">     </form>     <br>`+value)
c := make(chan string)
c <- value

fmt.Println("check:", <-c)
fmt.Println("at the end")

运行上面的代码,HTTP服务器就卡住了。为什么表单无法显示?

我的目的是在继续执行其余代码之前获取用户输入。实际上,我打算在第一个反馈(用户输入)之后呈现另一个用户输入表单,但发现两个表单都会自动同时运行,因此考虑使用一个通道来等待第一个用户输入,然后再进行第二个用户输入。但是加入通道后服务器就卡住了。

我对Golang非常陌生,对编程也很陌生。我在这里对http.Server的理解是错误的吗?代码不是应该逐行执行吗?

希望能得到一些启发。


更多关于Golang Web表单问题解决方法探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html

7 回复

哦哦……现在明白了。非常感谢。 我会尝试学习数据库。也会去查一下什么是KV存储以及Redis的含义。

非常感谢你的帮助!

更多关于Golang Web表单问题解决方法探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


不,HTTP 不是这样工作的。

一个处理器处理单个请求。不多也不少。

你需要通过其他方式将数据从前一个请求传递到另一个请求。数据库通常用于长期持久化数据,而像 Redis 这样的键值存储则用于短期持久化。

通道 c 是无缓冲的,因此 c <- value另一个协程从该通道读取数据之前不会继续执行。

这就是它“停滞”的原因。

针对您的评论,我有两个问题,这让我更加困惑了。

  1. channel 语句在表单语句之后。我假设我们还没有执行到 channel 语句,那么表单应该显示出来,对吗?我甚至看不到表单。

  2. 对于 channel c,我以为我紧接着就读取了它:

    fmt.Println("check:", <-c)
    

    但它仍然在同一个 main 函数中。channel 只能在 go 协程中使用吗?我不能在同一个函数中使用它吗?

抱歉,我还在努力理解一些基本概念。

非常感谢!

HTTP 的情况看起来有点混乱。我仍然不太清楚。

至于通道,我似乎得到了一些提示。让我试试带缓冲的?

实际上,我的主要目标是获取 2 个用户输入并在最后呈现结果:

  1. 网页表单 - 获取 data1
  2. 根据 data1,运行另一个网页表单,获取 data2
  3. 使用 data1 和 data2,在最后呈现结果。

有没有办法在一个处理器中运行 2 个网页表单来获取 2 个不同的用户输入,而不是调用单独的处理器来获取不同的用户输入?

learner:

通道语句在表单语句之后。我认为我们还没有执行到通道语句,表单应该被呈现出来,对吗?我甚至看不到表单。

写入 HTTP 响应通常是缓冲的,并且当前的表单可能没有填满缓冲区,因此它还没有被发送。这种缓冲发生在多个层面,res 可能被缓冲,运行时可能在内部和外部之间对流有额外的缓冲区,操作系统的网络栈可能还有另一个缓冲区。

learner:

对于通道 c,我以为我之后立即读取了它: fmt.Println(“check:”, <-c)

正如我所说,它无法到达那里,因为它无法通过无缓冲的发送。

这是一个典型的并发理解问题。你的代码卡住是因为通道操作阻塞了HTTP请求处理。

主要问题:

  1. 通道在同一个goroutine中发送和接收,导致死锁
  2. HTTP服务器是并发处理请求的,不能同步等待用户输入

这是修复后的代码:

package main

import (
    "fmt"
    "io"
    "net/http"
)

func main() {
    // 创建一个带缓冲的通道
    c := make(chan string, 1)
    
    http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
        form(res, req, c)
    })
    
    http.HandleFunc("/second", func(res http.ResponseWriter, req *http.Request) {
        secondForm(res, req, c)
    })
    
    fmt.Println("Server starting on :8080")
    http.ListenAndServe(":8080", nil)
}

func form(res http.ResponseWriter, req *http.Request, c chan string) {
    if req.Method == "POST" {
        value := req.FormValue("text")
        // 将值发送到通道(非阻塞,因为有缓冲)
        select {
        case c <- value:
            // 重定向到第二个表单
            http.Redirect(res, req, "/second", http.StatusSeeOther)
            return
        default:
            // 通道已满,处理错误
            io.WriteString(res, "系统繁忙,请重试")
            return
        }
    }
    
    res.Header().Set("Content-Type", "text/html; charset=utf-8")
    io.WriteString(res, `
        <form method="POST">
            <input type="text" name="text">
            <input type="submit">
        </form>
    `)
}

func secondForm(res http.ResponseWriter, req *http.Request, c chan string) {
    // 从通道读取第一个表单的值
    var firstValue string
    select {
    case firstValue = <-c:
        // 成功读取
    default:
        firstValue = "无"
    }
    
    if req.Method == "POST" {
        secondValue := req.FormValue("text2")
        io.WriteString(res, fmt.Sprintf(`
            第一个输入: %s<br>
            第二个输入: %s<br>
            <a href="/">重新开始</a>
        `, firstValue, secondValue))
        return
    }
    
    res.Header().Set("Content-Type", "text/html; charset=utf-8")
    io.WriteString(res, fmt.Sprintf(`
        第一个输入的值是: %s<br>
        <form method="POST">
            请输入第二个值: <input type="text" name="text2">
            <input type="submit">
        </form>
    `, firstValue))
}

如果你确实需要顺序处理,可以使用状态管理而不是通道:

package main

import (
    "io"
    "net/http"
    "sync"
)

type FormState struct {
    mu    sync.Mutex
    data  map[string]string
}

func main() {
    state := &FormState{
        data: make(map[string]string),
    }
    
    http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
        handleForm(res, req, state)
    })
    
    http.ListenAndServe(":8080", nil)
}

func handleForm(res http.ResponseWriter, req *http.Request, state *FormState) {
    res.Header().Set("Content-Type", "text/html; charset=utf-8")
    
    step := req.URL.Query().Get("step")
    
    switch step {
    case "1":
        if req.Method == "POST" {
            firstValue := req.FormValue("text1")
            state.mu.Lock()
            state.data["first"] = firstValue
            state.mu.Unlock()
            
            // 显示第二个表单
            io.WriteString(res, `
                <form method="POST" action="/?step=2">
                    第二个输入: <input type="text" name="text2">
                    <input type="submit">
                </form>
            `)
            return
        }
        
        // 显示第一个表单
        io.WriteString(res, `
            <form method="POST" action="/?step=1">
                第一个输入: <input type="text" name="text1">
                <input type="submit">
            </form>
        `)
        
    case "2":
        if req.Method == "POST" {
            secondValue := req.FormValue("text2")
            state.mu.Lock()
            firstValue := state.data["first"]
            state.mu.Unlock()
            
            io.WriteString(res, "第一个值: " + firstValue + "<br>")
            io.WriteString(res, "第二个值: " + secondValue + "<br>")
            io.WriteString(res, `<a href="/">重新开始</a>`)
            return
        }
        
    default:
        // 显示第一个表单
        io.WriteString(res, `
            <form method="POST" action="/?step=1">
                第一个输入: <input type="text" name="text1">
                <input type="submit">
            </form>
        `)
    }
}

关键点:

  1. HTTP服务器是并发模型,每个请求在独立的goroutine中处理
  2. 通道在同一个goroutine中发送和接收会导致死锁
  3. 使用URL参数或session来管理多步表单状态
  4. 需要共享状态时使用互斥锁保护并发访问
回到顶部