From c962f05c81773d47fab6b039379a61a96fb8aea3 Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Wed, 17 Sep 2025 10:03:28 -0500 Subject: [PATCH] update readme for new data access patterns --- README.md | 134 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 99 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 84a866d..feaee5a 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ func main() { // Initialize sessions sushi.InitSessions("sessions.json") - app.Get("/", func(ctx sushi.Ctx, params []any) { + app.Get("/", func(ctx sushi.Ctx) { ctx.SendHTML("

Hello Sushi!

") }) @@ -41,35 +41,86 @@ api.Get("/users", listUsersHandler) api.Post("/users", createUserHandler) ``` -## Parameters +## Parameters and Queries -URL parameters are automatically converted to the appropriate type: +Sushi provides a fluent API for accessing URL parameters, query strings, and form data with automatic type conversion: + +### URL Parameters (Named Routes) ```go -// Numeric parameters become integers -app.Get("/users/:id", func(ctx sushi.Ctx, params []any) { - userID := params[0].(int) // /users/123 -> 123 - // ... +app.Get("/users/:id", func(ctx sushi.Ctx) { + // Get URL parameter with fluent API + userID := ctx.Param("id").Int() // /users/123 -> 123 + userIDStr := ctx.Param("id").String() // /users/123 -> "123" + + // With defaults + limit := ctx.Param("limit").IntDefault(10) }) -// String parameters stay strings -app.Get("/users/:name", func(ctx sushi.Ctx, params []any) { - name := params[0].(string) // /users/john -> "john" - // ... -}) +app.Get("/users/:id/posts/:slug", func(ctx sushi.Ctx) { + userID := ctx.Param("id").Int() + slug := ctx.Param("slug").String() -// Mixed types -app.Get("/users/:id/posts/:slug", func(ctx sushi.Ctx, params []any) { - userID := params[0].(int) // 123 - slug := params[1].(string) // "my-post" - // ... + // Check if parameter exists + if ctx.Param("optional").Exists() { + // Handle optional parameter + } +}) +``` + +### Query Parameters + +```go +app.Get("/search", func(ctx sushi.Ctx) { + // Get query parameters with fluent API + query := ctx.Query("q").String() // ?q=hello -> "hello" + page := ctx.Query("page").IntDefault(1) // ?page=2 -> 2 + limit := ctx.Query("limit").IntDefault(20) // Default to 20 if not present + sortBy := ctx.Query("sort").StringDefault("name") // Default to "name" + + // Boolean query params + includeDeleted := ctx.Query("deleted").Bool() // ?deleted=true -> true + + // Float values + minPrice := ctx.Query("min_price").Float() // ?min_price=19.99 -> 19.99 + + // Check existence + if ctx.Query("filter").Exists() { + filter := ctx.Query("filter").String() + // Apply filter + } +}) +``` + +## Form Data + +Access form data with the fluent API: + +```go +app.Post("/users", func(ctx sushi.Ctx) { + // Get form fields with fluent API + name := ctx.Input("name").String() + email := ctx.Input("email").String() + age := ctx.Input("age").IntDefault(0) + + // Check if checkbox is checked + agreeToTerms := ctx.Input("terms").Bool() // Checks for "true", "on", "1", "yes" + + // Validate required fields + if ctx.Input("email").IsEmpty() { + ctx.SendError(400, "Email is required") + return + } + + // Get array of values (for multiple select, checkboxes) + tags := ctx.GetFormArray("tags[]") }) ``` ## Response Helpers ```go -func myHandler(ctx sushi.Ctx, params []any) { +func myHandler(ctx sushi.Ctx) { // JSON responses ctx.SendJSON(map[string]string{"message": "success"}) @@ -87,15 +138,20 @@ func myHandler(ctx sushi.Ctx, params []any) { // Status only ctx.SendStatus(204) + + // File responses + ctx.SendFile("/path/to/file.pdf") + + // Raw bytes + ctx.SendBytes(imageData, "image/png") } -``` ## Middleware ```go // Custom middleware func loggingMiddleware() sushi.Middleware { - return func(ctx sushi.Ctx, params []any, next func()) { + return func(ctx sushi.Ctx, next func()) { println("Request:", string(ctx.Method()), string(ctx.Path())) next() println("Status:", ctx.Response.StatusCode()) @@ -111,7 +167,7 @@ admin.Use(auth.RequireAuth("/login")) ## Authentication Workflow -### 1. Setup Password Hashing +### 1. Password Hashing ```go import "git.sharkk.net/Sharkk/Sushi/password" @@ -176,9 +232,16 @@ func main() { ### 4. Login Handler ```go -func loginHandler(ctx sushi.Ctx, params []any) { - email := string(ctx.PostArgs().Peek("email")) - password := string(ctx.PostArgs().Peek("password")) +func loginHandler(ctx sushi.Ctx) { + // Use fluent API for form data + email := ctx.Input("email").String() + password := ctx.Input("password").String() + + // Validate inputs + if ctx.Input("email").IsEmpty() || ctx.Input("password").IsEmpty() { + ctx.SendError(400, "Email and password are required") + return + } // Find user by email/username user := findUserByEmail(email) @@ -204,7 +267,7 @@ func loginHandler(ctx sushi.Ctx, params []any) { ### 5. Logout Handler ```go -func logoutHandler(ctx sushi.Ctx, params []any) { +func logoutHandler(ctx sushi.Ctx) { ctx.Logout() ctx.Redirect("/") } @@ -213,7 +276,7 @@ func logoutHandler(ctx sushi.Ctx, params []any) { ### 6. Getting Current User ```go -func dashboardHandler(ctx sushi.Ctx, params []any) { +func dashboardHandler(ctx sushi.Ctx) { user := ctx.GetCurrentUser().(*User) html := fmt.Sprintf("

Welcome, %s!

", user.Username) @@ -230,7 +293,7 @@ import "git.sharkk.net/Sharkk/Sushi/csrf" app.Use(csrf.Middleware()) // In your form template -func loginPageHandler(ctx sushi.Ctx, params []any) { +func loginPageHandler(ctx sushi.Ctx) { csrfField := csrf.CSRFHiddenField(ctx) html := fmt.Sprintf(` @@ -266,7 +329,7 @@ app.Get("/assets/*path", sushi.StaticEmbed(files)) ## Sessions ```go -func someHandler(ctx sushi.Ctx, params []any) { +func someHandler(ctx sushi.Ctx) { sess := ctx.GetCurrentSession() // Set session data @@ -357,7 +420,7 @@ func main() { app.Listen(":8080") } -func homeHandler(ctx sushi.Ctx, params []any) { +func homeHandler(ctx sushi.Ctx) { if ctx.IsAuthenticated() { ctx.Redirect("/dashboard") return @@ -365,7 +428,7 @@ func homeHandler(ctx sushi.Ctx, params []any) { ctx.SendHTML(`Login`) } -func loginPageHandler(ctx sushi.Ctx, params []any) { +func loginPageHandler(ctx sushi.Ctx) { html := fmt.Sprintf(`
%s @@ -378,9 +441,10 @@ func loginPageHandler(ctx sushi.Ctx, params []any) { ctx.SendHTML(html) } -func loginHandler(ctx sushi.Ctx, params []any) { - email := string(ctx.PostArgs().Peek("email")) - pass := string(ctx.PostArgs().Peek("password")) +func loginHandler(ctx sushi.Ctx) { + // Use fluent API for form data + email := ctx.Input("email").String() + pass := ctx.Input("password").String() user := findUserByEmail(email) if user == nil { @@ -397,7 +461,7 @@ func loginHandler(ctx sushi.Ctx, params []any) { ctx.Redirect("/dashboard") } -func dashboardHandler(ctx sushi.Ctx, params []any) { +func dashboardHandler(ctx sushi.Ctx) { user := ctx.GetCurrentUser().(*User) html := fmt.Sprintf(` @@ -411,7 +475,7 @@ func dashboardHandler(ctx sushi.Ctx, params []any) { ctx.SendHTML(html) } -func logoutHandler(ctx sushi.Ctx, params []any) { +func logoutHandler(ctx sushi.Ctx) { ctx.Logout() ctx.Redirect("/") }