github.com/mayra-cabrera/buffalo@v0.9.4-0.20170814145312-66d2e7772f11/default_context.go (about)

     1  package buffalo
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"net/url"
    10  	"sort"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/gobuffalo/buffalo/binding"
    15  	"github.com/gobuffalo/buffalo/render"
    16  	"github.com/gorilla/websocket"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  // assert that DefaultContext is implementing Context
    21  var _ Context = &DefaultContext{}
    22  var _ context.Context = &DefaultContext{}
    23  
    24  // DefaultContext is, as its name implies, a default
    25  // implementation of the Context interface.
    26  type DefaultContext struct {
    27  	context.Context
    28  	response    http.ResponseWriter
    29  	request     *http.Request
    30  	params      url.Values
    31  	logger      Logger
    32  	session     *Session
    33  	contentType string
    34  	data        map[string]interface{}
    35  	flash       *Flash
    36  }
    37  
    38  // Response returns the original Response for the request.
    39  func (d *DefaultContext) Response() http.ResponseWriter {
    40  	return d.response
    41  }
    42  
    43  // Request returns the original Request.
    44  func (d *DefaultContext) Request() *http.Request {
    45  	return d.request
    46  }
    47  
    48  // Params returns all of the parameters for the request,
    49  // including both named params and query string parameters.
    50  func (d *DefaultContext) Params() ParamValues {
    51  	return d.params
    52  }
    53  
    54  // Logger returns the Logger for this context.
    55  func (d *DefaultContext) Logger() Logger {
    56  	return d.logger
    57  }
    58  
    59  // Param returns a param, either named or query string,
    60  // based on the key.
    61  func (d *DefaultContext) Param(key string) string {
    62  	return d.Params().Get(key)
    63  }
    64  
    65  // Set a value onto the Context. Any value set onto the Context
    66  // will be automatically available in templates.
    67  func (d *DefaultContext) Set(key string, value interface{}) {
    68  	d.data[key] = value
    69  }
    70  
    71  // Value that has previously stored on the context.
    72  func (d *DefaultContext) Value(key interface{}) interface{} {
    73  	if k, ok := key.(string); ok {
    74  		if v, ok := d.data[k]; ok {
    75  			return v
    76  		}
    77  	}
    78  	return d.Context.Value(key)
    79  }
    80  
    81  // Session for the associated Request.
    82  func (d *DefaultContext) Session() *Session {
    83  	return d.session
    84  }
    85  
    86  // Cookies for the associated request and response.
    87  func (d *DefaultContext) Cookies() *Cookies {
    88  	return &Cookies{d.request, d.response}
    89  }
    90  
    91  // Flash messages for the associated Request.
    92  func (d *DefaultContext) Flash() *Flash {
    93  	return d.flash
    94  }
    95  
    96  // Render a status code and render.Renderer to the associated Response.
    97  // The request parameters will be made available to the render.Renderer
    98  // "{{.params}}". Any values set onto the Context will also automatically
    99  // be made available to the render.Renderer. To render "no content" pass
   100  // in a nil render.Renderer.
   101  func (d *DefaultContext) Render(status int, rr render.Renderer) error {
   102  	start := time.Now()
   103  	defer func() {
   104  		d.LogField("render", time.Since(start))
   105  	}()
   106  	if rr != nil {
   107  		data := d.data
   108  		pp := map[string]string{}
   109  		for k, v := range d.params {
   110  			pp[k] = v[0]
   111  		}
   112  		data["params"] = pp
   113  		data["flash"] = d.Flash().data
   114  		data["session"] = d.Session()
   115  		data["request"] = d.Request()
   116  		bb := &bytes.Buffer{}
   117  
   118  		err := rr.Render(bb, data)
   119  		if err != nil {
   120  			return HTTPError{Status: 500, Cause: errors.WithStack(err)}
   121  		}
   122  
   123  		if d.Session() != nil {
   124  			d.Flash().Clear()
   125  			d.Flash().persist(d.Session())
   126  		}
   127  
   128  		d.Response().Header().Set("Content-Type", rr.ContentType())
   129  		d.Response().WriteHeader(status)
   130  		_, err = io.Copy(d.Response(), bb)
   131  		if err != nil {
   132  			return HTTPError{Status: 500, Cause: errors.WithStack(err)}
   133  		}
   134  
   135  		return nil
   136  	}
   137  	d.Response().WriteHeader(status)
   138  	return nil
   139  }
   140  
   141  // Bind the interface to the request.Body. The type of binding
   142  // is dependent on the "Content-Type" for the request. If the type
   143  // is "application/json" it will use "json.NewDecoder". If the type
   144  // is "application/xml" it will use "xml.NewDecoder". See the
   145  // github.com/gobuffalo/buffalo/binding package for more details.
   146  func (d *DefaultContext) Bind(value interface{}) error {
   147  	return binding.Exec(d.Request(), value)
   148  }
   149  
   150  // LogField adds the key/value pair onto the Logger to be printed out
   151  // as part of the request logging. This allows you to easily add things
   152  // like metrics (think DB times) to your request.
   153  func (d *DefaultContext) LogField(key string, value interface{}) {
   154  	d.logger = d.logger.WithField(key, value)
   155  }
   156  
   157  // LogFields adds the key/value pairs onto the Logger to be printed out
   158  // as part of the request logging. This allows you to easily add things
   159  // like metrics (think DB times) to your request.
   160  func (d *DefaultContext) LogFields(values map[string]interface{}) {
   161  	d.logger = d.logger.WithFields(values)
   162  }
   163  
   164  func (d *DefaultContext) Error(status int, err error) error {
   165  	return HTTPError{Status: status, Cause: errors.WithStack(err)}
   166  }
   167  
   168  // Websocket returns an upgraded github.com/gorilla/websocket.Conn
   169  // that can then be used to work with websockets easily.
   170  func (d *DefaultContext) Websocket() (*websocket.Conn, error) {
   171  	return defaultUpgrader.Upgrade(d.Response(), d.Request(), nil)
   172  }
   173  
   174  // Redirect a request with the given status to the given URL.
   175  func (d *DefaultContext) Redirect(status int, url string, args ...interface{}) error {
   176  	d.Flash().persist(d.Session())
   177  
   178  	http.Redirect(d.Response(), d.Request(), fmt.Sprintf(url, args...), status)
   179  	return nil
   180  }
   181  
   182  // Data contains all the values set through Get/Set.
   183  func (d *DefaultContext) Data() map[string]interface{} {
   184  	return d.data
   185  }
   186  
   187  func (d *DefaultContext) String() string {
   188  	bb := make([]string, 0, len(d.data))
   189  
   190  	for k, v := range d.data {
   191  		if _, ok := v.(RouteHelperFunc); !ok {
   192  			bb = append(bb, fmt.Sprintf("%s: %s", k, v))
   193  		}
   194  	}
   195  	sort.Strings(bb)
   196  	return strings.Join(bb, "\n\n")
   197  }
   198  
   199  var defaultUpgrader = websocket.Upgrader{
   200  	ReadBufferSize:  1024,
   201  	WriteBufferSize: 1024,
   202  	CheckOrigin:     func(r *http.Request) bool { return true },
   203  }