diff --git a/ctx.go b/ctx.go index 6958836f..25ee773a 100644 --- a/ctx.go +++ b/ctx.go @@ -65,6 +65,11 @@ type ctx[B any] interface { // By default, [templateToExecute] is added to the list of templates to override. Render(templateToExecute string, data any, templateGlobsToOverride ...string) (HTML, error) + Cookie(name string) (*http.Cookie, error) // Get request cookie + SetCookie(cookie http.Cookie) // Sets response cookie + Header(key string) string // Get request header + SetHeader(key, value string) // Sets response header + Context() context.Context Request() *http.Request // Request returns the underlying http request. @@ -162,6 +167,26 @@ func (c ContextNoBody) Context() context.Context { return c.Req.Context() } +// Get request header +func (c ContextNoBody) Header(key string) string { + return c.Request().Header.Get(key) +} + +// Sets response header +func (c ContextNoBody) SetHeader(key, value string) { + c.Response().Header().Set(key, value) +} + +// Get request cookie +func (c ContextNoBody) Cookie(name string) (*http.Cookie, error) { + return c.Request().Cookie(name) +} + +// Sets response cookie +func (c ContextNoBody) SetCookie(cookie http.Cookie) { + http.SetCookie(c.Response(), &cookie) +} + // Render renders the given templates with the given data. // It returns just an empty string, because the response is written directly to the http.ResponseWriter. // diff --git a/documentation/docs/guides/controllers.mdx b/documentation/docs/guides/controllers.mdx index 0680c157..2e96a75b 100644 --- a/documentation/docs/guides/controllers.mdx +++ b/documentation/docs/guides/controllers.mdx @@ -4,8 +4,6 @@ import FlowChart from '@site/src/components/FlowChart'; Controllers are the main way to interact with the application. They are responsible for handling the requests and responses. - - ## Controller types @@ -48,7 +46,7 @@ type MyResponse struct { Message string `json:"message"` } -func MyController(ctx *fuego.ContextWithBody[MyInput]) (MyResponse, error) { +func MyController(c *fuego.ContextWithBody[MyInput]) (MyResponse, error) { body, err := c.Body() if err != nil { return MyResponse{}, err @@ -60,3 +58,44 @@ func MyController(ctx *fuego.ContextWithBody[MyInput]) (MyResponse, error) { } ``` +## Headers + +You can always go further in the request and response by using the underlying net/http request and response, by using `c.Request` and `c.Response`. + +### Get request header + +```go +func MyController(c *fuego.ContextNoBody) (MyResponse, error) { + value := c.Header("X-My-Header") + return MyResponse{}, nil +} +``` + +### Set response header + +```go +func MyController(c *fuego.ContextNoBody) (MyResponse, error) { + c.SetHeader("X-My-Header", "value") + return MyResponse{}, nil +} +``` + +## Cookies + +### Get request cookie + +```go +func MyController(c *fuego.ContextNoBody) (MyResponse, error) { + value := c.Cookie("my-cookie") + return MyResponse{}, nil +} +``` + +### Set response cookie + +```go +func MyController(c *fuego.ContextNoBody) (MyResponse, error) { + c.SetCookie("my-cookie", "value") + return MyResponse{}, nil +} +``` diff --git a/documentation/docs/guides/openapi.md b/documentation/docs/guides/openapi.md index 9d11c345..f418033f 100644 --- a/documentation/docs/guides/openapi.md +++ b/documentation/docs/guides/openapi.md @@ -81,9 +81,10 @@ func main() { s := fuego.NewServer(fuego.WithOpenAPIConfig(fuego.OpenAPIConfig{ DisableSwagger : false, // If true, the server will not serve the swagger ui nor the openapi json spec DisableLocalSave : false, // If true, the server will not save the openapi json spec locally - SwaggerUrl : "/xxx", // URL to serve the swagger ui - JsonUrl : "/xxx/swagger.json", // URL to serve the openapi json spec - JsonFilePath : "./foo/bar.json", // Local path to save the openapi json spec + SwaggerUrl : "/swagger", // URL to serve the swagger ui + JsonUrl : "/swagger/openapi.json", // URL to serve the openapi json spec + JsonFilePath : "doc/openapi.json", // Local path to save the openapi json spec + UIHandler : DefaultOpenAPIHandler, // Custom UI handler })) fuego.Get(s, "/", func(c fuego.ContextNoBody) (string, error) { @@ -114,6 +115,18 @@ func openApiHandler(specURL string) http.Handler { httpSwagger.URL(specURL), // The url pointing to API definition ) } + +func main() { + s := fuego.NewServer( + fuego.WithOpenAPIConfig(fuego.OpenAPIConfig{ + UIHandler: openApiHandler("/swagger.json"), + }), + ) + + fuego.Get(s, "/", helloWorld) + + s.Run() +} ``` The default spec url reference Element Stoplight swagger ui. @@ -157,4 +170,4 @@ func main() { s.Run() } -``` \ No newline at end of file +``` diff --git a/middleware/cache/cache_test.go b/middleware/cache/cache_test.go index 6013ffc9..df6a9822 100644 --- a/middleware/cache/cache_test.go +++ b/middleware/cache/cache_test.go @@ -17,7 +17,7 @@ type testStruct struct { const waitTime = 10 * time.Millisecond -func baseController(ctx *fuego.ContextNoBody) (testStruct, error) { +func baseController(c *fuego.ContextNoBody) (testStruct, error) { time.Sleep(waitTime) return testStruct{Name: "test", Age: 10}, nil } diff --git a/mux_test.go b/mux_test.go index 6b225438..b84028f1 100644 --- a/mux_test.go +++ b/mux_test.go @@ -2,6 +2,7 @@ package fuego import ( "encoding/json" + "fmt" "net/http" "net/http/httptest" "strings" @@ -494,3 +495,44 @@ func TestGroup(t *testing.T) { require.Equal(t, "/slash/", g.basePath) }) } + +func ExampleContextNoBody_SetCookie() { + s := NewServer() + Get(s, "/test", func(c *ContextNoBody) (string, error) { + c.SetCookie(http.Cookie{ + Name: "name", + Value: "value", + }) + return "test", nil + }) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "/test", nil) + + s.Mux.ServeHTTP(w, r) + + fmt.Println(w.Result().Cookies()[0].Name) + fmt.Println(w.Result().Cookies()[0].Value) + + // Output: + // name + // value +} + +func ExampleContextNoBody_SetHeader() { + s := NewServer() + Get(s, "/test", func(c *ContextNoBody) (string, error) { + c.SetHeader("X-Test", "test") + return "test", nil + }) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "/test", nil) + + s.Mux.ServeHTTP(w, r) + + fmt.Println(w.Header().Get("X-Test")) + + // Output: + // test +}