I'm writing a small web app with gorilla/mux. My folder structure inside my GOPATH is like this.
app/
- app.go
- views/
- layout.html
- login/
- login.go
- login_test.go
- login.html
I'd like to keep login as a completely separate package. Within login.go I initiate the template and render it on request. All file paths are relative to app.go as I run go run app.go in my main app/ folder.
package login
var view = template.Must(template.ParseFiles(
"login/login.html",
"views/layout.html",
))
func GetLogin(w http.ResponseWriter, r *http.Request) {
err := view.ExecuteTemplate(w, "layout", nil)
check(err)
}
That works fine and I can call the route in my app.go file.
import (
"github.com/zemirco/app/login"
)
func main() {
router := mux.NewRouter()
router.HandleFunc("/login", login.GetLogin).Methods("GET")
http.Handle("/", router)
http.ListenAndServe(":"+os.Getenv("PORT"), nil)
}
My problem is testing this route. Within login_test.go I have something like this.
package login
import (
"net/http"
"net/http/httptest"
"testing"
"."
)
func TestHandleGetLogin(t *testing.T) {
request, _ := http.NewRequest("GET", "/", nil)
response := httptest.NewRecorder()
login.GetLogin(response, request)
t.Log(response)
}
Whenever I run go test login/login_test.go I get the error message
open login/login.html: no such file or directory
As far as I know go test executes from within the login/ directory and therefore cannot find the html files. The relative file paths are wrong.
How can I solve this problem or what would be a better directory structure?
Related
I'm creating an endpoint using Go's Gin web framework. I need full server URL in my handler function. For example, if server is running on http://localhost:8080 and my endpoint is /foo then I need http://localhost:8080/foo when my handler is called.
If anyone is familiar with Python's fast API, the Request object has a method url_for(<endpoint_name>) which has the exact same functionality: https://stackoverflow.com/a/63682957/5353128
In Go, I've tried accessing context.FullPath() but that only returns my endpoint /foo and not the full URL. Other than this, I can't find appropriate method in docs: https://pkg.go.dev/github.com/gin-gonic/gin#Context
So is this possible via gin.Context object itself or are there other ways as well? I'm completely new to Go.
c.Request.Host+c.Request.URL.Path should work but the scheme has to be determined.
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/foo", func(c *gin.Context) {
fmt.Println("The URL: ", c.Request.Host+c.Request.URL.Path)
})
r.Run(":8080")
}
You can determine scheme which also you may know already. But you can check as follows:
scheme := "http"
if c.Request.TLS != nil {
scheme = "https"
}
If your server is behind the proxy, you can get the scheme by c.Request.Header.Get("X-Forwarded-Proto")
You can get host part localhost:8080 from context.Request.Host and path part /foo from context.Request.URL.String().
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/foo", func(c *gin.Context) {
c.String(http.StatusOK, "bar")
fmt.Println(c.Request.Host+c.Request.URL.String())
})
// Listen and Server in 0.0.0.0:8080
r.Run(":8080")
}
And you can get http protocol version by context.Request.Proto, But it will not determine http or https. you need to get it from your service specifications.
I am trying to build a web app with two files.
app.go and main.go are both in the same directory.
app.go
package main
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"github.com/gorilla/mux"
)
type App struct {
Router *mux.Router
DB *sql.DB
}
func (a *App) Initialize(username, password, server, port, dbName, cacheAddr, cachePass string){
}
func (a *App) Run(addr string) {
}
main.go
package main
func main() {
a := App{}
// more code here
}
I thought my main.go file would recognize App{} but my editor is complaining that App is undeclared name
Both files are in the same main package but I am not sure what went wrong. Could anyone help me about it? Thank you!
From the comments I assume you run the following command: go run main.go. This will ONLY load code in main.go (and files included with import statements). To tell Go to load all .go files in the current directory, run the following instead:
go run .
Similarly, to tell VSCode to load alll files start it like this:
code .
I am having some trouble serving some files from a subdirectory when a client request comes in at the root directory.
I am using gorilla/mux to serve files. Here is my code below:
package main
import (
"log"
"net/http"
"time"
"github.com/gorilla/mux"
"github.com/zhughes3/zacharyhughes.com/configparser"
)
var config configparser.Configuration
func main() {
config := configparser.GetConfiguration()
r := mux.NewRouter()
initFileServer(r)
server := &http.Server{
Handler: r,
Addr: ":" + config.Server.Port,
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
log.Fatal(server.ListenAndServe())
}
func initFileServer(r *mux.Router) {
fs := http.FileServer(http.Dir("public/"))
r.PathPrefix("/public/").Handler(http.StripPrefix("/public/", fs))
}
The code that creates the file system is in the initFileServer function above.
Currently, it serves the static files as intended when the user goes to localhost:3000/public/. However, I want the files to be served when the user goes to localhost:3000/.
I tried changing the ParsePrefix function call to r.PathPrefix("/").Handler(http.StripPrefix("/public/", fs)) but it did not work. Any suggestions would be greatly appreciated...I am pretty new with Go.
Your file server is already serving files under /public, so you don't need to strip the prefix from the http path. This should work:
r.PathPrefix("/").Handler(http.StripPrefix("/",fs))
I saw the similar question here. But I couldn't solve my case.
I am having project initialised with dep and added the first dependency "Echo". Now folder structure looks like this
|--server
| |--server.go
|--vendor
|--main.go
The server.go has the following code
package server
import (
"net/http"
"github.com/labstack/echo"
)
// TestController : Test controller
func TestController(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
}
and the main.go has
package main
import (
"github.com/labstack/echo"
"github.com/sfkshan/pos/server"
)
func main() {
e := echo.New()
e.GET("/", server.TestController)
e.Logger.Fatal(e.Start(":1323"))
}
Now vscode shows the warning
cannot use server.TestController (type
func("github.com/sfkshan/pos/vendor/github.com/labstack/echo".Context)
error) as type "github.com/labstack/echo".HandlerFunc in argument to
e.GET
I am not sure why is this happening? If I delete the vendor folder folder the error vanishes. But again after running dep ensure (in this case vendor folder gets created which is expected) the error appears again.
package main
import (
"log"
"net/http"
)
func main() {
fs := http.FileServer(http.Dir("."))
http.Handle("/", fs)
log.Println("Listening...")
http.ListenAndServe(":3000", nil)
}
So I have a index.html file and want server to stop showing it.
The docs for FileServer state that:
As a special case, the returned file server redirects any request
ending in "/index.html" to the same path, without the final
"index.html".
So /index.html is redirected to /, /foo/bar/index.html is redirected to /foo/bar/.
To avoid this register an additional handler for the special case.
http.HandleFunc("/index.html", func(w http.ResponseWriter, r *http.Request) {
f, err := os.Open("index.html")
if err != nil {
// handle error
return
}
http.ServeContent(w, r, "index.html", time.Now(), f)
})
Please note I'm using ServeContent insead of ServeFile because ServeFile handles /index.html requests the same way as FileServer.
There's no redirection going on, the default file to render when requesting a directory is index.html. The directory listing is a fallback for when this file isn't found, so you can't get a directory listing without removing the index.html file.
If you want a directory listing, you'll have to write it out yourself, which you can then format and style however you choose. The basic structure is very simple if you want to write it directly, take the internal dirList function for example:
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintf(w, "<pre>\n")
for _, d := range dirs {
name := d.Name()
if d.IsDir() {
name += "/"
}
url := url.URL{Path: name}
fmt.Fprintf(w, "%s\n", url.String(), htmlReplacer.Replace(name))
}
fmt.Fprintf(w, "</pre>\n")
I think there are 3 ways:
Make a PR which allow accessing /index.html without redirect to golang's net/http/fs library. https://github.com/golang/go/issues/53870
Hack your code: replace /index.html with / to stop 301 redirect, e.g., https://github.com/ahuigo/go-lib/commit/1a99191d5c01cf9025136ce8ddb9668f123de05c#diff-67e8621fbb99281a50c089bae53d4874663d3d21ca5e90809ec207c070029351R44
Customize your own http fs handler instead of official tools.