Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
◦
◦
◦
◦
◦
◦
func h(w http.ResponseWriter, r *http.Request) {
}
Boilerplate code (logging, auth, parameters)
Boilerplate code (response...
func h(w http.ResponseWriter, r *http.Request) {
f(w, r) { }
g(w, r) { }
}
Boilerplate code (logging, auth, parameters)
Bo...
func f(w http.ResponseWriter, r *http.Request) {
}
func h(w http.ResponseWriter, r *http.Request) {
}
func g(w http.Respon...
func(c siesta.Context, w http.ResponseWriter, r *http.Request) {
// set interface{}s
c.Set("foo", bar);
// get interface{}...
gorilla/context
◦
◦
◦
func MyHandler(w http.ResponseWriter, r *http.Request) {
// ...
requestID := context.Get(r, "request...
func (s *Service) ServeHTTPInContext(c Context, w http.ResponseWriter, r *http.Request) {
// ...
quit := false
for _, m :=...
flags
◦ GET /resources/:resourceID/things?all=true
◦ params.Int("resourceID", …)
◦ params.Bool("all", …)
var params siesta.Params
// GET /resources/:resourceID/...
resourceID := params.Int("resourceID", -1, "Resource identifier...
// responseGenerator converts response and/or error data passed through the
// context into a structured response.
func re...
func TestHandler(t *testing.T) {
c := siesta.NewSiestaContext()
// c.Set(...)
myHandler(c, mockResponseWriter, &http.Reque...
database/sql
◦
◦
◦
◦
◦
sql.DB sql.Tx
sql.
DB sql.Tx
◦ database/sql
sql.DB sql.Tx
sql.DB
type SQLDB interface {
Query(query string,
args ...interface{}) (SQLRows, error)
QueryRow(query string,
args ...int...
type DB struct {
sqlDB *sql.DB
tx *sql.Tx
context siesta.Context
}
func (db *DB) Query(query string,
args ...interface{}) ...
$ curl localhost:8080/resources/1 -u abcde:
{
"data": "foo"
}
$ curl localhost:8080/resources/1?usage -u abcde:
{
"data": ...
// Check parameters
var params siesta.Params
resourceID := params.Int("resourceID", -1, "Resource identifier")
params.Bool...
/resources/:resourceID?summary=true
{
"method": "GET",
"url": [
{
"name": "resources",
"type": "fixed"
},
{
"name": "resou...
Siesta: A Different Go Framework
Siesta: A Different Go Framework
Siesta: A Different Go Framework
Siesta: A Different Go Framework
Siesta: A Different Go Framework
Siesta: A Different Go Framework
Siesta: A Different Go Framework
Siesta: A Different Go Framework
Upcoming SlideShare
Loading in …5
×

Siesta: A Different Go Framework

402 views

Published on

Most web frameworks for Go are written in the same, idiomatic way, which embraces net/http and its Handler type. The first step that frameworks make is extending the standard HTTP package, but they draw the line for built-in features and functionality all over the map. From our experience looking for a better framework, we often found lightweight frameworks lacked a feature or two that we were particularly interested in, but incorporating those features would have been either difficult or inelegant.

When we developed Siesta, we made sure to keep it as simple as possible to avoid spaghetti code both in the package itself and user-facing code that uses it. As a result, Siesta enables a lot more functionality without imposing a strict view for writing HTTP services.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Siesta: A Different Go Framework

  1. 1. ◦ ◦ ◦ ◦ ◦ ◦
  2. 2. func h(w http.ResponseWriter, r *http.Request) { } Boilerplate code (logging, auth, parameters) Boilerplate code (response writing, etc) Main handler code
  3. 3. func h(w http.ResponseWriter, r *http.Request) { f(w, r) { } g(w, r) { } } Boilerplate code (logging, auth, parameters) Boilerplate code (response writing, etc) Main handler code
  4. 4. func f(w http.ResponseWriter, r *http.Request) { } func h(w http.ResponseWriter, r *http.Request) { } func g(w http.ResponseWriter, r *http.Request) { } Boilerplate code (logging, auth, parameters) Boilerplate code (response writing, etc) Main handler code
  5. 5. func(c siesta.Context, w http.ResponseWriter, r *http.Request) { // set interface{}s c.Set("foo", bar); // get interface{}s c.Get("foo") // => interface{}(bar) } ◦ ◦
  6. 6. gorilla/context ◦ ◦ ◦ func MyHandler(w http.ResponseWriter, r *http.Request) { // ... requestID := context.Get(r, "request-id") // ... }
  7. 7. func (s *Service) ServeHTTPInContext(c Context, w http.ResponseWriter, r *http.Request) { // ... quit := false for _, m := range s.pre { // s.pre is a slice of handlers m(c, w, r, func() { quit = true }) if quit { break } } // ... }
  8. 8. flags ◦ GET /resources/:resourceID/things?all=true ◦ params.Int("resourceID", …) ◦ params.Bool("all", …)
  9. 9. var params siesta.Params // GET /resources/:resourceID/... resourceID := params.Int("resourceID", -1, "Resource identifier") err := params.Parse(r.Form) if err != nil { // Do something with the err } // Make sure we have a valid resource ID. if *resourceID == -1 { // Handle the invalid resource }
  10. 10. // responseGenerator converts response and/or error data passed through the // context into a structured response. func responseGenerator(c siesta.Context, w http.ResponseWriter, r *http.Request) { response := apiResponse{} if data := c.Get("data"); data != nil { response.Data = data } if err := c.Get("error"); err != nil { response.Error = err.(string) } c.Set("response", response) }
  11. 11. func TestHandler(t *testing.T) { c := siesta.NewSiestaContext() // c.Set(...) myHandler(c, mockResponseWriter, &http.Request{ Form: url.Values(map[string][]string{ "id": []string{"1"}, // maybe a GET /resource/:id }), } // c.Get(...) }
  12. 12. database/sql ◦ ◦ ◦ ◦
  13. 13.
  14. 14. sql.DB sql.Tx sql. DB sql.Tx ◦ database/sql sql.DB sql.Tx
  15. 15. sql.DB type SQLDB interface { Query(query string, args ...interface{}) (SQLRows, error) QueryRow(query string, args ...interface{}) SQLRow Exec(query string, args ...interface{}) (sql.Result, error) Begin() error Rollback() error Commit() error } type SQLRows interface { SQLRow Close() error Err() error Next() bool } type SQLRow interface { Scan(dest ...interface{}) error }
  16. 16. type DB struct { sqlDB *sql.DB tx *sql.Tx context siesta.Context } func (db *DB) Query(query string, args ...interface{}) (Rows, error) { // db.context is accessible! if db.tx != nil { return db.tx.Query(query, args...) } return db.sqlDB.Query(query, args...) } // ...
  17. 17. $ curl localhost:8080/resources/1 -u abcde: { "data": "foo" } $ curl localhost:8080/resources/1?usage -u abcde: { "data": { "resourceID": [ "resourceID", "int", "Resource identifier" ], "usage": [ "usage", "bool", "Show usage" ] } } Name Type Description
  18. 18. // Check parameters var params siesta.Params resourceID := params.Int("resourceID", -1, "Resource identifier") params.Bool("usage", false, "Show usage") if r.URL.Query()["usage"] != nil { c.Set("data", params.Usage()) return } err := params.Parse(r.Form) // ...
  19. 19. /resources/:resourceID?summary=true { "method": "GET", "url": [ { "name": "resources", "type": "fixed" }, { "name": "resourceID", "type": "parameter" "parameterType": "int", "description": "Resource ID" } ], "queryString": [ { "name": "summary", "parameterType": "bool", "description": "Show only feature summary" } ], "endpoint": "/resources/:resourceID" }

×