最简单的Golang CGI表单示例

huangapple go评论82阅读模式
英文:

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 &quot;fmt&quot;
import &quot;os&quot;

func main() {
        ip := os.Getenv(&quot;REMOTE_ADDR&quot;)
        fmt.Printf(&quot;Content-type: text/plain\n\n&quot;)
        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 (
        &quot;fmt&quot;
        &quot;net/http&quot;
        s &quot;strings&quot;
)

func main() {
        var r *http.Request

        fmt.Printf(&quot;Content-type: text/html\n\n&quot;)
        fmt.Println(&quot;&lt;!DOCTYPE html&gt;&quot;)
        fmt.Println(&quot;&lt;title&gt;login&lt;/title&gt;&quot;)
        r.ParseForm()
        username := r.FormValue(&quot;username&quot;)
        password := r.FormValue(&quot;password&quot;)
        if s.Compare(password, username) == 0 {
                fmt.Println(&quot;&lt;p&gt;invalid username/password&quot;)
        }
}

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:

&lt;!DOCTYPE html&gt;
&lt;title&gt;Username and password&lt;/title&gt;

&lt;form action=&quot;/cgi-bin/login&quot; method=&quot;post&quot;&gt;
&lt;table&gt;
&lt;tr&gt;&lt;td&gt;Username:&lt;/td&gt;&lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;username&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Password:&lt;/td&gt;&lt;td&gt;&lt;input type=&quot;password&quot; name=&quot;password&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt; &lt;/td&gt;&lt;td&gt;&lt;input type=&quot;submit&quot; value=&quot;Submit&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;/form&gt;

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 (
        &quot;fmt&quot;
        &quot;os&quot;
        &quot;net/http/cgi&quot;
)

func main() {
        httpReq, err := cgi.Request()
        if err != nil {
                fmt.Fprintf(os.Stderr, err.Error())
                os.Exit(1)
        }

        r := httpReq.ParseForm()
        username := r.FormValue(&quot;username&quot;)
        password := r.FormValue(&quot;password&quot;)

        fmt.Printf(&quot;Content-type: text/html\n\n&quot;)
        fmt.Printf(&quot;&lt;!DOCTYPE html&gt;\n&quot;)
        fmt.Printf(&quot;&lt;p&gt;username: %s\n&quot;, username)
        fmt.Printf(&quot;&lt;p&gt;password: %s\n&quot;, 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,如果传入 nilServe 将从 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 (
	&quot;fmt&quot;
	&quot;net/http/fcgi&quot;
	&quot;os&quot;
)

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(&quot;username&quot;)
	password := r.FormValue(&quot;password&quot;)

	fmt.Printf(&quot;Content-type: text/html\n\n&quot;)
	fmt.Printf(&quot;&lt;!DOCTYPE html&gt;\n&quot;)
	fmt.Printf(&quot;&lt;p&gt;username: %s\n&quot;, username)
	fmt.Printf(&quot;&lt;p&gt;password: %s\n&quot;, password)
}

func main() {
    if err := fcgi.Serve(nil, http.HandlerFunc(myhandler)); err != nil {
        panic(err)
    }
}

huangapple
  • 本文由 发表于 2017年4月30日 07:06:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/43701302.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定