英文:
A web server that return the content of given url
问题
我写了一个简单的网络服务器,它通过http.ResponseWriter
获取给定URL的内容并将其写出。但问题是它在页面上不显示任何图像和CSS。以下是代码:
func factHandler(w http.ResponseWriter, r *http.Request) {
res, err := http.Get("http://www.meaningfultype.com/")
if err != nil {
log.Fatal(err)
}
robots, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
log.Fatal(err)
}
w.Write(robots)
}
我需要做什么改变才能返回在Web浏览器中看到的整个页面内容?
英文:
I wrote a simple web sever that gets the content of a given URL and writes it out using a http.ResponseWriter
. But the problem is that it does not display any images and CSS on the page. The code is given bellow:
func factHandler(w http.ResponseWriter, r *http.Request) {
res, err := http.Get("http://www.meaningfultype.com/")
if err != nil {
log.Fatal(err)
}
robots, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
log.Fatal(err)
}
w.Write(robots)
}
What do I need to change so that the the whole page content as seen in the web browser is returned?
答案1
得分: 2
这里的问题可能是你所提到的网站在使用相对路径来引用图片和样式表,例如"/css/main.css"
。你通过Go服务向浏览器提供的本地网站具有另一个域名(例如localhost
),因此浏览器无法解析相对路径(没有http://localhost/css/main.css
)。
所以你需要做的是在你提供的文档中设置一个基本URL,或者将每个相对路径重写为绝对路径(/css/main.css
重写为http://originalwebsite.com/css/main.css
)。
为了添加基本标签或重写文档中的URL,我建议使用类似于goquery的工具,它可以让你像jQuery一样操作HTML结构。
以下是添加<base>
标签的示例代码:
import (
"github.com/PuerkitoBio/goquery"
"fmt"
)
func main() {
doc,err := goquery.NewDocument("http://www.meaningfultype.com/")
if err != nil {
panic(err)
}
m := doc.Find("head")
m.Children().First().BeforeHtml(`<base url="http://localhost/">`)
fmt.Println(doc.Html())
}
<details>
<summary>英文:</summary>
The problem here is presumably that the website you refer is using relative paths for the images and stylesheets, e.g. `"/css/main.css"`. The local website that you deliver to the browser with your Go service has another domain (for example `localhost`) and thus the relative path cannot be resolved by the browser (there is no `http://localhost/css/main.css`).
So what you need to do is either set a base URL in the document you deliver or, alternatively, rewrite every relative path to an absolute path (`/css/main.css` to `http://originalwebsite.com/css/main.css`).
For adding the base tag or rewriting the URLs in the document I suggest employing something like [goquery][1] which lets you manipulate the HTML structure similar to how jQuery does it.
Example of adding a [`<base>`][2] tag:
import (
"github.com/PuerkitoBio/goquery"
"fmt"
)
func main() {
doc,err := goquery.NewDocument("http://www.meaningfultype.com/")
if err != nil {
panic(err)
}
m := doc.Find("head")
m.Children().First().BeforeHtml(`<base url="http://localhost/">`)
fmt.Println(doc.Html())
}
[1]: https://godoc.org/github.com/PuerkitoBio/goquery
[2]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
</details>
# 答案2
**得分**: 0
# TL;DR
- 你只是提供了根HTML页面,你需要响应其他资源的请求(可以通过*http.Request变量的URL变量查看请求的资源是什么)
- 在提供资源时,你需要写入Header的`Content-Type`,以让浏览器知道资源的类型(比如`image/png`)
# 完整回答
在你的请求处理程序中,你获取了`http://www.meaningfultype.com/`,即HTML页面,然后浏览器找到了像`/images/header-logo.png`这样的图片,并发送请求获取它,但是你的本地服务器不知道如何响应`http://localhost/images/header-logo.png`。
假设你的处理函数处理的是服务器的根目录(`"/"`),你可以获取请求的URL `r.URL`,并使用它来获取所需的资源:
```go
url := "http://www.meaningfultype.com/"
if r.URL.Host == "" {
url += r.URL.String()
} else {
url = r.URL.String()
}
res, err := http.Get(url)
问题是,即使这样做了,所有的资源仍然以plain/text
发送,因为你没有设置Header的Content-Type
。这就是为什么在写入之前需要指定资源的类型。为了知道资源的类型,只需从刚刚在http.Get
的响应Header中接收到的Content-Type
中获取:
contentType := res.Header.Get("Content-Type")
w.Header().Set("Content-Type", contentType)
w.Write(robots)
最终结果:
package main
import (
"io/ioutil"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", factHandler)
http.ListenAndServe(":8080", nil)
}
func factHandler(w http.ResponseWriter, r *http.Request) {
url := "http://www.meaningfultype.com/"
if r.URL.Host == "" {
url += r.URL.String()
} else {
url = r.URL.String()
}
res, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
robots, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
log.Fatal(err)
}
contentType := res.Header.Get("Content-Type")
w.Header().Set("Content-Type", contentType)
w.Write(robots)
}
英文:
TL;DR
-
You are only serving the root HTML page, you need to respond to requests to other resources too (you can see what resource is being requested with the URL variable of the *http.Request variable)
-
When serving the resources, you need to write the Header's
Content-Type
to let the browser know what's the type of the resource (likeimage/png
)
Full Answer
What you are doing in your request handler is getting http://www.meaningfultype.com/
, the HTML page, then the browser finds an image like /images/header-logo.png
and makes the request to get it but your server in localhost doesn't know how to respond to http://localhost/images/header-logo.png
.
Assuming your handler function is handling requests at the root of the server ("/"
), you could get the requested URL r.URL
and use it to get the required resource:
<!-- language: go -->
url := "http://www.meaningfultype.com/"
if r.URL.Host == "" {
url += r.URL.String()
} else {
url = r.URL.String()
}
res, err := http.Get(url)
The problem is that, even after doing this, all the resources are sent as plain/text
because you are not setting the Header's Content-Type
. That's why you need to specify the type of the resource before writing. In order to know what the type of the resource is, just get it from the Content-Type
you just received in the Header of the response of http.Get
:
<!-- language: go -->
contentType := res.Header.Get("Content-Type")
w.Header().Set("Content-Type", contentType)
w.Write(robots)
The final result:
package main
import (
"io/ioutil"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", factHandler)
http.ListenAndServe(":8080", nil)
}
func factHandler(w http.ResponseWriter, r *http.Request) {
url := "http://www.meaningfultype.com/"
if r.URL.Host == "" {
url += r.URL.String()
} else {
url = r.URL.String()
}
res, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
robots, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
log.Fatal(err)
}
contentType := res.Header.Get("Content-Type")
w.Header().Set("Content-Type", contentType)
w.Write(robots)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论