mirror of
https://github.com/gogs/gogs.git
synced 2026-02-28 09:10:57 +01:00
Add Flamego quick reference guide for easy lookup
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
This commit is contained in:
595
docs/dev/flamego_quick_reference.md
Normal file
595
docs/dev/flamego_quick_reference.md
Normal file
@@ -0,0 +1,595 @@
|
||||
# Macaron vs Flamego: Quick Reference
|
||||
|
||||
This document provides quick lookup tables for common migration patterns.
|
||||
|
||||
## At a Glance
|
||||
|
||||
| Aspect | Macaron | Flamego | Status |
|
||||
|--------|---------|---------|--------|
|
||||
| **Creator** | Unknwon | Unknwon | ✅ Same author |
|
||||
| **Status** | Maintenance only | Active development | ⚠️ Macaron deprecated |
|
||||
| **Go Version** | 1.11+ | 1.19+ | 📈 Modern |
|
||||
| **Philosophy** | Dependency injection | Dependency injection | ✅ Same |
|
||||
| **Performance** | Good | Better | 📈 Improved |
|
||||
| **Routing** | Basic | Advanced | 📈 Enhanced |
|
||||
|
||||
## Import Mapping
|
||||
|
||||
| Macaron Package | Flamego Package | Notes |
|
||||
|----------------|-----------------|-------|
|
||||
| `gopkg.in/macaron.v1` | `github.com/flamego/flamego` | Core framework |
|
||||
| `github.com/go-macaron/binding` | `github.com/flamego/binding` | Form binding |
|
||||
| `github.com/go-macaron/cache` | `github.com/flamego/cache` | Caching |
|
||||
| `github.com/go-macaron/captcha` | `github.com/flamego/captcha` | Captcha |
|
||||
| `github.com/go-macaron/csrf` | `github.com/flamego/csrf` | CSRF protection |
|
||||
| `github.com/go-macaron/gzip` | `github.com/flamego/gzip` | Gzip compression |
|
||||
| `github.com/go-macaron/i18n` | `github.com/flamego/i18n` | Internationalization |
|
||||
| `github.com/go-macaron/session` | `github.com/flamego/session` | Session management |
|
||||
| Built-in | `github.com/flamego/template` | Template rendering |
|
||||
| `github.com/go-macaron/toolbox` | ❌ Custom implementation | Health checks |
|
||||
|
||||
## Type Mapping
|
||||
|
||||
| Macaron Type | Flamego Type | Change |
|
||||
|--------------|--------------|--------|
|
||||
| `*macaron.Macaron` | `*flamego.Flame` | Main app type |
|
||||
| `macaron.Context` | `flamego.Context` | Interface vs pointer |
|
||||
| `macaron.Handler` | `flamego.Handler` | Same concept |
|
||||
| `session.Store` | `session.Session` | Interface name |
|
||||
| `csrf.CSRF` | `csrf.CSRF` | Same |
|
||||
| `cache.Cache` | `cache.Cache` | Same |
|
||||
|
||||
## Method Mapping
|
||||
|
||||
### Core Methods
|
||||
|
||||
| Operation | Macaron | Flamego |
|
||||
|-----------|---------|---------|
|
||||
| Create app | `macaron.New()` | `flamego.New()` |
|
||||
| Classic setup | `macaron.Classic()` | `flamego.Classic()` |
|
||||
| Add middleware | `m.Use(handler)` | `f.Use(handler)` |
|
||||
| GET route | `m.Get(path, h)` | `f.Get(path, h)` |
|
||||
| POST route | `m.Post(path, h)` | `f.Post(path, h)` |
|
||||
| Route group | `m.Group(path, fn)` | `f.Group(path, fn)` |
|
||||
| Combo route | `m.Combo(path)` | `f.Combo(path)` |
|
||||
| Start server | `http.ListenAndServe(addr, m)` | `f.Run(addr)` |
|
||||
|
||||
### Context Methods
|
||||
|
||||
| Operation | Macaron | Flamego |
|
||||
|-----------|---------|---------|
|
||||
| Get param | `c.Params(":name")` | `c.Param("name")` |
|
||||
| Get query | `c.Query("key")` | `c.Query("key")` |
|
||||
| Get request | `c.Req` | `c.Request()` |
|
||||
| Get response | `c.Resp` | `c.ResponseWriter()` |
|
||||
| Redirect | `c.Redirect(url)` | `c.Redirect(url)` |
|
||||
| Set cookie | `c.SetCookie(...)` | `c.SetCookie(...)` |
|
||||
| Get cookie | `c.GetCookie(name)` | `c.Cookie(name)` |
|
||||
|
||||
### Session Methods
|
||||
|
||||
| Operation | Macaron | Flamego |
|
||||
|-----------|---------|---------|
|
||||
| Set value | `sess.Set(k, v)` | `sess.Set(k, v)` |
|
||||
| Get value | `sess.Get(k)` | `sess.Get(k)` |
|
||||
| Delete | `sess.Delete(k)` | `sess.Delete(k)` |
|
||||
| ID | `sess.ID()` | `sess.ID()` |
|
||||
| Flush | `sess.Flush()` | `sess.Flush()` |
|
||||
|
||||
### CSRF Methods
|
||||
|
||||
| Operation | Macaron | Flamego |
|
||||
|-----------|---------|---------|
|
||||
| Get token | `x.GetToken()` | `x.Token()` |
|
||||
| Validate | Automatic | Automatic |
|
||||
|
||||
### Cache Methods
|
||||
|
||||
| Operation | Macaron | Flamego |
|
||||
|-----------|---------|---------|
|
||||
| Set value | `c.Put(k, v, timeout)` | `c.Set(k, v, timeout)` |
|
||||
| Get value | `c.Get(k)` | `c.Get(k)` |
|
||||
| Delete | `c.Delete(k)` | `c.Delete(k)` |
|
||||
| Flush | `c.Flush()` | `c.Flush()` |
|
||||
|
||||
## Route Syntax
|
||||
|
||||
| Feature | Macaron | Flamego | Example |
|
||||
|---------|---------|---------|---------|
|
||||
| Basic param | `:param` | `<param>` | `/:id` → `/<id>` |
|
||||
| Regex param | `^:name(a\|b)$` | `<name:a\|b>` | Pattern matching |
|
||||
| Optional param | Multiple routes | `?<param>` | `/wiki/?<page>` |
|
||||
| Wildcard | `:path(*)` | `<**path>` | Glob pattern |
|
||||
|
||||
### Before (Macaron)
|
||||
```go
|
||||
m.Get("/", handler) // Root
|
||||
m.Get("/:username", handler) // Basic param
|
||||
m.Get("/:username/:repo", handler) // Multiple params
|
||||
m.Get("/^:type(issues|pulls)$", handler) // Regex
|
||||
```
|
||||
|
||||
### After (Flamego)
|
||||
```go
|
||||
f.Get("/", handler) // Root
|
||||
f.Get("/<username>", handler) // Basic param
|
||||
f.Get("/<username>/<repo>", handler) // Multiple params
|
||||
f.Get("/<type:issues|pulls>", handler) // Regex
|
||||
```
|
||||
|
||||
## Handler Signatures
|
||||
|
||||
### Basic Handler
|
||||
|
||||
**Macaron:**
|
||||
```go
|
||||
func Handler(c *macaron.Context) {
|
||||
c.JSON(200, map[string]string{"msg": "hello"})
|
||||
}
|
||||
```
|
||||
|
||||
**Flamego:**
|
||||
```go
|
||||
func Handler(c flamego.Context) {
|
||||
c.ResponseWriter().Header().Set("Content-Type", "application/json")
|
||||
c.ResponseWriter().WriteHeader(200)
|
||||
json.NewEncoder(c.ResponseWriter()).Encode(map[string]string{"msg": "hello"})
|
||||
}
|
||||
```
|
||||
|
||||
### With Custom Context
|
||||
|
||||
**Macaron:**
|
||||
```go
|
||||
func Handler(c *context.Context) {
|
||||
c.Data["Title"] = "Page"
|
||||
c.HTML(200, "template")
|
||||
}
|
||||
```
|
||||
|
||||
**Flamego:**
|
||||
```go
|
||||
func Handler(c *context.Context, t template.Template, data template.Data) {
|
||||
data["Title"] = "Page"
|
||||
t.HTML(200, "template")
|
||||
}
|
||||
```
|
||||
|
||||
### With Session
|
||||
|
||||
**Macaron:**
|
||||
```go
|
||||
func Handler(c *context.Context, sess session.Store) {
|
||||
sess.Set("key", "value")
|
||||
}
|
||||
```
|
||||
|
||||
**Flamego:**
|
||||
```go
|
||||
func Handler(c *context.Context, sess session.Session) {
|
||||
sess.Set("key", "value")
|
||||
}
|
||||
```
|
||||
|
||||
### With Form Binding
|
||||
|
||||
**Macaron:**
|
||||
```go
|
||||
func Handler(c *context.Context, form Form) {
|
||||
// Use form
|
||||
}
|
||||
|
||||
// Route
|
||||
m.Post("/", binding.Bind(Form{}), Handler)
|
||||
```
|
||||
|
||||
**Flamego:**
|
||||
```go
|
||||
func Handler(c *context.Context, form Form) {
|
||||
// Use form
|
||||
}
|
||||
|
||||
// Route
|
||||
f.Post("/", binding.Form(Form{}), Handler)
|
||||
```
|
||||
|
||||
## Form Tags
|
||||
|
||||
| Validation | Macaron | Flamego |
|
||||
|------------|---------|---------|
|
||||
| Required | `binding:"Required"` | `validate:"required"` |
|
||||
| Email | `binding:"Email"` | `validate:"email"` |
|
||||
| URL | `binding:"Url"` | `validate:"url"` |
|
||||
| Min length | `binding:"MinSize(5)"` | `validate:"min=5"` |
|
||||
| Max length | `binding:"MaxSize(100)"` | `validate:"max=100"` |
|
||||
| Range | `binding:"Range(1,10)"` | `validate:"min=1,max=10"` |
|
||||
| Alpha | `binding:"Alpha"` | `validate:"alpha"` |
|
||||
| AlphaDash | `binding:"AlphaDash"` | `validate:"alphanum"` |
|
||||
|
||||
### Before (Macaron)
|
||||
```go
|
||||
type LoginForm struct {
|
||||
Username string `form:"username" binding:"Required;AlphaDash"`
|
||||
Password string `form:"password" binding:"Required;MinSize(6)"`
|
||||
Email string `form:"email" binding:"Email"`
|
||||
}
|
||||
```
|
||||
|
||||
### After (Flamego)
|
||||
```go
|
||||
type LoginForm struct {
|
||||
Username string `form:"username" validate:"required,alphanum"`
|
||||
Password string `form:"password" validate:"required,min=6"`
|
||||
Email string `form:"email" validate:"email"`
|
||||
}
|
||||
```
|
||||
|
||||
## Middleware Configuration
|
||||
|
||||
### Session
|
||||
|
||||
**Macaron:**
|
||||
```go
|
||||
m.Use(session.Sessioner(session.Options{
|
||||
Provider: "memory",
|
||||
ProviderConfig: "",
|
||||
CookieName: "session_id",
|
||||
CookiePath: "/",
|
||||
Gclifetime: 3600,
|
||||
Maxlifetime: 3600,
|
||||
}))
|
||||
```
|
||||
|
||||
**Flamego:**
|
||||
```go
|
||||
f.Use(session.Sessioner(session.Options{
|
||||
Config: session.MemoryConfig{
|
||||
GCInterval: 3600,
|
||||
},
|
||||
Cookie: session.CookieOptions{
|
||||
Name: "session_id",
|
||||
Path: "/",
|
||||
MaxAge: 3600,
|
||||
},
|
||||
}))
|
||||
```
|
||||
|
||||
### CSRF
|
||||
|
||||
**Macaron:**
|
||||
```go
|
||||
m.Use(csrf.Csrfer(csrf.Options{
|
||||
Secret: "secret-key",
|
||||
Cookie: "_csrf",
|
||||
CookiePath: "/",
|
||||
SetCookie: true,
|
||||
Secure: false,
|
||||
}))
|
||||
```
|
||||
|
||||
**Flamego:**
|
||||
```go
|
||||
f.Use(csrf.Csrfer(csrf.Options{
|
||||
Secret: "secret-key",
|
||||
Cookie: "_csrf",
|
||||
CookiePath: "/",
|
||||
Secure: false,
|
||||
}))
|
||||
```
|
||||
|
||||
### Cache
|
||||
|
||||
**Macaron:**
|
||||
```go
|
||||
m.Use(cache.Cacher(cache.Options{
|
||||
Adapter: "memory",
|
||||
AdapterConfig: "",
|
||||
Interval: 60,
|
||||
}))
|
||||
```
|
||||
|
||||
**Flamego:**
|
||||
```go
|
||||
f.Use(cache.Cacher(cache.Options{
|
||||
Config: cache.MemoryConfig{
|
||||
GCInterval: 60,
|
||||
},
|
||||
}))
|
||||
```
|
||||
|
||||
### Template
|
||||
|
||||
**Macaron:**
|
||||
```go
|
||||
m.Use(macaron.Renderer(macaron.RenderOptions{
|
||||
Directory: "templates",
|
||||
Funcs: template.FuncMap(),
|
||||
}))
|
||||
```
|
||||
|
||||
**Flamego:**
|
||||
```go
|
||||
f.Use(template.Templater(template.Options{
|
||||
Directory: "templates",
|
||||
FuncMaps: []template.FuncMap{template.FuncMap()},
|
||||
}))
|
||||
```
|
||||
|
||||
### i18n
|
||||
|
||||
**Macaron:**
|
||||
```go
|
||||
m.Use(i18n.I18n(i18n.Options{
|
||||
SubURL: "/",
|
||||
Langs: []string{"en-US", "zh-CN"},
|
||||
Names: []string{"English", "简体中文"},
|
||||
DefaultLang: "en-US",
|
||||
}))
|
||||
```
|
||||
|
||||
**Flamego:**
|
||||
```go
|
||||
f.Use(i18n.I18n(i18n.Options{
|
||||
URLPrefix: "/",
|
||||
Languages: []string{"en-US", "zh-CN"},
|
||||
Names: []string{"English", "简体中文"},
|
||||
DefaultLanguage: "en-US",
|
||||
}))
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Pattern: Get User by Username
|
||||
|
||||
**Macaron:**
|
||||
```go
|
||||
func UserProfile(c *context.Context) {
|
||||
username := c.Params(":username")
|
||||
user, err := database.GetUserByName(username)
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get user")
|
||||
return
|
||||
}
|
||||
c.Data["User"] = user
|
||||
c.HTML(200, "user/profile")
|
||||
}
|
||||
```
|
||||
|
||||
**Flamego:**
|
||||
```go
|
||||
func UserProfile(c *context.Context, t template.Template, data template.Data) {
|
||||
username := c.Param("username")
|
||||
user, err := database.GetUserByName(username)
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get user")
|
||||
return
|
||||
}
|
||||
data["User"] = user
|
||||
t.HTML(200, "user/profile")
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern: Form Submission
|
||||
|
||||
**Macaron:**
|
||||
```go
|
||||
// Form struct
|
||||
type CreateRepoForm struct {
|
||||
Name string `form:"name" binding:"Required;AlphaDashDot"`
|
||||
}
|
||||
|
||||
// Route
|
||||
m.Post("/repo/create", binding.Bind(CreateRepoForm{}), CreateRepoPost)
|
||||
|
||||
// Handler
|
||||
func CreateRepoPost(c *context.Context, form CreateRepoForm) {
|
||||
if c.HasError() {
|
||||
c.RenderWithErr(c.GetErrMsg(), "repo/create", &form)
|
||||
return
|
||||
}
|
||||
// Create repo...
|
||||
c.Redirect("/")
|
||||
}
|
||||
```
|
||||
|
||||
**Flamego:**
|
||||
```go
|
||||
// Form struct
|
||||
type CreateRepoForm struct {
|
||||
Name string `form:"name" validate:"required,alphaDashDot"`
|
||||
}
|
||||
|
||||
// Route
|
||||
f.Post("/repo/create", binding.Form(CreateRepoForm{}), CreateRepoPost)
|
||||
|
||||
// Handler
|
||||
func CreateRepoPost(c *context.Context, form CreateRepoForm, t template.Template, data template.Data) {
|
||||
if c.HasError() {
|
||||
c.RenderWithErr(c.GetErrMsg(), "repo/create", &form, t, data)
|
||||
return
|
||||
}
|
||||
// Create repo...
|
||||
c.Redirect("/")
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern: JSON API
|
||||
|
||||
**Macaron:**
|
||||
```go
|
||||
func APIHandler(c *context.APIContext) {
|
||||
data := map[string]any{
|
||||
"id": 123,
|
||||
"name": "example",
|
||||
}
|
||||
c.JSON(200, data)
|
||||
}
|
||||
```
|
||||
|
||||
**Flamego:**
|
||||
```go
|
||||
func APIHandler(c *context.APIContext) {
|
||||
data := map[string]any{
|
||||
"id": 123,
|
||||
"name": "example",
|
||||
}
|
||||
|
||||
c.ResponseWriter().Header().Set("Content-Type", "application/json")
|
||||
c.ResponseWriter().WriteHeader(200)
|
||||
json.NewEncoder(c.ResponseWriter()).Encode(data)
|
||||
}
|
||||
|
||||
// Or create helper method on APIContext
|
||||
func (c *APIContext) JSON(status int, v any) error {
|
||||
c.ResponseWriter().Header().Set("Content-Type", "application/json")
|
||||
c.ResponseWriter().WriteHeader(status)
|
||||
return json.NewEncoder(c.ResponseWriter()).Encode(v)
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern: Middleware Chain
|
||||
|
||||
**Macaron:**
|
||||
```go
|
||||
m.Group("/repo", func() {
|
||||
m.Get("/create", repo.Create)
|
||||
m.Post("/create", binding.Bind(form.CreateRepo{}), repo.CreatePost)
|
||||
}, reqSignIn, context.RepoAssignment())
|
||||
```
|
||||
|
||||
**Flamego:**
|
||||
```go
|
||||
f.Group("/repo", func() {
|
||||
f.Get("/create", repo.Create)
|
||||
f.Post("/create", binding.Form(form.CreateRepo{}), repo.CreatePost)
|
||||
}, reqSignIn, context.RepoAssignment())
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Not Found
|
||||
|
||||
**Macaron:**
|
||||
```go
|
||||
func Handler(c *context.Context) {
|
||||
user, err := getUser()
|
||||
if err != nil {
|
||||
if isNotFound(err) {
|
||||
c.NotFound()
|
||||
return
|
||||
}
|
||||
c.Error(err, "get user")
|
||||
return
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Flamego:**
|
||||
```go
|
||||
func Handler(c *context.Context) {
|
||||
user, err := getUser()
|
||||
if err != nil {
|
||||
if isNotFound(err) {
|
||||
c.NotFound()
|
||||
return
|
||||
}
|
||||
c.Error(err, "get user")
|
||||
return
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Mock Context
|
||||
|
||||
**Macaron:**
|
||||
```go
|
||||
import "gopkg.in/macaron.v1"
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
m := macaron.New()
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
m.ServeHTTP(resp, req)
|
||||
}
|
||||
```
|
||||
|
||||
**Flamego:**
|
||||
```go
|
||||
import "github.com/flamego/flamego"
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
f := flamego.New()
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
f.ServeHTTP(resp, req)
|
||||
}
|
||||
```
|
||||
|
||||
## Migration Checklist (Quick)
|
||||
|
||||
- [ ] Update imports
|
||||
- [ ] Change `:param` → `<param>` in routes
|
||||
- [ ] Change `macaron.Handler` → `flamego.Handler`
|
||||
- [ ] Change `*macaron.Context` → `flamego.Context`
|
||||
- [ ] Change `c.Params(":name")` → `c.Param("name")`
|
||||
- [ ] Change `c.Resp` → `c.ResponseWriter()`
|
||||
- [ ] Change `c.Req` → `c.Request()`
|
||||
- [ ] Change `session.Store` → `session.Session`
|
||||
- [ ] Change `x.GetToken()` → `x.Token()`
|
||||
- [ ] Change `cache.Put()` → `cache.Set()`
|
||||
- [ ] Add template parameters to handlers
|
||||
- [ ] Update form validation tags
|
||||
- [ ] Test everything!
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Forgot to remove `:` from param name | Use `c.Param("name")` not `c.Param(":name")` |
|
||||
| Template not rendering | Add `template.Template` and `template.Data` to handler |
|
||||
| Session not working | Changed interface from `Store` to `Session` |
|
||||
| CSRF validation fails | Use `Token()` not `GetToken()` |
|
||||
| Cache not working | Use `Set()` not `Put()` |
|
||||
| Form validation errors | Update tags: `binding` → `validate` |
|
||||
| Context methods fail | Use methods not fields: `c.ResponseWriter()` not `c.Resp` |
|
||||
|
||||
## Performance Notes
|
||||
|
||||
### Flamego Advantages
|
||||
|
||||
1. **O(1) static route lookup** - Faster than Macaron's tree
|
||||
2. **Better regex handling** - Compiled patterns cached
|
||||
3. **Reduced allocations** - More efficient memory usage
|
||||
4. **Faster middleware chain** - Optimized injection
|
||||
|
||||
### Expected Improvements
|
||||
|
||||
- 10-30% faster route matching for static routes
|
||||
- 5-15% faster overall request handling
|
||||
- Slightly lower memory usage
|
||||
- Better scalability under load
|
||||
|
||||
## Support and Resources
|
||||
|
||||
| Need Help? | Resource |
|
||||
|------------|----------|
|
||||
| Official Docs | https://flamego.dev/ |
|
||||
| API Reference | https://pkg.go.dev/github.com/flamego/flamego |
|
||||
| GitHub | https://github.com/flamego/flamego |
|
||||
| Middleware | https://github.com/flamego (multiple repos) |
|
||||
| FAQ | https://flamego.dev/faqs.html |
|
||||
| Examples | https://github.com/flamego/flamego/tree/main/_examples |
|
||||
|
||||
## Version Information
|
||||
|
||||
| Framework | Current Version | Release Date | Status |
|
||||
|-----------|----------------|--------------|--------|
|
||||
| Macaron | v1.5.0 | 2021 | Maintenance |
|
||||
| Flamego | v1.9.0+ | 2024 | Active |
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2026-01-25
|
||||
**Applies to:** Gogs migration from Macaron to Flamego
|
||||
Reference in New Issue
Block a user