Why is my script reading in files linked in my HTML which aren't being specified when reading in with ioutil.ReadFile() in GoLang?

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

Why is my script reading in files linked in my HTML which aren't being specified when reading in with ioutil.ReadFile() in GoLang?

问题

我有一个 GoLang 脚本,用于根据浏览器中的输入查询动态构建网页,格式如下:http://localhost:8000/blog/post#,其中的 post# 部分用于标识要解析为我创建的 HTML 模板的哪个 JSON 数据文件;例如,如果我输入 http://localhost:8000/blog/post1,那么它将从我的 post1.json 文件创建一个 index.html 文件。目前,当我运行脚本时,它允许我在浏览器中加载单个页面,然后在终端标准输出日志中退出并显示以下错误:

2022/03/18 00:32:02 error: open jsofun.css.json: no such file or directory
exit status 1

这是我的当前脚本:

package main

import (
	"encoding/json"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

func blogHandler(w http.ResponseWriter, r *http.Request) {
	blogstr := r.URL.Path[len("/blog/"):]

	blogstr = blogstr + ".json"

	// 本地读取文件
	json_file, err := ioutil.ReadFile(blogstr)
	if err != nil {
		log.Fatal("error: ", err)
	}

	// 定义数据结构
	type BlogPost struct {
		// 为了在后面看到这些元素,这些字段必须是可导出的
		// 这意味着使用大写字母命名和在末尾标识 json 字段
		Title       string `json:"title"`
		Timestamp   string `json:"timestamp"`
		Main        string `json:"main"`
		ContentInfo string `json:"content_info"`
	}

	// JSON 数据
	var obj BlogPost

	err = json.Unmarshal(json_file, &obj)
	if err != nil {
		log.Fatal("error: ", err)
	}

	tmpl, err := template.ParseFiles("./blogtemplate.html")

	HTMLfile, err := os.Create("index.html")
	if err != nil {
		log.Fatal(err)
	}

	defer HTMLfile.Close()

	tmpl.Execute(HTMLfile, obj)

	http.ServeFile(w, r, "./index.html")
}

func main() {
	http.HandleFunc("/blog/", blogHandler)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

我已经进行了一些基本的调试,并确定问题出在以下几行代码:

json_file, err := ioutil.ReadFile(blogstr)
	if err != nil {
		log.Fatal("error: ", err)
	}

让我困惑的是为什么 ioutil.ReadFile 还会尝试读取我的 HTML 中的链接?难道不应该由浏览器处理这个链接,而不是由我的处理程序处理?作为参考,这是我的 HTML,其中链接了文件 jsofun.css;我在控制台输出中引用的错误显示我的脚本在调用 ioutil.ReadFile 时尝试访问此文件作为 jsofun.css.json

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dynamic JSON Events</title>
    <link rel="stylesheet" href="./jsofun.css"></style>
</head>
<body>
    <section id="title">
        <h1 id="text-title">{{.Title}}</h1>
        <time id="timestamp">
		    {{.Timestamp}}
        </time>
    </section>
    <nav role="navigation" id="site-nav">
        <ul id="sitemap">
        </ul>
    </nav>
    <main role="main" id="main">
		{{.Main}}
    </main>
    <footer role="contentinfo" id="footer">
        <section id="content-info" role="contentinfo">
			{{.ContentInfo}}
        </section>
        <form id="contact-form" role="form">
        <address>
            Contact me by <a id="my-email" href="mailto:antonhibl11@gmail.com" class="my-email">e-mail</a>
        </address>
        </form>
    </footer>
<script src="./jsofun.js">
</script>
</body>
</html>

我知道我在这行代码中添加了 .json 到查询字符串:blogstr = blogstr + ".json",但是,我不明白为什么这也会影响到我的 HTML 中的链接。有什么我忽略的东西,或者为什么它会读取我的 HTML 模板中的链接?我只希望 ioutil.ReadFile 读取我的 JSON 文件,并将其解析到我的模板中。

编辑:我想补充一点,当我最初将页面加载到浏览器中时,它成功加载了来自 JSON 的文本内容,但没有应用任何样式。当我查看源代码时,链接也是正确的,这让我困惑为什么样式表不起作用,因为它在正确的目录中可访问。这是当我在浏览器中选择查看源代码时的源代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dynamic JSON Events</title>
    <link rel="stylesheet" href="./jsofun.css"></style>
</head>
<body>
    <section id="title">
        <h1 id="text-title">Second Post Test</h1>
        <time id="timestamp">
		    Friday, March 18th, 12:21AM
        </time>
    </section>
    <nav role="navigation" id="site-nav">
        <ul id="sitemap">
        </ul>
    </nav>
    <main role="main" id="main">
		This is my second post where I am able to use dynamic URL routing
    </main>
    <footer role="contentinfo" id="footer">
        <section id="content-info" role="contentinfo">
			This post was used primarily to test and layout how the rest of my posts will appear.
        </section>
        <form id="contact-form" role="form">
        <address>
            Contact me by <a id="my-email" href="mailto:antonhibl11@gmail.com" class="my-email">e-mail</a>
        </address>
        </form>
    </footer>
<script src="./jsofun.js">
</script>
</body>
</html>

作为参考,这是我的 JSON 文件的示例:

{
    "title" : "Second Post Test",
    "timestamp": "Friday, March 18th, 12:21AM",
    "main": "This is my second post where I am able to use dynamic URL routing",
    "content_info": "This post was used primarily to test and layout how the rest of my posts will appear."
}
英文:

I have a GoLang script that is meant to dynamically construct webpages based on an input query in my browser that looks like this http://localhost:8000/blog/post#, the post# portion is what is used to identify which JSON data file to parse into the HTML template I created; for example, if I put http://localhost:8000/blog/post1 then it creates an index.html file from my post1.json file. Currently, my script when run allows me to load a single page in my browser before it exits with the error in my terminal stdout log:

2022/03/18 00:32:02 error: open jsofun.css.json: no such file or directory
exit status 1

this is my current script:

package main
import (
&quot;encoding/json&quot;
&quot;html/template&quot;
&quot;io/ioutil&quot;
&quot;log&quot;
&quot;net/http&quot;
&quot;os&quot;
)
func blogHandler(w http.ResponseWriter, r *http.Request) {
blogstr := r.URL.Path[len(&quot;/blog/&quot;):]
blogstr = blogstr + &quot;.json&quot;
// read the file in locally
json_file, err := ioutil.ReadFile(blogstr)
if err != nil {
log.Fatal(&quot;error: &quot;, err)
}
// define a data structure
type BlogPost struct {
// In order to see these elements later, these fields must be exported
// this means capitalized naming and the json field identified at the end
Title       string `json:&quot;title&quot;`
Timestamp   string `json:&quot;timestamp&quot;`
Main        string `json:&quot;main&quot;`
ContentInfo string `json:&quot;content_info&quot;`
}
// json data
var obj BlogPost
err = json.Unmarshal(json_file, &amp;obj)
if err != nil {
log.Fatal(&quot;error: &quot;, err)
}
tmpl, err := template.ParseFiles(&quot;./blogtemplate.html&quot;)
HTMLfile, err := os.Create(&quot;index.html&quot;)
if err != nil {
log.Fatal(err)
}
defer HTMLfile.Close()
tmpl.Execute(HTMLfile, obj)
http.ServeFile(w, r, &quot;./index.html&quot;)
}
func main() {
http.HandleFunc(&quot;/blog/&quot;, blogHandler)
log.Fatal(http.ListenAndServe(&quot;:8080&quot;, nil))
}

I have done some basic debugging and identified that the issue is coming from the lines:

json_file, err := ioutil.ReadFile(blogstr)
if err != nil {
log.Fatal(&quot;error: &quot;, err)
}

What confuses me is why the ioutil.ReadFile is also trying to read files linked within my HTML? Shouldn't the browser be handling that linkage and not my Handler? For reference this is my HTML where the file jsofun.css is linked; the error referenced in my console output shows me my script is trying to access this file as jsofun.css.json during the call to ioutil.ReadFile:

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
&lt;title&gt;Dynamic JSON Events&lt;/title&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;./jsofun.css&quot;&gt;&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;section id=&quot;title&quot;&gt;
&lt;h1 id=&quot;text-title&quot;&gt;{{.Title}}&lt;/h1&gt;
&lt;time id=&quot;timestamp&quot;&gt;
{{.Timestamp}}
&lt;/time&gt;
&lt;/section&gt;
&lt;nav role=&quot;navigation&quot; id=&quot;site-nav&quot;&gt;
&lt;ul id=&quot;sitemap&quot;&gt;
&lt;/ul&gt;
&lt;/nav&gt;
&lt;main role=&quot;main&quot; id=&quot;main&quot;&gt;
{{.Main}}
&lt;/main&gt;
&lt;footer role=&quot;contentinfo&quot; id=&quot;footer&quot;&gt;
&lt;section id=&quot;content-info&quot; role=&quot;contentinfo&quot;&gt;
{{.ContentInfo}}
&lt;/section&gt;
&lt;form id=&quot;contact-form&quot; role=&quot;form&quot;&gt;
&lt;address&gt;
Contact me by &lt;a id=&quot;my-email&quot; href=&quot;mailto:antonhibl11@gmail.com&quot; class=&quot;my-email&quot;&gt;e-mail&lt;/a&gt;
&lt;/address&gt;
&lt;/form&gt;
&lt;/footer&gt;
&lt;script src=&quot;./jsofun.js&quot;&gt;
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;

I know that I have appended .json to my query string in the line: blogstr = blogstr + &quot;.json&quot;, however, I do not see why this would also affect the links in my HTML. Is there something I am missing or why would it be reading links in my HTML template? All I want the ioutil.ReadFile to do is to read my JSON file which is parsed into my template.

EDIT: I wanted to add that when I load the page initially into the browser it loads successfully with my text content from my JSON but fails to have any styling. When I view the source the links also are correct which confuses me why the stylesheet wouldn't apply because it is in the right directory to be accessible. This is the source when I select view-source in browser:

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
&lt;title&gt;Dynamic JSON Events&lt;/title&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;./jsofun.css&quot;&gt;&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;section id=&quot;title&quot;&gt;
&lt;h1 id=&quot;text-title&quot;&gt;Second Post Test&lt;/h1&gt;
&lt;time id=&quot;timestamp&quot;&gt;
Friday, March 18th, 12:21AM
&lt;/time&gt;
&lt;/section&gt;
&lt;nav role=&quot;navigation&quot; id=&quot;site-nav&quot;&gt;
&lt;ul id=&quot;sitemap&quot;&gt;
&lt;/ul&gt;
&lt;/nav&gt;
&lt;main role=&quot;main&quot; id=&quot;main&quot;&gt;
This is my second post where I am able to use dynamic URL routing
&lt;/main&gt;
&lt;footer role=&quot;contentinfo&quot; id=&quot;footer&quot;&gt;
&lt;section id=&quot;content-info&quot; role=&quot;contentinfo&quot;&gt;
This post was used primarily to test and layout how the rest of my posts will appear.
&lt;/section&gt;
&lt;form id=&quot;contact-form&quot; role=&quot;form&quot;&gt;
&lt;address&gt;
Contact me by &lt;a id=&quot;my-email&quot; href=&quot;mailto:antonhibl11@gmail.com&quot; class=&quot;my-email&quot;&gt;e-mail&lt;/a&gt;
&lt;/address&gt;
&lt;/form&gt;
&lt;/footer&gt;
&lt;script src=&quot;./jsofun.js&quot;&gt;
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;

For reference this is an example of how my JSON file looks:

{
&quot;title&quot; : &quot;Second Post Test&quot;,
&quot;timestamp&quot;: &quot;Friday, March 18th, 12:21AM&quot;,
&quot;main&quot;: &quot;This is my second post where I am able to use dynamic URL routing&quot;,
&quot;content_info&quot;: &quot;This post was used primarily to test and layout how the rest of my posts will appear.&quot;
}

答案1

得分: 1

你的Go服务器只设置了/blog/路径来执行blogHandler来提供服务。在你的Go服务器中没有其他设置来提供像css、js或图像文件这样的资源。

对于这些资源,通常需要在单独的路径上注册一个独立的FileServer处理程序。例如:

func main() {
    http.HandleFunc("/blog/", blogHandler)
    // 要在磁盘上的目录(/path/to/assets/on/my/computer)下提供服务,
    // 并使用StripPrefix来修改请求URL的路径,使其在FileServer看到之前:
    http.Handle("/assets/", http.StripPrefix("/assets/",
        http.FileServer(http.Dir("/path/to/assets/on/my/computer"))))
    log.Fatal(http.ListenAndServe(":8080", nil))
}

另外,你需要修复HTML中对这些资源文件的链接,它们应该是绝对路径,而不是相对路径。

...
<link rel="stylesheet" href="/assets/jsofun.css"></style>
...
<script src="/assets/jsofun.js">

上述代码当然只在资源位于/path/to/assets/on/my/computer目录下时才有效,例如:

/path/to/assets/on/my/computer
├── jsofun.css
└── jsofun.js

你的blogHandler在每个请求中不必要地创建一个新文件,而且没有删除它,这可能会迅速将你的磁盘填满到最大容量。为了提供模板,你不需要创建一个新文件,而是可以直接将模板执行到http.ResponseWriter中。尤其是在生产代码中,建议只解析模板一次,以避免资源的不必要浪费:

type BlogPost struct {
    Title       string `json:"title"`
    Timestamp   string `json:"timestamp"`
    Main        string `json:"main"`
    ContentInfo string `json:"content_info"`
}

var blogTemplate = template.Must(template.ParseFiles("./blogtemplate.html"))

func blogHandler(w http.ResponseWriter, r *http.Request) {
    blogstr := r.URL.Path[len("/blog/"):] + ".json"

    f, err := os.Open(blogstr)
    if err != nil {
        http.Error(w, err.Error(), http.StatusNotFound)
        return
    }
    defer f.Close()

    var post BlogPost
    if err := json.NewDecoder(f).Decode(&post); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    if err := blogTemplate.Execute(w, post); err != nil {
        log.Println(err)
    }
}
英文:

Your Go server is set up to only serve the /blog/ path and it does that by executing the blogHandler. There is nothing else in your Go server that is set up to serve assets like css, js, or image files.

For such things you generally need to register a separate FileServer handler at a separate path. Example:

func main() {
    http.HandleFunc(&quot;/blog/&quot;, blogHandler)
    // To serve a directory on disk (/path/to/assets/on/my/computer)
    // under an alternate URL path (/assets/), use StripPrefix to
    // modify the request URL&#39;s path before the FileServer sees it:
	http.Handle(&quot;/assets/&quot;, http.StripPrefix(&quot;/assets/&quot;,
        http.FileServer(http.Dir(&quot;/path/to/assets/on/my/computer&quot;))))
    log.Fatal(http.ListenAndServe(&quot;:8080&quot;, nil))
}

The other thing you need to fix are the links to those asset fiels in the HTML, they should be absolute, not relative.

...
&lt;link rel=&quot;stylesheet&quot; href=&quot;/assets/jsofun.css&quot;&gt;&lt;/style&gt;
...
&lt;script src=&quot;/assets/jsofun.js&quot;&gt;

The above will of course work only if the assets are located in the /path/to/assets/on/my/computer directory, e.g.

/path/to/assets/on/my/computer
├── jsofun.css
└── jsofun.js

You blogHandler is unnecessarily creating a new file for every request without removing it, this has the potential to very quickly fill up your disk to its max capacity. To serve a template you do not need to create a new file, you can instead execute the template directly into the http.ResposeWriter. It is also advisable to parse the template only once, especially in production code, to avoid unnecessary waste of resources:

type BlogPost struct {
	Title       string `json:&quot;title&quot;`
	Timestamp   string `json:&quot;timestamp&quot;`
	Main        string `json:&quot;main&quot;`
	ContentInfo string `json:&quot;content_info&quot;`
}

var blogTemplate = template.Must(template.ParseFiles(&quot;./blogtemplate.html&quot;))

func blogHandler(w http.ResponseWriter, r *http.Request) {
	blogstr := r.URL.Path[len(&quot;/blog/&quot;):] + &quot;.json&quot;

	f, err := os.Open(blogstr)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}
	defer f.Close()

	var post BlogPost
	if err := json.NewDecoder(f).Decode(&amp;post); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if err := blogTemplate.Execute(w, post); err != nil {
		log.Println(err)
	}
}

答案2

得分: 1

让我们来看看当你请求 http://localhost:8000/blog/post# 时会发生什么。

浏览器发送请求并接收到页面;你的代码成功构建并返回一些 html - 这将包括:

&lt;link rel=&quot;stylesheet&quot; href=&quot;./jsofun.css&quot;&gt;&lt;/style&gt;

浏览器接收并处理 HTML;在此过程中,它请求上述的 css 文件。现在,原始请求是在 /blog/post# 文件夹中,所以 ./jsofun.css 变成了 http://localhost:8000/blog/jsofun.css

当你的 Go 应用程序接收到这个请求时,blogHandler 将被调用(由于请求路径);它会去掉 /blog/,然后添加 .json 来获取文件名 jsofun.css.json。然后你尝试打开这个文件,但会出现错误,因为它不存在。

你可以通过几种方式来修复这个问题;将模板更改为使用 &lt;link rel=&quot;stylesheet&quot; href=&quot;/jsofun.css&quot;&gt;&lt;/style&gt; 可能是一个开始(但我不知道 jsofun.css 存储在哪里,你也没有展示任何能够提供该文件的代码)。我认为值得注意的是,你不必在磁盘上创建 index.html 文件(除非有其他原因需要这样做)。

(有关其他问题和进一步步骤,请参阅 mkopriva 的答案 - 当我输入这个答案时,那个答案已经发布了一半,我觉得这个步骤说明可能仍然有用)。

英文:

Lets work through what happens when you request http://localhost:8000/blog/post#.

The browser requests the page; your code successfully builds and returns some html - this will include:

&lt;link rel=&quot;stylesheet&quot; href=&quot;./jsofun.css&quot;&gt;&lt;/style&gt;

The browser receives and processes the HTML; as part of that process it requests the css above. Now the original request was in the folder /blog/post# so ./jsofun.css becomes http://localhost:8000/blog/jsofun.css.

When your go application receives this request blogHandler will be called (due to the request path); it strips off the /blog/ then adds .json to get the filename jsofun.css.json. You then try to open this file and get the error because it does not exist.

There are a few ways you can fix this; changing the template to use &lt;link rel=&quot;stylesheet&quot; href=&quot;/jsofun.css&quot;&gt;&lt;/style&gt; might be a start (but I don't know where jsofun.css is stored and you don't show any code that would serve that file). I think it's also worth noting that you do not have to create the file index.html on disk (unless there is some other reason for doing this).

(See mkopriva's answer for other issues and further steps - was half way through entering this when that answer was posted and felt the walk through might still be of use).

huangapple
  • 本文由 发表于 2022年3月18日 15:45:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/71523806.html
匿名

发表评论

匿名网友

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

确定