Skip to content

Instantly share code, notes, and snippets.

@nickajacks1
Created January 4, 2024 03:23
Show Gist options
  • Save nickajacks1/2fd8bc71ba3a5a9839ebaf4313082efe to your computer and use it in GitHub Desktop.
Save nickajacks1/2fd8bc71ba3a5a9839ebaf4313082efe to your computer and use it in GitHub Desktop.
Proposal for clarifying Request and Response APIs.
// Sample proposed changes to have Ctx act as an alias to req and res methods where possible
// The Ctx API remains as-is for the most part. The implementation in DefaultCtx would be delegated
// to new Request and Response objects.
// Certain portions of the Ctx API exist because of the lack of distinct Request and Response APIs.
// GetRespHeader, for example, is an artifact of overlap between request and response functionality (Get() was taken!).
// It wouldn't need to exist if users could instead call `c.Res().Get("Content-Type")`
// Gets the request's cookies
func (c *DefaultCtx) Cookies(key string, defaultValue ...string) string {
return c.req.Cookies(key, defaultValue)
}
// Sets the response's cookie
func (c *DefaultCtx) Cookie(cookie *Cookie) {
return c.res.Cookie(cookie)
}
// Get a request header. One can still get response headers with c.Res().Get(...)
func (c *DefaultCtx) Get(key string, defaultValue ...string) string {
return c.req.Get(key, defaultValue...)
}
func (c *DefaultCtx) Set(key, val string) {
c.res.Set(key, val)
}
// Note that there is potential conflict with the existing c.Request() and c.Response() functions,
// which return the raw fasthttp objects.
func (c *DefaultCtx) Req() *Request {
return c.req
}
func (c *DefaultCtx) Res() *Response {
return c.res
}
// Today's ctx.go on v3-beta branch:
// Gets the request's cookies
func (c *DefaultCtx) Cookies(key string, defaultValue ...string) string {
return defaultString(c.app.getString(c.fasthttp.Request.Header.Cookie(key)), defaultValue)
}
// Sets the response's cookie
func (c *DefaultCtx) Cookie(cookie *Cookie) {
fcookie := fasthttp.AcquireCookie()
fcookie.SetKey(cookie.Name)
fcookie.SetValue(cookie.Value)
fcookie.SetPath(cookie.Path)
fcookie.SetDomain(cookie.Domain)
// only set max age and expiry when SessionOnly is false
// i.e. cookie supposed to last beyond browser session
// refer: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#define_the_lifetime_of_a_cookie
if !cookie.SessionOnly {
fcookie.SetMaxAge(cookie.MaxAge)
fcookie.SetExpire(cookie.Expires)
}
fcookie.SetSecure(cookie.Secure)
fcookie.SetHTTPOnly(cookie.HTTPOnly)
switch utils.ToLower(cookie.SameSite) {
case CookieSameSiteStrictMode:
fcookie.SetSameSite(fasthttp.CookieSameSiteStrictMode)
case CookieSameSiteNoneMode:
fcookie.SetSameSite(fasthttp.CookieSameSiteNoneMode)
case CookieSameSiteDisabled:
fcookie.SetSameSite(fasthttp.CookieSameSiteDisabled)
default:
fcookie.SetSameSite(fasthttp.CookieSameSiteLaxMode)
}
c.fasthttp.Response.Header.SetCookie(fcookie)
fasthttp.ReleaseCookie(fcookie)
}
package main
import (
"github.com/gofiber/fiber/v3"
)
func main() {
app := fiber.New()
// This syntax is still valid under the proposed changes
app.Get("/old-stuff", func(c fiber.Ctx) error {
if c.Accepts("text/plain") == "" {
return c.SendStatus(fiber.StatusNotAcceptable)
}
name := c.Cookies("name")
cookie := new(fiber.Cookie)
cookie.Name = "userId"
cookie.Value = "1234"
c.Cookie(cookie)
// ...
return c.SendString("Hello " + name)
})
// Ergonomic mix of Ctx and direct req/res usage
app.Get("/new-stuff", func(c fiber.Ctx) error {
if c.Accepts("text/plain") == "" {
return c.SendStatus(fiber.StatusNotAcceptable)
}
name := c.Req().Cookies("name")
cookie := new(fiber.Cookie)
cookie.Name = "userId"
cookie.Value = "1234"
c.Res().Cookie(cookie)
// ...
return c.SendString("Hello " + name)
})
// Directly using req/res for everything
app.Get("/new-stuff2", func(c fiber.Ctx) error {
if c.Req().Accepts("text/plain") == "" {
return c.Res().SendStatus(fiber.StatusNotAcceptable)
}
name := c.Req().Cookies("name")
cookie := new(fiber.Cookie)
cookie.Name = "userId"
cookie.Value = "1234"
c.Res().Cookie(cookie)
// ...
return c.Res().SendString("Hello " + name)
})
}
// Example of what the Request might look like
type Request struct {
app *App
c Ctx
fasthttp *fasthttp.Request
}
func (req *Request) FastHTTP() *fasthttp.Request {
return req.fasthttp
}
func (req *Request) Get(key string) string {
return req.app.getString(req.fasthttp.Header.Peek(key))
}
func (req *Request) Cookies(key string, defaultValue ...string) string {
return defaultString(req.app.getString(req.fasthttp.Header.Cookie(key)), defaultValue)
}
// Example of what the Response might look like
type Response struct {
app *App
c Ctx // eg for accessing c.req when necessary
fasthttp *fasthttp.Response
}
func (res *Response) FastHTTP() *fasthttp.Response {
return res.fasthttp
}
func (res *Response) Get(key string) string {
return res.app.getString(res.fasthttp.Header.Peek(key))
}
func (res *Response) Set(key, val string) {
res.fasthttp.Header.Set(key, val)
}
func (res *Response) Cookies(key string, defaultValue ...string) string {
return defaultString(res.app.getString(res.fasthttp.Header.Cookie(key)), defaultValue)
}
func (res *Response) Cookie(cookie *Cookie) {
fcookie := fasthttp.AcquireCookie()
fcookie.SetKey(cookie.Name)
fcookie.SetValue(cookie.Value)
fcookie.SetPath(cookie.Path)
fcookie.SetDomain(cookie.Domain)
// only set max age and expiry when SessionOnly is false
// i.e. cookie supposed to last beyond browser session
// refer: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#define_the_lifetime_of_a_cookie
if !cookie.SessionOnly {
fcookie.SetMaxAge(cookie.MaxAge)
fcookie.SetExpire(cookie.Expires)
}
fcookie.SetSecure(cookie.Secure)
fcookie.SetHTTPOnly(cookie.HTTPOnly)
switch utils.ToLower(cookie.SameSite) {
case CookieSameSiteStrictMode:
fcookie.SetSameSite(fasthttp.CookieSameSiteStrictMode)
case CookieSameSiteNoneMode:
fcookie.SetSameSite(fasthttp.CookieSameSiteNoneMode)
case CookieSameSiteDisabled:
fcookie.SetSameSite(fasthttp.CookieSameSiteDisabled)
default:
fcookie.SetSameSite(fasthttp.CookieSameSiteLaxMode)
}
res.Header.SetCookie(fcookie)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment