func ExampleAddChain() { c := xhandler.Chain{} close := xhandler.CloseHandler cors := cors.Default().Handler timeout := xhandler.TimeoutHandler(2 * time.Second) auth := func(next xhandler.HandlerC) xhandler.HandlerC { return xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { if v := ctx.Value("Authorization"); v == nil { http.Error(w, "Not authorized", http.StatusUnauthorized) return } next.ServeHTTPC(ctx, w, r) }) } c.Add(close, cors, timeout) mux := http.NewServeMux() // Use c.Handler to terminate the chain with your final handler mux.Handle("/", c.Handler(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "Welcome to the home page!") }))) // Create a new chain from an existing one, and add route-specific middleware to it protected := c.With(auth) mux.Handle("/admin", protected.Handler(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "protected endpoint!") }))) }
func main() { // Create a REST API resource index index := resource.NewIndex() // Add a resource on /users[/:user_id] users := index.Bind("users", resource.New(user, mem.NewHandler(), resource.Conf{ // We allow all REST methods // (rest.ReadWrite is a shortcut for []resource.Mode{resource.Create, resource.Read, resource.Update, resource.Delete, resource,List}) AllowedModes: resource.ReadWrite, })) // Bind a sub resource on /users/:user_id/posts[/:post_id] // and reference the user on each post using the "user" field of the posts resource. posts := users.Bind("posts", "user", resource.New(post, mem.NewHandler(), resource.Conf{ // Posts can only be read, created and deleted, not updated AllowedModes: []resource.Mode{resource.Read, resource.List, resource.Create, resource.Delete}, })) // Add a friendly alias to public posts // (equivalent to /users/:user_id/posts?filter={"published":true}) posts.Alias("public", url.Values{"filter": []string{"{\"published\":true}"}}) // Create API HTTP handler for the resource graph api, err := rest.NewHandler(index) if err != nil { log.Fatalf("Invalid API configuration: %s", err) } // Init a xhandler chain (see https://github.com/rs/xhandler) c := xhandler.Chain{} // Add close notifier handler so context is cancelled when the client closes // the connection c.UseC(xhandler.CloseHandler) // Add timeout handler c.UseC(xhandler.TimeoutHandler(2 * time.Second)) // Install a logger (see https://github.com/rs/xlog) c.UseC(xlog.NewHandler(xlog.Config{})) // Log API access c.UseC(xaccess.NewHandler()) // Add CORS support with passthrough option on so rest-layer can still // handle OPTIONS method c.UseC(cors.New(cors.Options{OptionsPassthrough: true}).HandlerC) // Bind the API under /api/ path http.Handle("/api/", http.StripPrefix("/api/", c.Handler(api))) // Serve it log.Print("Serving API on http://localhost:8080") if err := http.ListenAndServe(":8080", nil); err != nil { log.Fatal(err) } }
func ExampleIf() { c := xhandler.Chain{} // Add a timeout handler only if the URL path matches a prefix c.UseC(xhandler.If( func(ctx context.Context, w http.ResponseWriter, r *http.Request) bool { return strings.HasPrefix(r.URL.Path, "/with-timeout/") }, xhandler.TimeoutHandler(2*time.Second), )) http.Handle("/", c.Handler(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "Welcome to the home page!") }))) }
func construct(o *webOptions) xhandler.HandlerC { var chain xhandler.Chain chain.UseC(xhandler.CloseHandler) chain.UseC(xhandler.TimeoutHandler(o.reqTimeout)) chain.UseC(o.componentSetter) chain.UseC(o.templateCtxSetter) for _, m := range o.middlewares { chain.UseC(m) } chain.UseC(CompileInContext) chain.UseC(RenderInContext) if o.alwaysHTML { return chain.HandlerCF(WriteRenderedHTML) } return chain.HandlerCF(WriteRendered) }
func ExampleHandleTimeout() { var xh xhandler.HandlerC xh = xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello World")) if _, ok := ctx.Deadline(); ok { w.Write([]byte(" with deadline")) } }) // This handler adds a timeout to the handler xh = xhandler.TimeoutHandler(5 * time.Second)(xh) ctx := context.Background() // Bridge context aware handlers with http.Handler using xhandler.Handle() http.Handle("/", xhandler.New(ctx, xh)) if err := http.ListenAndServe(":8080", nil); err != nil { log.Fatal(err) } }
func ExampleMux() { c := xhandler.Chain{} // Append a context-aware middleware handler c.UseC(xhandler.CloseHandler) // Another context-aware middleware handler c.UseC(xhandler.TimeoutHandler(2 * time.Second)) mux := xmux.New() // Use c.Handler to terminate the chain with your final handler mux.GET("/welcome/:name", xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "Welcome %s!", xmux.Param(ctx, "name")) })) if err := http.ListenAndServe(":8080", c.Handler(mux)); err != nil { log.Fatal(err) } }
func ExampleChain() { c := xhandler.Chain{} // Append a context-aware middleware handler c.UseC(xhandler.CloseHandler) // Mix it with a non-context-aware middleware handler c.Use(cors.Default().Handler) // Another context-aware middleware handler c.UseC(xhandler.TimeoutHandler(2 * time.Second)) mux := http.NewServeMux() // Use c.Handler to terminate the chain with your final handler mux.Handle("/", c.Handler(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "Welcome to the home page!") }))) // You can reuse the same chain for other handlers mux.Handle("/api", c.Handler(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "Welcome to the API!") }))) }
func main() { if !checkFlags() { os.Exit(1) } sessionStore := sessions.NewCookieStore([]byte(*sessionHashKey), []byte(*sessionBlockKey)) // OpenID Connect Providers // Google oidcGoogleLoginRoute := "/logingoogle" oidcGoogleCBRoute := "/gcallback" oidcGoogle := &oidc.Google{ ClientID: *oidcGoogleClientID, ClientSecret: *oidcGoogleClientSecret, RedirectURI: *publicURL + oidcGoogleCBRoute, SessionStore: sessionStore, } // PayPal oidcPaypalLoginRoute := "/loginpaypal" oidcPaypalCBRoute := "/pcallback" oidcPaypal := &oidc.Paypal{ ClientID: *oidcPaypalClientID, ClientSecret: *oidcPaypalClientSecret, RedirectURI: *publicURL + oidcPaypalCBRoute, SessionStore: sessionStore, } // Dynamodb cfg := &aws.Config{} if *dynamodbEndpoint != "" { cfg.Endpoint = aws.String(*dynamodbEndpoint) } sess := session.New(cfg) if *debug { sess.Config.LogLevel = aws.LogLevel(aws.LogDebug) } // Model var m model.Model m = awsdynamo.NewModelFromSession(sess) // Controller // OAuth / OpenID Connect authCGoogle := controller.NewAuthController(m.UserPeer(), oidcGoogle, "google") authCPaypal := controller.NewAuthController(m.UserPeer(), oidcPaypal, "paypal") // Post Controller postContrData := &postDataProvider{ PostPeer: m.PostPeer(), UserPeer: m.UserPeer(), } postController := &controller.PostController{ Model: postContrData, } // Middleware baseChain := xhandler.Chain{} baseChain.UseC(xhandler.TimeoutHandler(2 * time.Second)) // Session management sessionMiddleware := middleware.Session{} sessionMiddleware.Init([]byte(*sessionHashKey), []byte(*sessionBlockKey)) baseChain.UseC(sessionMiddleware.Enable("posty-session")) // Chain for authenticated routes authedChain := xhandler.Chain{} authedChain = append(authedChain, baseChain...) authedChain.UseC(middleware.AuthenticatedFilter("/login")) authedChain.UseC(middleware.UserContext()) // Chain for authenticated routes with json response jsonChain := xhandler.Chain{} jsonChain = append(jsonChain, authedChain...) jsonChain.UseC(middleware.JSONWrapper()) // Chain for unauthenticated routes unauthedChain := xhandler.Chain{} unauthedChain = append(unauthedChain, baseChain...) unauthedChain.UseC(middleware.UnauthenticatedFilter("/")) // Main Context ctx := context.Background() route := func(chain xhandler.Chain, handler xhandler.HandlerC) web.Handler { return handle(ctx, chain.HandlerC(handler)) } // Routes mux := web.New() mux.Get("/api/posts", route(jsonChain, xhandler.HandlerFuncC(postController.Posts))) mux.Post("/api/posts", route(jsonChain, xhandler.HandlerFuncC(postController.Create))) mux.Delete("/api/posts/:id", route(jsonChain, xhandler.HandlerFuncC(postController.Remove))) // OIDC Routes mux.Get(oidcGoogleLoginRoute, route(unauthedChain, authCGoogle.Login())) mux.Get(oidcGoogleCBRoute, route(unauthedChain, authCGoogle.Callback("/"))) mux.Get(oidcPaypalLoginRoute, route(unauthedChain, authCPaypal.Login())) mux.Get(oidcPaypalCBRoute, route(unauthedChain, authCPaypal.Callback("/"))) mux.Get("/logout", route(authedChain, authCGoogle.Logout("/login"))) // Static file mux.Get("/login", route(unauthedChain, serveSingleFile(filepath.Join(*frontendPath, "login.html")))) mux.Get("/", route(authedChain, serveSingleFile(filepath.Join(*frontendPath, "index.html")))) mux.Get("/static/*", route(baseChain, serveFiles(filepath.Join(*frontendPath, "/static"), "/static/"))) log.Infof("Listening on %s", *listen) log.Fatal(http.ListenAndServe(":8080", gctx.ClearHandler(mux))) }
func Example() { var ( // Define a user resource schema user = schema.Schema{ "id": schema.Field{ Required: true, // When a field is read-only, on default values or hooks can // set their value. The client can't change it. ReadOnly: true, // This is a field hook called when a new user is created. // The schema.NewID hook is a provided hook to generate a // unique id when no value is provided. OnInit: &schema.NewID, // The Filterable and Sortable allows usage of filter and sort // on this field in requests. Filterable: true, Sortable: true, Validator: &schema.String{ Regexp: "^[0-9a-f]{32}$", }, }, "created": schema.Field{ Required: true, ReadOnly: true, Filterable: true, Sortable: true, OnInit: &schema.Now, Validator: &schema.Time{}, }, "updated": schema.Field{ Required: true, ReadOnly: true, Filterable: true, Sortable: true, OnInit: &schema.Now, // The OnUpdate hook is called when the item is edited. Here we use // provided Now hook which just return the current time. OnUpdate: &schema.Now, Validator: &schema.Time{}, }, // Define a name field as required with a string validator "name": schema.Field{ Required: true, Filterable: true, Validator: &schema.String{ MaxLen: 150, }, }, } // Define a post resource schema post = schema.Schema{ // schema.*Field are shortcuts for common fields (identical to users' same fields) "id": schema.IDField, "created": schema.CreatedField, "updated": schema.UpdatedField, // Define a user field which references the user owning the post. // See bellow, the content of this field is enforced by the fact // that posts is a sub-resource of users. "user": schema.Field{ Required: true, Filterable: true, Validator: &schema.Reference{ Path: "users", }, }, "public": schema.Field{ Filterable: true, Validator: &schema.Bool{}, }, // Sub-documents are handled via a sub-schema "meta": schema.Field{ Schema: &schema.Schema{ "title": schema.Field{ Required: true, Validator: &schema.String{ MaxLen: 150, }, }, "body": schema.Field{ Validator: &schema.String{ MaxLen: 100000, }, }, }, }, } ) // Create a REST API root resource index := resource.NewIndex() // Add a resource on /users[/:user_id] users := index.Bind("users", resource.New(user, mem.NewHandler(), resource.Conf{ // We allow all REST methods // (rest.ReadWrite is a shortcut for []rest.Mode{Create, Read, Update, Delete, List}) AllowedModes: resource.ReadWrite, })) // Bind a sub resource on /users/:user_id/posts[/:post_id] // and reference the user on each post using the "user" field of the posts resource. posts := users.Bind("posts", "user", resource.New(post, mem.NewHandler(), resource.Conf{ // Posts can only be read, created and deleted, not updated AllowedModes: []resource.Mode{resource.Read, resource.List, resource.Create, resource.Delete}, })) // Add a friendly alias to public posts // (equivalent to /users/:user_id/posts?filter={"public":true}) posts.Alias("public", url.Values{"filter": []string{"{\"public\"=true}"}}) // Create API HTTP handler for the resource graph api, err := rest.NewHandler(index) if err != nil { log.Fatalf("Invalid API configuration: %s", err) } // Init a xhandler chain (see https://github.com/rs/xhandler) c := xhandler.Chain{} // Add close notifier handler so context is cancelled when the client closes // the connection c.UseC(xhandler.CloseHandler) // Add timeout handler c.UseC(xhandler.TimeoutHandler(2 * time.Second)) // Install a logger (see https://github.com/rs/xlog) c.UseC(xlog.NewHandler(xlog.Config{})) // Log API access c.UseC(xaccess.NewHandler()) // Add CORS support with passthrough option on so rest-layer can still // handle OPTIONS method c.UseC(cors.New(cors.Options{OptionsPassthrough: true}).HandlerC) // Bind the API under /api/ path http.Handle("/api/", http.StripPrefix("/api/", c.Handler(api))) // Serve it log.Print("Serving API on http://localhost:8080") if err := http.ListenAndServe(":8080", nil); err != nil { log.Fatal(err) } }