英文:
Why and when would a ResponseWriter generate raw html?
问题
我不明白为什么代码能够正确生成view.html和post.html的数据,但显示为纯文本。我一直在按照这里的指南进行操作,并且在构建过程中,我认为Execute函数生成的HTML会发送到ResponseWriter来进行显示,但我得到的错误似乎表明我对Execute或ResponseWriter的理解是错误的。
package main
import (
"os"
"fmt"
"time"
"bufio"
"net/http"
"html/template"
)
type UserPost struct {
Name string
About string
PostTime string
}
func check(e error) {
if e != nil {
fmt.Println("Error Recieved...")
panic(e)
}
}
func lineCounter(workingFile *os.File) int {
fileScanner := bufio.NewScanner(workingFile)
lineCount := 0
for fileScanner.Scan() {
lineCount++
}
return lineCount
}
func loadPage(i int) (*UserPost, error) {
Posts, err := os.Open("dataf.txt")
check(err)
var PostArray [512]UserPost = parsePosts(Posts, i)
Name := PostArray[i].Name
About := PostArray[i].About
PostTime := PostArray[i].PostTime
Posts.Close()
return &UserPost{Name: Name[:len(Name)-1], About: About[:len(About)-1], PostTime: PostTime[:len(PostTime)-1]}, nil
}
func viewHandler(w http.ResponseWriter, r *http.Request) {
tmp, err := os.Open("dataf.txt")
check(err)
num := (lineCounter(tmp) / 3)
tmp.Close()
for i := 0; i < num; i++ {
p, _ := loadPage(i)
t, _ := template.ParseFiles("view.html")
t.Execute(w, p)
}
p := UserPost{Name: "", About: "", PostTime: ""}
t, _ := template.ParseFiles("post.html")
t.Execute(w, p)
}
func inputHandler(w http.ResponseWriter, r *http.Request) {
Name := r.FormValue("person")
About := r.FormValue("body")
PostTime := time.Now().String()
filePaste, err := os.OpenFile("dataf.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND|os.SEEK_END, 0666)
check(err)
filePaste.WriteString(Name + "~\n")
filePaste.WriteString(About + "~\n")
filePaste.WriteString(PostTime + "~\n")
filePaste.Close()
fmt.Println("Data recieved: ", Name, About, PostTime)
http.Redirect(w, r, "/#bottom", http.StatusFound) //Use "/#bottom" to go to bottom of html page.
}
func parsePosts(fileToParse *os.File, num int) [512]UserPost {
var buffer [512]UserPost
reader := bufio.NewReader(fileToParse)
//This For loop reads each "forum post" then saves it to the buffer, then iterates to the next.
for i := 0; i <= num; i++ {
currentPost := new(UserPost)
str, err := reader.ReadString('~')
check(err)
currentPost.Name = str
//I search for '~' because my files save the end of reading line with that, so i can keep formatting saved (\n placement).
str2, err2 := reader.ReadString('~')
check(err2)
currentPost.About = str2
str3, err3 := reader.ReadString('~')
check(err3)
currentPost.PostTime = str3
buffer[i] = *currentPost
}
return buffer
}
func main() {
fmt.Println("Listening...")
http.HandleFunc("/", viewHandler)
http.HandleFunc("/post/", inputHandler)
http.ListenAndServe(":8080", nil)
}
view.html
<h4>{{.Name}}</h4>
<font size="3">
<div>{{printf "%s" .About}}</div>
</font>
<br>
<font size="2" align="right">
<div align="right">{{.PostTime}}</div>
</font>
post.html
<form action="/post/" method="POST">
<div><textarea name="person" rows="1" cols="30">{{printf "%s" .Name}}</textarea></div>
<div><textarea name="body" rows="5" cols="100">{{printf "%s" .About}}</textarea></div>
<div><input type="submit" value="Submit"></div>
<a name="bottom"></a>
</form>
我目前正在从一个空的dataf.txt文件中读取。
英文:
I don't understand why the code is generating the view.html and post.html data correctly but displaying it all as raw text. I had been following the guide here and as I was building it, I thought that the generated html from the Execute function would be sent to the ResponserWriter which would handle displaying it, but the error I'm getting seems to indicate my understanding of Execute or the ResponseWriter is wrong.
package main
import (
"os"
"fmt"
"time"
"bufio"
"net/http"
"html/template"
)
type UserPost struct {
Name string
About string
PostTime string
}
func check(e error) {
if e != nil {
fmt.Println("Error Recieved...")
panic(e)
}
}
func lineCounter(workingFile *os.File) int {
fileScanner := bufio.NewScanner(workingFile)
lineCount := 0
for fileScanner.Scan() {
lineCount++
}
return lineCount
}
func loadPage(i int) (*UserPost, error) {
Posts,err := os.Open("dataf.txt")
check(err)
var PostArray [512]UserPost = parsePosts(Posts,i)
Name := PostArray[i].Name
About := PostArray[i].About
PostTime := PostArray[i].PostTime
Posts.Close()
return &UserPost{Name: Name[:len(Name)-1], About: About[:len(About)-1], PostTime: PostTime[:len(PostTime)-1]}, nil
}
func viewHandler(w http.ResponseWriter, r *http.Request) {
tmp,err := os.Open("dataf.txt")
check(err)
num := (lineCounter(tmp)/3)
tmp.Close()
for i := 0; i < num; i++ {
p, _ := loadPage(i)
t, _ := template.ParseFiles("view.html")
t.Execute(w, p)
}
p := UserPost{Name: "", About: "", PostTime: ""}
t, _ := template.ParseFiles("post.html")
t.Execute(w, p)
}
func inputHandler(w http.ResponseWriter, r *http.Request) {
Name := r.FormValue("person")
About := r.FormValue("body")
PostTime := time.Now().String()
filePaste,err := os.OpenFile("dataf.txt", os.O_RDWR | os.O_CREATE | os.O_APPEND | os.SEEK_END, 0666)
check(err)
filePaste.WriteString(Name+"~\n")
filePaste.WriteString(About+"~\n")
filePaste.WriteString(PostTime+"~\n")
filePaste.Close()
fmt.Println("Data recieved: ", Name,About,PostTime)
http.Redirect(w, r, "/#bottom", http.StatusFound) //Use "/#bottom" to go to bottom of html page.
}
//os.File is the file type.
func parsePosts(fileToParse *os.File,num int) [512]UserPost {
var buffer [512]UserPost
reader := bufio.NewReader(fileToParse)
//This For loop reads each "forum post" then saves it to the buffer, then iterates to the next.
for i := 0;i <= num; i++ {
currentPost := new(UserPost)
str, err := reader.ReadString('~')
check(err)
currentPost.Name = str
//I search for '~' because my files save the end of reading line with that, so i can keep formatting saved (\n placement).
str2, err2 := reader.ReadString('~')
check(err2)
currentPost.About = str2
str3, err3 := reader.ReadString('~')
check(err3)
currentPost.PostTime = str3
buffer[i] = *currentPost
}
return buffer
}
func main() {
fmt.Println("Listening...")
http.HandleFunc("/", viewHandler)
http.HandleFunc("/post/", inputHandler)
http.ListenAndServe(":8080", nil)
}
view.html
<h4>{{.Name}}</h4>
<font size="3">
<div>{{printf "%s" .About}}</div>
</font>
<br>
<font size="2" align="right">
<div align="right">{{.PostTime}}</div>
</font>
post.html
<form action="/post/" method="POST">
<div><textarea name="person" rows="1" cols="30">{{printf "%s" .Name}}</textarea></div>
<div><textarea name="body" rows="5" cols="100">{{printf "%s" .About}}</textarea></div>
<div><input type="submit" value="Submit"></div>
<a name="bottom"></a>
</form>
I've currently been reading from an empty dataf.txt file.
答案1
得分: 3
如提示所示,这是因为您没有设置内容类型。引用自http.ResponseWriter
:
// Write将数据作为HTTP回复的一部分写入连接。
// 如果尚未调用WriteHeader,则Write在写入数据之前调用WriteHeader(http.StatusOK)。
// 如果标头不包含Content-Type行,则Write将Content-Type添加到将初始512字节写入数据传递给DetectContentType的结果。
Write([]byte) (int, error)
如果您不自己设置内容类型,第一次调用ResponseWriter.Write()
将调用http.DetectContentType()
来猜测要设置的内容类型。如果您发送的内容以"<form>"
开头,它将不会被检测为HTML,而是设置为"text/plain; charset=utf-8"
(这会“指示”浏览器将内容显示为文本而不是尝试解释为HTML)。
例如,如果内容以"<html>"
开头,将自动设置内容类型为"text/html; charset=utf-8"
,并且无需进一步操作即可正常工作。
但是,如果您知道自己发送的内容,请不要依赖自动检测,而是自己设置它比在其上运行检测算法要快得多,因此在写入/发送任何数据之前,只需添加以下行:
w.Header().Set("Content-Type", "text/html; charset=utf-8")
还要确保您的post.html
模板是一个完整的、有效的HTML文档。
另外一个建议是:在您的代码中,您总是忽略了检查返回的错误。不要这样做。您至少可以在控制台上打印错误。如果您不忽略错误,将为自己节省很多时间。
英文:
As hinted, it's because you haven't set the content type. Quoting from http.ResponseWriter
:
// Write writes the data to the connection as part of an HTTP reply.
// If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK)
// before writing the data. If the Header does not contain a
// Content-Type line, Write adds a Content-Type set to the result of passing
// the initial 512 bytes of written data to DetectContentType.
Write([]byte) (int, error)
If you don't set the content type yourself, first call to ResponseWriter.Write()
will call http.DetectContentType()
to guess what to set. If the content you send starts with "<form>"
, it won't be detected as HTML, but "text/plain; charset=utf-8"
will be set (which "instructs" the browser to display the content as text and not try to interpret it as HTML).
If the content would start with "<html>"
for example, content type "text/html; charset=utf-8"
would be set automatically and it would work without further actions.
But don't rely on automatic detection if you know what you're sending, also it's much faster to set it yourself than to run a detection algorithm on it, so simply add this line before writing/sending any data:
w.Header().Set("Content-Type", "text/html; charset=utf-8")
And also make your post.html
template a complete, valid HTML document.
Also another piece of advice: in your code you religiously omit checking returned errors. Don't do that. The least you could do is print them on the console. You will save a lot of time for yourself if you don't omit errors.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论