英文:
Simplest Golang CGI form example
问题
我很乐意帮助你编写一个最基本的 CGI 程序,其中包含一个表单输入。我不想运行一个监听器或使用框架,只想提供一个最基本的示例。这只是为了让你入门,稍后你可以添加更多功能。
下面是一个没有表单输入的简单 CGI 应用程序:
package main
import "fmt"
import "os"
func main() {
ip := os.Getenv("REMOTE_ADDR")
fmt.Printf("Content-type: text/plain\n\n")
fmt.Println(ip)
}
当我访问 https://example.com/cgi-bin/ip 时,它会打印出我的 IP 地址。
然而,下面的代码会导致 502 错误:
package main
import (
"fmt"
"net/http"
s "strings"
)
func main() {
var r *http.Request
fmt.Printf("Content-type: text/html\n\n")
fmt.Println("<!DOCTYPE html>")
fmt.Println("<title>login</title>")
r.ParseForm()
username := r.FormValue("username")
password := r.FormValue("password")
if s.Compare(password, username) == 0 {
fmt.Println("<p>invalid username/password")
}
}
nginx 日志显示:
2017/04/29 22:55:12 [error] 45768#0: *802 upstream prematurely closed FastCGI stdout while reading response header from upstream, client: 192.0.2.80, server: example.com, request: "POST /cgi-bin/login HTTP/2.0", upstream: "fastcgi://unix:run/slowcgi.sock:", host: "example.com", referrer: "https://example.com/login.html"
该表单的 HTML 代码如下:
<!DOCTYPE html>
<title>Username and password</title>
<form action="/cgi-bin/login" method="post">
<table>
<tr><td>Username:</td><td><input type="text" name="username"></td></tr>
<tr><td>Password:</td><td><input type="password" name="password"></td></tr>
<tr><td> </td><td><input type="submit" value="Submit"></td></tr>
</table>
</form>
另外注意,这是在 OpenBSD 上使用 slowcgi(8) 的 nginx。由于我的玩具 "ip" 程序可以工作,我相信问题出在我的 Go 代码中。
在我的 Go 代码中,我做错了什么?谢谢!
编辑:我现在有以下代码,但它无法编译。我做错了什么?
package main
import (
"fmt"
"os"
"net/http/cgi"
)
func main() {
httpReq, err := cgi.Request()
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
r := httpReq.ParseForm()
username := r.FormValue("username")
password := r.FormValue("password")
fmt.Printf("Content-type: text/html\n\n")
fmt.Printf("<!DOCTYPE html>\n")
fmt.Printf("<p>username: %s\n", username)
fmt.Printf("<p>password: %s\n", password)
}
请问我在这段 Go 代码中做错了什么?谢谢!
英文:
I'd appreciate some help with the most basic possible CGI program with a form input. I don't want to run a listener or use a framework or do anything beyond the most basic possible example. This is just to get my feet in the door, and I will add the bells and whistles later.
Here's a simple CGI app without a form input:
package main
import "fmt"
import "os"
func main() {
ip := os.Getenv("REMOTE_ADDR")
fmt.Printf("Content-type: text/plain\n\n")
fmt.Println(ip)
}
This prints my IP address when I go to https://example.com/cgi-bin/ip
However, the following code results in a 502 error:
package main
import (
"fmt"
"net/http"
s "strings"
)
func main() {
var r *http.Request
fmt.Printf("Content-type: text/html\n\n")
fmt.Println("<!DOCTYPE html>")
fmt.Println("<title>login</title>")
r.ParseForm()
username := r.FormValue("username")
password := r.FormValue("password")
if s.Compare(password, username) == 0 {
fmt.Println("<p>invalid username/password")
}
}
The nginx log says:
2017/04/29 22:55:12 [error] 45768#0: *802 upstream prematurely closed FastCGI stdout while reading response header from upstream, client: 192.0.2.80, server: example.com, request: "POST /cgi-bin/login HTTP/2.0", upstream: "fastcgi://unix:run/slowcgi.sock:", host: "example.com", referrer: "https://example.com/login.html"
The HTML for this form is:
<!DOCTYPE html>
<title>Username and password</title>
<form action="/cgi-bin/login" method="post">
<table>
<tr><td>Username:</td><td><input type="text" name="username"></td></tr>
<tr><td>Password:</td><td><input type="password" name="password"></td></tr>
<tr><td> </td><td><input type="submit" value="Submit"></td></tr>
</table>
</form>
Another note: this is nginx on OpenBSD, using slowcgi(8). Since my toy "ip" program works, I believe my Go code is the problem.
What am I doing wrong in my Go code? Thank you!
EDIT: I now have the following code, which doesn't compile. What am I doing wrong?
package main
import (
"fmt"
"os"
"net/http/cgi"
)
func main() {
httpReq, err := cgi.Request()
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
r := httpReq.ParseForm()
username := r.FormValue("username")
password := r.FormValue("password")
fmt.Printf("Content-type: text/html\n\n")
fmt.Printf("<!DOCTYPE html>\n")
fmt.Printf("<p>username: %s\n", username)
fmt.Printf("<p>password: %s\n", password)
}
答案1
得分: 4
由于 net/http/cgi 对您不起作用,并且查看了 nginx 的错误日志,提到了 FastCGI,您可能需要使用 net/http/fcgi 包。
如果查看其文档,您会发现需要做一些额外的工作才能访问请求。
首先声明一个用于处理请求的函数:
func myhandler(_ http.ResponseWriter, r *http.Request) {
// 处理请求
}
然后将您的新处理程序传递给 fcgi.Serve 函数。
fcgi.Serve(nil, http.HandlerFunc(myhandler))
Serve
函数接受两个参数。第一个参数是一个 net.Listener
,如果传入 nil
,Serve
将从 stdin
读取请求。第二个参数是 http.Handler 类型,它是一个接口,myhandler
函数本身并没有实现该接口,但是根据其特定的签名 (func(http.ResponseWriter, *http.Request)
),您可以使用 http.HandlerFunc 类型将 myhandler
转换为 http.Handler
。
完整示例:
package main
import (
"fmt"
"net/http/fcgi"
"os"
)
func myhandler(_ http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
username := r.FormValue("username")
password := r.FormValue("password")
fmt.Printf("Content-type: text/html\n\n")
fmt.Printf("<!DOCTYPE html>\n")
fmt.Printf("<p>username: %s\n", username)
fmt.Printf("<p>password: %s\n", password)
}
func main() {
if err := fcgi.Serve(nil, http.HandlerFunc(myhandler)); err != nil {
panic(err)
}
}
英文:
Since net/http/cgi is not working for you and looking at the error log from nginx mentioning FastCGI, you might need to use the net/http/fcgi package.
If you look at its documentation you can see that you'll need to do a little bit more work to get access to the request.
First declare a function that will be used to handle the requests:
func myhandler(_ http.ResponseWriter, r *http.Request) {
// handle request
}
Then pass your new handler to the fcgi.Serve function.
fcgi.Serve(nil, http.HandlerFunc(myhandler))
Serve
takes two parameters. The first one is a net.Listener
, if passed in as nil
, Serve
will read the requests from stdin
. The second one, is of type http.Handler which is an interface that the myhandler
function, as is, does not implement but given its specific signature (func(http.ResponseWriter, *http.Request)
) you can convert myhandler
to an http.Handler
using the http.HandlerFunc type.
Full example:
package main
import (
"fmt"
"net/http/fcgi"
"os"
)
func myhandler(_ http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
username := r.FormValue("username")
password := r.FormValue("password")
fmt.Printf("Content-type: text/html\n\n")
fmt.Printf("<!DOCTYPE html>\n")
fmt.Printf("<p>username: %s\n", username)
fmt.Printf("<p>password: %s\n", password)
}
func main() {
if err := fcgi.Serve(nil, http.HandlerFunc(myhandler)); err != nil {
panic(err)
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论