一个返回给定URL内容的Web服务器

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

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. `&quot;/css/main.css&quot;`. 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 [`&lt;base&gt;`][2] tag:

    import (
    	&quot;github.com/PuerkitoBio/goquery&quot;
    	&quot;fmt&quot;
    )
    
    func main() {
    	doc,err := goquery.NewDocument(&quot;http://www.meaningfultype.com/&quot;)
    
    	if err != nil {
    		panic(err)
    	}
    
    	m := doc.Find(&quot;head&quot;)
    
    	m.Children().First().BeforeHtml(`&lt;base url=&quot;http://localhost/&quot;&gt;`)
    
    	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 (like image/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 (&quot;/&quot;), you could get the requested URL r.URL and use it to get the required resource:

<!-- language: go -->

url := &quot;http://www.meaningfultype.com/&quot;
if r.URL.Host == &quot;&quot; {
    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(&quot;Content-Type&quot;)
w.Header().Set(&quot;Content-Type&quot;, contentType)
w.Write(robots)

The final result:

package main

import (
    &quot;io/ioutil&quot;
    &quot;log&quot;
    &quot;net/http&quot;
)

func main() {
    http.HandleFunc(&quot;/&quot;, factHandler)
    http.ListenAndServe(&quot;:8080&quot;, nil)
}

func factHandler(w http.ResponseWriter, r *http.Request) {
    url := &quot;http://www.meaningfultype.com/&quot;
    if r.URL.Host == &quot;&quot; {
        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(&quot;Content-Type&quot;)
    w.Header().Set(&quot;Content-Type&quot;, contentType)
    w.Write(robots)
}

huangapple
  • 本文由 发表于 2017年4月9日 18:11:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/43305509.html
匿名

发表评论

匿名网友

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

确定