Skip to content

Commit ef8dee8

Browse files
committed
feat: #7 implemented a simple Barf interface for interacting with form data
1 parent 5bf0775 commit ef8dee8

11 files changed

Lines changed: 226 additions & 18 deletions

File tree

example/main.go

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,83 @@
11
package main
22

33
import (
4+
"io"
45
"net/http"
56
"os"
67

78
"github.com/opensaucerer/barf"
89
)
910

1011
func main() {
12+
13+
type Env struct {
14+
// Port for the server to listen on
15+
Port string `barfenv:"key=PORT;required=true"`
16+
}
17+
18+
env := new(Env) // global environment variable
19+
20+
// load environment variables
21+
if err := barf.Env(env, "example/.env"); err != nil {
22+
barf.Logger().Error(err.Error())
23+
os.Exit(1)
24+
}
25+
1126
// create server
1227
allow := true
28+
disallow := false
1329
if err := barf.Stark(barf.Augment{
14-
Port: "5000",
30+
Port: env.Port,
1531
Logging: &allow, // enable request logging
16-
Recovery: &allow, // enable panic recovery so barf returns a 500 error instead of crashing
32+
Recovery: &disallow,
33+
CORS: &barf.CORS{
34+
AllowedOrigins: []string{"https://*.google.com"},
35+
MaxAge: 3600,
36+
AllowedMethods: []string{
37+
http.MethodGet,
38+
},
39+
AllowedOriginFunc: func(origin string) bool {
40+
return origin == "https://www.google.com"
41+
},
42+
},
1743
}); err != nil {
1844
barf.Logger().Error(err.Error())
1945
os.Exit(1)
2046
}
2147

22-
barf.Get("/", func(w http.ResponseWriter, r *http.Request) {
23-
barf.Response(w).Status(http.StatusOK).JSON(barf.Res{
24-
Status: true,
25-
Data: nil,
26-
Message: "Hello World",
27-
})
28-
})
29-
3048
// create a subrouter (retroframe)
31-
s := barf.RetroFrame("/api/v1")
49+
s := barf.RetroFrame("/api").RetroFrame("/v1")
3250
s.Get("/about", func(w http.ResponseWriter, r *http.Request) {
51+
52+
message := "About"
53+
54+
// parsing form-data
55+
body, err := barf.Request(r).Form().Body().JSON()
56+
if err != nil {
57+
message = err.Error()
58+
}
59+
60+
head := barf.Request(r).Form().File().Get("file")
61+
file, _ := head.Open()
62+
defer file.Close()
63+
64+
// save file
65+
f, err := os.OpenFile(head.Filename, os.O_WRONLY|os.O_CREATE, 0666)
66+
if err != nil {
67+
message = err.Error()
68+
}
69+
defer f.Close()
70+
io.Copy(f, file)
71+
3372
barf.Response(w).Status(http.StatusOK).JSON(barf.Res{
3473
Status: true,
35-
Data: nil,
36-
Message: "About",
74+
Data: body,
75+
Message: message,
3776
})
3877
})
3978

40-
// start barf server
79+
// start server - create & start server
4180
if err := barf.Beck(); err != nil {
42-
// barf exposes a logger instance
4381
barf.Logger().Error(err.Error())
4482
os.Exit(1)
4583
}

example/multipart_formdata/main.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package main
2+
3+
import (
4+
"io"
5+
"net/http"
6+
"os"
7+
8+
"github.com/opensaucerer/barf"
9+
)
10+
11+
func main() {
12+
13+
type Env struct {
14+
// Port for the server to listen on
15+
Port string `barfenv:"key=PORT;required=true"`
16+
}
17+
18+
env := new(Env) // global environment variable
19+
20+
// load environment variables
21+
if err := barf.Env(env, "example/.env"); err != nil {
22+
barf.Logger().Error(err.Error())
23+
os.Exit(1)
24+
}
25+
26+
// create server
27+
allow := true
28+
disallow := false
29+
if err := barf.Stark(barf.Augment{
30+
Port: env.Port,
31+
Logging: &allow, // enable request logging
32+
Recovery: &disallow,
33+
CORS: &barf.CORS{
34+
AllowedOrigins: []string{"https://*.google.com"},
35+
MaxAge: 3600,
36+
AllowedMethods: []string{
37+
http.MethodGet,
38+
},
39+
AllowedOriginFunc: func(origin string) bool {
40+
return origin == "https://www.google.com"
41+
},
42+
},
43+
}); err != nil {
44+
barf.Logger().Error(err.Error())
45+
os.Exit(1)
46+
}
47+
48+
// create a subrouter (retroframe)
49+
s := barf.RetroFrame("/api").RetroFrame("/v1")
50+
s.Get("/about", func(w http.ResponseWriter, r *http.Request) {
51+
52+
message := "About"
53+
54+
// parsing form-data
55+
body, err := barf.Request(r).Form().Body().JSON()
56+
if err != nil {
57+
message = err.Error()
58+
}
59+
60+
head := barf.Request(r).Form().File().Get("file")
61+
file, _ := head.Open()
62+
defer file.Close()
63+
64+
// save file
65+
f, err := os.OpenFile(head.Filename, os.O_WRONLY|os.O_CREATE, 0666)
66+
if err != nil {
67+
message = err.Error()
68+
}
69+
defer f.Close()
70+
io.Copy(f, file)
71+
72+
barf.Response(w).Status(http.StatusOK).JSON(barf.Res{
73+
Status: true,
74+
Data: body,
75+
Message: message,
76+
})
77+
})
78+
79+
// start server - create & start server
80+
if err := barf.Beck(); err != nil {
81+
barf.Logger().Error(err.Error())
82+
os.Exit(1)
83+
}
84+
}

example/subroute/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func main() {
5252
})
5353

5454
// create a subrouter (retroframe)
55-
var r *barf.SubRoute = barf.RetroFrame("/api/v1")
55+
var r *barf.SubRoute = barf.RetroFrame("/api")
5656

5757
// apply middleware to subrouter. note that the only difference between this and global middlewares is that you need to pass the
5858
barf.Hippocampus(r).Hijack(func(h http.Handler) http.Handler {
@@ -73,7 +73,7 @@ func main() {
7373

7474
// create another subrouter (retroframe)
7575
// note that although the path is the same, the subroute is different and won't inherit the middleware from the previous subroute
76-
s := barf.RetroFrame("/api/v1")
76+
s := barf.RetroFrame("/api").RetroFrame("/v1")
7777
s.Get("/about", func(w http.ResponseWriter, r *http.Request) {
7878
barf.Response(w).Status(http.StatusOK).JSON(barf.Res{
7979
Status: true,

router/body/body.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ import (
77

88
type B []byte
99

10+
// Body prepares the barf request with the request body for further formatting
1011
func Body(r *http.Request) []byte {
1112
body := make([]byte, r.ContentLength)
1213
r.Body.Read(body)
1314
return body
1415
}
1516

16-
// JSON formats the request body as map[string]interface{}.
17+
// JSON formats the body as map[string]interface{}.
1718
// It returns an error if the body is not a valid JSON.
1819
func (b B) JSON() (map[string]interface{}, error) {
1920
var data map[string]interface{}

router/form/body.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package form
2+
3+
import (
4+
"encoding/json"
5+
6+
"github.com/opensaucerer/barf/router/body"
7+
)
8+
9+
// Body prepares the non-file part of the form-data for further formatting
10+
func (m M) Body() body.B {
11+
// write r.MultipartForm.Value to f.r.Body
12+
val := make(map[string]string)
13+
for k, v := range m.r.MultipartForm.Value {
14+
val[k] = v[0]
15+
}
16+
b, _ := json.Marshal(val)
17+
return b
18+
}

router/form/file.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package form
2+
3+
import (
4+
"mime/multipart"
5+
)
6+
7+
type F map[string][]*multipart.FileHeader
8+
9+
// File prepares the file part of the form-data for further formatting
10+
func (m M) File() F {
11+
return F(m.r.MultipartForm.File)
12+
}
13+
14+
// Get returns the first file for the given key.
15+
// It returns nil if the key does not exist.
16+
func (f F) Get(key string) *multipart.FileHeader {
17+
if f[key] == nil {
18+
return nil
19+
}
20+
return f[key][0]
21+
}
22+
23+
// All returns all files for the given key.
24+
// It returns nil if the key does not exist.
25+
func (f F) All(key string) []*multipart.FileHeader {
26+
return f[key]
27+
}

router/form/form.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package form
2+
3+
import (
4+
"net/http"
5+
)
6+
7+
type M struct {
8+
r *http.Request
9+
}
10+
11+
// Form prepares the barf request with the request form or multipart/form-data for further formatting
12+
func Form(r *http.Request, maxMemory int64) M {
13+
r.ParseMultipartForm(maxMemory)
14+
return M{r}
15+
}

router/param/param.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
type P []byte
1111

12+
// Params prepares the barf request with the request params for further formatting
1213
func Params(r *http.Request) []byte {
1314
p := r.Context().Value(typing.ParamsCtxKey{})
1415
if p == nil {

router/query/query.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
type Q []byte
99

10+
// Query prepares the barf request with the request query for further formatting
1011
func Query(r *http.Request) []byte {
1112
q := make(map[string]string)
1213
for k, v := range r.URL.Query() {

router/retroframe.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ func RetroFrame(path string) *SubRoute {
2020
return s
2121
}
2222

23+
// RetroFrame returns a new subrouter instance registered against the given entry path and the RetroFrame instance it is called on.
24+
func (r *SubRoute) RetroFrame(path string) *SubRoute {
25+
s := &SubRoute{
26+
entry: r.entry + "/" + regexp.MustCompile("^/+|/+$").ReplaceAllString(path, ""),
27+
routes: []*Route{},
28+
stack: []typing.Middleware{},
29+
}
30+
s.key = fmt.Sprintf("%p", s)
31+
stable[s.key] = s
32+
return s
33+
}
34+
2335
// Get registers a route with the GET HTTP method against the path of the RetroFrame router instance.
2436
func (r *SubRoute) Get(path string, handler func(http.ResponseWriter, *http.Request)) {
2537
fget(fmt.Sprintf("/%s/%s", r.entry, regexp.MustCompile("^/+|/+$").ReplaceAllString(path, "")), handler, r.key)

0 commit comments

Comments
 (0)