How to add custom http header in go fasthttp ? - http

I am using a fasthttp server https://github.com/valyala/fasthttp
I need to add a custom header for all requests
Access-Control-Allow-Origin: *
How can I do this ?

Since it's a response header i assume you mean this:
ctx.Response.Header.Set("Access-Control-Allow-Origin", "*")

Another option if you are not using Context:
func setResponseHeader(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
h.ServeHTTP(w, r)
}
}
setResponseHeader is essentially a decorator of the argument HandlerFunc h. When you assemble your routes, you can do something like this:
http.HandleFunc("/api/endpoint", setResponseHeader(myHandlerFunc))
http.ListenAndServe(":8000", nil)

To enable CORS support on fasthttp, better use fasthttpcors package.
import (
...
cors "github.com/AdhityaRamadhanus/fasthttpcors"
...
)
func main() {
...
withCors := cors.NewCorsHandler(cors.Options{
AllowMaxAge: math.MaxInt32,
})
log.Fatal(fasthttp.ListenAndServe(":8080", withCors.CorsMiddleware(router.HandleRequest)))
}

Related

Stylesheet not loaded because of MIME-type in go [duplicate]

I'm having issues with including css files in a GO webserver using Gorilla Mux. I get the following error in the Google Chrome console:
forum:1 Refused to apply style from 'http://localhost:8080/css/forum.css' because its MIME type ('text/plain') is not a supported stylesheet MIME type, and strict MIME checking is enabled.
I understand that a lot of people fail using a FileServer by handling the "/" wrong, but this also won't work for me. My file struture is as follow:
file structure
When I run the server, I execute in cmd: go run src/main.go. I also tried running it in the src folder. But that won't work too. In the HTML file, I add the css file with
<link rel="stylesheet" type="text/css" href="/css/forum.css" />
My GO code is below. I tried handling the FileServer in two ways, one of them is commented out above the other. Both won't work. Everything else is working except the FileServer.
package main
import (
"fmt"
"net/http"
"html/template"
"github.com/gorilla/mux"
)
var templates *template.Template
func main() {
r := mux.NewRouter()
templates = template.Must(template.ParseGlob("src/templates/*.html"))
cssHandler := http.FileServer(http.Dir("./static/css"))
r.HandleFunc("/home", homeGetHandler).Methods("GET")
r.HandleFunc("/home", homePostHandler).Methods("POST")
r.HandleFunc("/forum", forumGetHandler).Methods("GET")
r.HandleFunc("/forum", forumPostHandler).Methods("POST")
http.Handle("/forum", r)
http.Handle("/home", r)
// http.Handle("/css/", http.StripPrefix("/src/static/css/", cssHandler))
r.PathPrefix("/css/").Handler(http.StripPrefix("/src/static/css/", cssHandler))
http.ListenAndServe(":8080", nil)
}
func homeGetHandler(w http.ResponseWriter, r *http.Request) {
templates.ExecuteTemplate(w, "home.html", nil)
}
func homePostHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
comment := r.PostForm.Get("comment")
fmt.Println(comment)
http.Redirect(w, r,"/home", 302)
}
func forumGetHandler(w http.ResponseWriter, r *http.Request) {
templates.ExecuteTemplate(w, "forum.html", nil)
}
func forumPostHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
comment := r.PostForm.Get("post")
fmt.Println(comment)
http.Redirect(w, r,"/forum", 302)
}
[SOLUTION]
I found the answer:
http.Handle("/forum", r)
http.Handle("/home", r)
should just be:
http.Handle("/",r)
What's because you're serving your css file with wrong MIME type, you should set right header for css. Use:
func serveCss(w http.ResponseWriter, r *http.Request) {
// some code here
w.Header().Add("Content-Type", "text/css")
// some code here
}
The problem is that your csshandler returns the contents of the file with Content-Type set to "text/plain". You have to set it to "text/css" to have the browser interpret it as a CSS file. You can set the content type before returning the file contents using a middleware-like function:
func SetHeader(header,value string, handle http.Handler) func(http.ResponseWriter,*http.Request) {
return func(w http.ResponseWriter,req *http.Request) {
w.Header().Set(header,value)
handle.ServeHTTP(w,req)
}
}
r.PathPrefix("/css/").HandlerFunc(SetHeader("Content-Type","text/css",http.StripPrefix("/src/static/css/", cssHandler)))

Handling custom 404 pages with http.FileServer

I'm currently using a basic http.FileServer setup to serve a simple static site. I need to handle 404 errors with a custom not found page. I've been looking into this issue quite a bit, and I cannot determine what the best solution is.
I've seen several responses on GitHub issues along the lines of:
You can implement your own ResponseWriter which writes a custom message after WriteHeader.
It seems like this is the best approach but I'm a bit unsure of how this would actually be implemented. If there are any simple examples of this implementation, it'd be greatly appreciated!
I think this can be solved with your own middleware. You can try to open the file first and if it doesn't exist, call your own 404 handler. Otherwise just dispatch the call to the static file server in the standard library.
Here is how that could look:
package main
import (
"fmt"
"net/http"
"os"
"path"
)
func notFound(w http.ResponseWriter, r *http.Request) {
// Here you can send your custom 404 back.
fmt.Fprintf(w, "404")
}
func customNotFound(fs http.FileSystem) http.Handler {
fileServer := http.FileServer(fs)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := fs.Open(path.Clean(r.URL.Path)) // Do not allow path traversals.
if os.IsNotExist(err) {
notFound(w, r)
return
}
fileServer.ServeHTTP(w, r)
})
}
func main() {
http.ListenAndServe(":8080", customNotFound(http.Dir("/path/to/files")))
}

How do I pass a URL as a parameter in a Golang route?

I'm trying to pass a URL as a parameter in Golang, and I haven't been able to find a solution in all of the tutorials I've looked at. The problem is that I can only get the url to return minus a crucial forward slash.
My handler looks like this:
router.HandleFunc("/new/{url}", createURL)
So the request would look like:
www.myapp.heroku.com/new/https://www.google.com
However, the url that I results is missing a slash:
http:/www.google.com
I sure it's probably got something to do with RFC3986, but is there a way to pass in the url as it is?
After reading the other question, I understand what do you mean. Implement a kind of URL re-writer before URL goes to gorilla/mux. The function will look like:
func Rewriter(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//Simple URL rewriter. Rewrite if it's started with API path
pathReq := r.RequestURI
if strings.HasPrefix(pathReq, "/new/") {
//Use url.QueryEscape for pre go1.8
pe := url.PathEscape(strings.TrimLeft(pathReq, "/new/"))
r.URL.Path = "/new/" + pe
r.URL.RawQuery = ""
}
h.ServeHTTP(w, r)
})
}
Wrap gorilla router when starting the http server:
r := mux.NewRouter()
// ... other handler
r.HandleFunc("/new/{original-url}", NewHandler)
//Wrap mux.Router using Rewriter
log.Fatal(http.ListenAndServe(":8080", Rewriter(r)))
Then in your URL shortener handler, the original URL can be extracted using the following code:
func NewHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
ou := vars["original-url"]
//Use url.QueryUnascape for pre go1.8
originalURL, err := url.PathUnescape(ou)
//... other processing
}
IMHO, implementing URL shortener service like this is not recommended, mainly due to incorrect use of HTTP verbs. Any GET request should not leave side effect in server e.g. no record creation in database, etc.
This particular behavior in Gorilla Mux can be changed by setting SkipClean to true.
router := mux.NewRouter()
router.SkipClean(true)
router.HandleFunc("/new/", index)
router.HandleFunc("/", index)
http.ListenAndServe(":"+port, router)
The relevant documentation can be found here.

How to process GET operation (CRUD) in go lang via Postman?

I want to perform a get operation. I am passng name as a resource to the URL.
The URL I am hitting in Postman is : localhost:8080/location/{titan rolex} ( I chose the GET method in the dropdown list)
On the URL hit in Postman, I am executing the GetUser func() with body as:
func GetUser(rw http.ResponseWriter, req *http.Request) {
}
Now I wish to get the resource value i.e 'titan rolex' in the GetUser method.
How can I achieve this in golang?
In main(), I have this :
http.HandleFunc("/location/{titan rolex}", GetUser)
Thanks in advance.
What you are doing is binding the complete path /location/{titan rolex} to be handled by GetUser.
What you really want is to bind /location/<every possible string> to be handled by one handler (e.g. LocationHandler).
You can do that with either the standard library or another router. I will present both ways:
Standard lib:
import (
"fmt"
"net/http"
"log"
)
func locationHandler(w http.ResponseWriter, r *http.Request) {
name := r.URL.Path[len("/location/"):]
fmt.Fprintf(w, "Location: %s\n", name)
}
func main() {
http.HandleFunc("/location/", locationHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Note however, more complex paths (such as /location/<every possible string>/<some int>/<another string>) will be tedious to implement this way.
The other way is to use github.com/julienschmidt/httprouter, especially if you encounter these situations more often (and have more complex paths).
Here's an example for your use case:
import (
"fmt"
"github.com/julienschmidt/httprouter"
"net/http"
"log"
)
func LocationHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
fmt.Fprintf(w, "Location: %s\n", ps.ByName("loc"))
}
func main() {
router := httprouter.New()
router.GET("/location/:loc", LocationHandler)
log.Fatal(http.ListenAndServe(":8080", router))
}
Note that httprouter uses a slightly different signature for handlers. This is because, as you can see, it passes these parameters to the functions as well.
Oh and another note, you can just hit http://localhost:8080/location/titan rolex with your browser (or something else) - if that something else is decent enough, it will URLEncode that to be http://localhost:8080/location/titan%20rolex.

Serving gzipped content for Go

I'm starting to write server-side applications in Go. I'd like to use the Accept-Encoding request header to determine whether to compress the response entity using GZIP. I had hoped to find a way to do this directly using the http.Serve or http.ServeFile methods.
This is quite a general requirement; did I miss something or do I need to roll my own solution?
The New York Times have released their gzip middleware package for Go.
You just pass your http.HandlerFunc through their GzipHandler and you're done. It looks like this:
package main
import (
"io"
"net/http"
"github.com/nytimes/gziphandler"
)
func main() {
withoutGz := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
io.WriteString(w, "Hello, World")
})
withGz := gziphandler.GzipHandler(withoutGz)
http.Handle("/", withGz)
http.ListenAndServe("0.0.0.0:8000", nil)
}
There is no “out of the box” support for gzip-compressed HTTP responses yet. But adding it is pretty trivial. Have a look at
https://gist.github.com/the42/1956518
also
https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/cgUp8_ATNtc
For the sake of completeness, I eventually answered my own question with a handler that is simple and specialises in solving this issue.
https://pkg.go.dev/github.com/rickb777/servefiles?tab=doc
https://github.com/rickb777/servefiles
This serves static files from a Go http server, including the asked-for performance-enhancing features. It is based on the standard net/http ServeFiles, with gzip/brotli and cache performance enhancements.
There is yet another "out of the box" middleware now, supporting net/http and Gin:
https://github.com/nanmu42/gzip
net/http example:
import github.com/nanmu42/gzip
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
writeString(w, fmt.Sprintf("This content is compressed: l%sng!", strings.Repeat("o", 1000)))
})
// wrap http.Handler using default settings
log.Println(http.ListenAndServe(fmt.Sprintf(":%d", 3001), gzip.DefaultHandler().WrapHandler(mux)))
}
func writeString(w http.ResponseWriter, payload string) {
w.Header().Set("Content-Type", "text/plain; charset=utf8")
_, _ = io.WriteString(w, payload+"\n")
}
Gin example:
import github.com/nanmu42/gzip
func main() {
g := gin.Default()
// use default settings
g.Use(gzip.DefaultHandler().Gin)
g.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, map[string]interface{}{
"code": 0,
"msg": "hello",
"data": fmt.Sprintf("l%sng!", strings.Repeat("o", 1000)),
})
})
log.Println(g.Run(fmt.Sprintf(":%d", 3000)))
}

Resources