I created a static folder that contains index.html file, and in my go file, I wrote:
package main
import (
"net/http"
)
func main() {
http.Handle("/", http.FileServer(http.Dir("./static")))
http.ListenAndServe(":8482", nil)
}
And it works fine upon exploring http://localhost:8482/
I tried to write the code as:
http.Handle("/static", http.FileServer(http.Dir("./static")))
But it fails upon exploring http://localhost:8482/static with 404 error
http.Handle("/static", http.FileServer(http.Dir("./static"))) simply means, "whenever someone connects to .../static, reroute the entire request to a file server rooted at directory ./static".
However, the url is passed along to the file server as-is. In other words, the file server receives the request from the user, and believes that the user is looking for a file called "static" within the root ("./static") directory.
In fact, if you simply placed a file called "static" in your "./static" directory, going to .../static would serve that file.
So the fix requires two things:
Change the path prefix to "/static/" rather than "/static", so that all files within the static directory can be rerouted to the file server (rather than only the "/static" request)
Strip the "/static/" path prefix from the request before passing it to the file server.
Like so:
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
Related
main.go
package main
import (
"net/http"
)
func main() {
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
http.ListenAndServe(":8080", nil)
}
Directory structure:
%GOPATH%/src/project_name/main.go
%GOPATH%/src/project_name/static/..files and folders ..
Even after reading the documentation I have trouble understanding what exactly http.StripPrefix does here.
1) Why can't I access localhost:8080/static if I remove http.StripPrefix?
2) What URL maps to /static folder if I remove that function?
http.StripPrefix() forwards the handling of the request to one you specify as its parameter, but before that it modifies the request URL by stripping off the specified prefix.
So for example in your case if the browser (or an HTTP client) requests the resource:
/static/example.txt
StripPrefix will cut the /static/ and forward the modified request to the handler returned by http.FileServer() so it will see that the requested resource is
/example.txt
The Handler returned by http.FileServer() will look for and serve the content of a file relative to the folder (or rather FileSystem) specified as its parameter (you specified "static" to be the root of static files).
Now since "example.txt" is in the static folder, you have to specify a relative path to that to get the corrent file path.
Another Example
This example can be found on the http package documentation page (here):
// To serve a directory on disk (/tmp) under an alternate URL
// path (/tmpfiles/), use StripPrefix to modify the request
// URL's path before the FileServer sees it:
http.Handle("/tmpfiles/",
http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp"))))
Explanation:
FileServer() is told the root of static files is "/tmp". We want the URL to start with "/tmpfiles/". So if someone requests "/tempfiles/example.txt", we want the server to send the file "/tmp/example.txt".
In order to achieve this, we have to strip "/tmpfiles" from the URL, and the remaining will be the relative path compared to the root folder "/tmp" which if we join gives:
/tmp/example.txt
Assume that
I have a file
/home/go/src/js/kor.js
Then, tell fileserve serves local directory
fs := http.FileServer(http.Dir("/home/go/src/js"))
Example 1 - root url to Filerserver root
Now file server takes "/" request as "/home/go/src/js"+"/"
http.Handle("/", fs)
Yes, http://localhost/kor.js request tells Fileserver, find kor.js in
"/home/go/src/js" + "/" + "kor.js".
we got kor.js file.
Example2 - custom url to Fileserver root
But, if we add additional resquest name.
http.Handle("/static/", fs)
Now file server takes "/static/" request as "/home/go/src/js"+"/static/"
Yes, http://localhost/static/kor.js request tells Fileserver, find kor.js in
"/home/go/src/js" + "/static/" + "kor.js".
We got 404 error.
Example 3 - custom url to Fileserver root with
so, we modify request url before Fileserver takes it with http.StripPrefix("/tmpfiles/", ...
after stripPrefix Fileserver take / instead /static/
"/home/go/src/js" + "/" + "kor.js".
got kor.js
I'll answer the two questions one by one.
Answer 1 to Question 1:
If your code is written like below:
http.Handle("/static/", http.FileServer(http.Dir("static"))
your root folder is %GOPATH%/src/project_name/static/. When you access localhost:8080/static, the URL /static will be forwarded to the handler returned by http.FileServer(). However, you have no directory or file named static in the root folder.
Answer 2 to Question 2: In general, you cannot access /static folder if you remove the http.StripPrefix(). However, if you have a directory or file named static, you can access it (exactly not the root directory you want) with the URL localhost:8080:/static.
By the way, you cannot access anything if your URL does not begin with /static, because the http.ServeMux will not redirect your request.
For anyone having issues with subdirectories, you need to add a "." because it seems it treats the path as relative only if it's a folder in the root directory of the binary.
For example
s.router.PathPrefix("/logos/").Handler(
http.StripPrefix("/logos/", http.FileServer(http.Dir("./uploads/logos/"))))
Note the "." before "/uploads"
I need to serve an html file to localhost:8080/lvlione but the FileServer function in golang doesn't seem to work.
Here is main.go:
package main
import (
"log" //logging that the server is running and other stuff
"net/http" //serving files and stuff
)
func main() {
//servemux
server := http.NewServeMux()
//handlers that serve the home html file when called
fs := http.FileServer(http.Dir("./home"))
os := http.FileServer(http.Dir("./lvlone")) //!!this is what is broken!!
//handles paths by serving correct files
//there will be if statements down here that check if someone has won or not soon
server.Handle("/", fs)
server.Handle("/lvlione", os)
//logs that server is Listening
log.Println("Listening...")
//starts server
http.ListenAndServe(":8080", server)
}
There is a folder in this directory called lvlone with one file in it (index.html). When I point my browser to localhost:8080/lvlione it returns 404, but when it is pointed to localhost:8080 it returns the correct file.
You need to call http.StripPrefix to remove the extra lvlone from the directory path.
server.Handle("/lvlone/", http.StripPrefix("/lvlone/", os))
By default the http.FileServer assumes the path given to it is the root path, and appends the URL to it. If it is to serve a subdirectory of the virtual path, then that needs to be stripped from the path.
And note that you need to have the trailing slashes in both places.
What's wrong with http.Handle("/static/", http.FileServer(http.Dir("")))?
The shortest example I can find looks like this:
fs := http.FileServer(http.Dir("static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
Is http.StripPrefix necessary?
No it is not required, but if you DO NOT use it the path used to find the file will include the prefix. This is clearer with an example, so imagine your folder structure was:
main.go
static/
styles.css
And you serve the files with:
http.Handle("/static/", http.FileServer(http.Dir("")))
Then a user requesting the file at yoursite.com/static/styles.css would get the styles.css file in the static dir. But for this to work your paths must line up perfectly.
Most people prefer to do the following instead:
fs := http.FileServer(http.Dir("static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
Because they could change their URL path to be something like /assets/ without needing to rename the static dir (or vise versa - change the local dir structure w/out updating the URL path).
TL;DR - Path prefix isn't required but is useful to break any requirements of URL paths and local directory structure matching perfectly.
I'm trying to read from a file in my project's directory.
My problem is, that depending on the caller, the path changes. The caller changes, because I want to unit test this code and the caller is not Main.go anymore.
This is what my project structure looks like:
The code where I try to access specialChars.txt from looks like this:
func RemoveSpecialChars(word string) string {
file, err := ioutil.ReadFile("wordlists/specialChars.txt")
[...]
}
This code works for the start from Main.go but not for the start from CleanupUtil_test.go. To get it working from the test I would need file, err := ioutil.ReadFile("../wordlists/specialChars.txt")
I found answers like this one: https://stackoverflow.com/a/32163888/2837489
_, filename, _, ok := runtime.Caller(0) which is obviously also dependent on the caller.
Is it possible to get the projects root path independent of the calling function?
Or is my code design wrong? Should I pass the file path into the function?
Starting from Go 1.16, you can use the embed package. This allows you to embed the files in the running go program. It comes with the caveat that the referenced directory needs to exist at or below the embedding file. In your case, the structure would look as follows:
-- main.go
-- cleanup
-- wordlist
\- specialChars.txt
CleanupUtil.go
CleanupUtil_test.go
You can reference the file using a go directive
// CleanupUtil.go
package cleanup
import (
"embed"
)
//go:embed wordlists/specialChars.txt
var content embed.FS
func RemoveSpecialChars(word string) string {
file, err := content.ReadFile("wordlists/specialChars.txt")
[...]
}
This program will run successfully regardless of where the program is executed. You should be able to reference this code in both your main.go file and your CleanupUtil_test.go file.
Pass in the filepath as a parameter to the function (as indicated in your last question).
More details:
The relative path "wordlists/specialChars.txt" is in fact not dependent on where the source file is located (such as Main.go or CleanupUtil_test.go), but where you execute it from. So you could run your tests from your root directory and then it would actually work. In short, the current working directory is relevant.
Still, I'd recommend specifying the path, because that makes your function more reusable and universal.
Maybe you don't even need to put this information into a file, but can simply have a string containing those chars. In this case you could also check if https://golang.org/pkg/regexp/#Regexp.ReplaceAll already covers your use case.
I have a project with next structure:
|_main.go
|_config
|_config.go
|_config_test.go
|_config.json
I'm having next code line in config.go:
file, _ := os.Open("config/config.json")
When I'm executing method contained this code line from main.go all is working. But when I'm trying to execute this method from config_test.go it produces error:
open config/config.json: no such file or directory
As I understood it is a working directory issue because I'm launching same code with relative path from different directories. How can I fix this problem without using full path in config.go?
Relative paths are always resolved basis your current directory. Hence it's better to avoid relative paths.
Use command line flags or a configuration management tool (better approach) like Viper
Also according to The Twelve-Factor App your config files should be outside your project.
Eg usage with Viper:
import "github.com/spf13/viper"
func init() {
viper.SetConfigName("config")
// Config files are stored here; multiple locations can be added
viper.AddConfigPath("$HOME/configs")
errViper := viper.ReadInConfig()
if errViper != nil {
panic(errViper)
}
// Get values from config.json
val := viper.GetString("some_key")
// Use the value
}