github.com/System-Glitch/goyave/v3@v3.6.1-0.20210226143142-ac2fe42ee80e/request.go (about)

     1  package goyave
     2  
     3  import (
     4  	"net"
     5  	"net/http"
     6  	"net/url"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/System-Glitch/goyave/v3/cors"
    11  	"github.com/imdario/mergo"
    12  
    13  	"github.com/System-Glitch/goyave/v3/helper/filesystem"
    14  	"github.com/System-Glitch/goyave/v3/validation"
    15  	"github.com/google/uuid"
    16  )
    17  
    18  // Request struct represents an http request.
    19  // Contains the validated body in the Data attribute if the route was defined with a request generator function
    20  type Request struct {
    21  	httpRequest *http.Request
    22  	corsOptions *cors.Options
    23  	cookies     []*http.Cookie
    24  	route       *Route
    25  	User        interface{}
    26  	Rules       *validation.Rules
    27  	Data        map[string]interface{}
    28  	Extra       map[string]interface{}
    29  	Params      map[string]string
    30  	Lang        string
    31  }
    32  
    33  // Request return the raw http request.
    34  // Prefer using the "goyave.Request" accessors.
    35  func (r *Request) Request() *http.Request {
    36  	return r.httpRequest
    37  }
    38  
    39  // Method specifies the HTTP method (GET, POST, PUT, etc.).
    40  func (r *Request) Method() string {
    41  	return r.httpRequest.Method
    42  }
    43  
    44  // Protocol the protocol used by this request, "HTTP/1.1" for example.
    45  func (r *Request) Protocol() string {
    46  	return r.httpRequest.Proto
    47  }
    48  
    49  // URI specifies the URI being requested.
    50  // Use this if you absolutely need the raw query params, url, etc.
    51  // Otherwise use the provided methods and fields of the "goyave.Request".
    52  func (r *Request) URI() *url.URL {
    53  	return r.httpRequest.URL
    54  }
    55  
    56  // Route returns the current route.
    57  func (r *Request) Route() *Route {
    58  	return r.route
    59  }
    60  
    61  // Header contains the request header fields either received
    62  // by the server or to be sent by the client.
    63  // Header names are case-insensitive.
    64  //
    65  // If the raw request has the following header lines,
    66  //
    67  //	Host: example.com
    68  //	accept-encoding: gzip, deflate
    69  //	Accept-Language: en-us
    70  //	fOO: Bar
    71  //	foo: two
    72  //
    73  // then the header map will look like this:
    74  //
    75  //	Header = map[string][]string{
    76  //		"Accept-Encoding": {"gzip, deflate"},
    77  //		"Accept-Language": {"en-us"},
    78  //		"Foo": {"Bar", "two"},
    79  //	}
    80  func (r *Request) Header() http.Header {
    81  	return r.httpRequest.Header
    82  }
    83  
    84  // ContentLength records the length of the associated content.
    85  // The value -1 indicates that the length is unknown.
    86  func (r *Request) ContentLength() int64 {
    87  	return r.httpRequest.ContentLength
    88  }
    89  
    90  // RemoteAddress allows to record the network address that
    91  // sent the request, usually for logging.
    92  func (r *Request) RemoteAddress() string {
    93  	return r.httpRequest.RemoteAddr
    94  }
    95  
    96  // Cookies returns the HTTP cookies sent with the request.
    97  func (r *Request) Cookies(name string) []*http.Cookie {
    98  	if r.cookies == nil {
    99  		r.cookies = r.httpRequest.Cookies()
   100  	}
   101  	return r.cookies
   102  }
   103  
   104  // Referrer returns the referring URL, if sent in the request.
   105  func (r *Request) Referrer() string {
   106  	return r.httpRequest.Referer()
   107  }
   108  
   109  // UserAgent returns the client's User-Agent, if sent in the request.
   110  func (r *Request) UserAgent() string {
   111  	return r.httpRequest.UserAgent()
   112  }
   113  
   114  // BasicAuth returns the username and password provided in the request's
   115  // Authorization header, if the request uses HTTP Basic Authentication.
   116  func (r *Request) BasicAuth() (username, password string, ok bool) {
   117  	return r.httpRequest.BasicAuth()
   118  }
   119  
   120  // BearerToken extract the auth token from the "Authorization" header.
   121  // Only takes tokens of type "Bearer".
   122  // Returns empty string if no token found or the header is invalid.
   123  func (r *Request) BearerToken() (string, bool) {
   124  	const schema = "Bearer "
   125  	header := r.Header().Get("Authorization")
   126  	if !strings.HasPrefix(header, schema) {
   127  		return "", false
   128  	}
   129  	return strings.TrimSpace(header[len(schema):]), true
   130  }
   131  
   132  // CORSOptions returns the CORS options applied to this request, or nil.
   133  // The returned object is a copy of the options applied to the router.
   134  // Therefore, altering the returned object will not alter the router's options.
   135  func (r *Request) CORSOptions() *cors.Options {
   136  	if r.corsOptions == nil {
   137  		return nil
   138  	}
   139  
   140  	cpy := *r.corsOptions
   141  	return &cpy
   142  }
   143  
   144  // Has check if the given field exists in the request data.
   145  func (r *Request) Has(field string) bool {
   146  	_, exists := r.Data[field]
   147  	return exists
   148  }
   149  
   150  // String get a string field from the request data.
   151  // Panics if the field is not a string.
   152  func (r *Request) String(field string) string {
   153  	str, ok := r.Data[field].(string)
   154  	if !ok {
   155  		ErrLogger.Panicf("Field \"%s\" is not a string", field)
   156  	}
   157  	return str
   158  }
   159  
   160  // Numeric get a numeric field from the request data.
   161  // Panics if the field is not numeric.
   162  func (r *Request) Numeric(field string) float64 {
   163  	str, ok := r.Data[field].(float64)
   164  	if !ok {
   165  		ErrLogger.Panicf("Field \"%s\" is not numeric", field)
   166  	}
   167  	return str
   168  }
   169  
   170  // Integer get an integer field from the request data.
   171  // Panics if the field is not an integer.
   172  func (r *Request) Integer(field string) int {
   173  	str, ok := r.Data[field].(int)
   174  	if !ok {
   175  		ErrLogger.Panicf("Field \"%s\" is not an integer", field)
   176  	}
   177  	return str
   178  }
   179  
   180  // Bool get a bool field from the request data.
   181  // Panics if the field is not a bool.
   182  func (r *Request) Bool(field string) bool {
   183  	str, ok := r.Data[field].(bool)
   184  	if !ok {
   185  		ErrLogger.Panicf("Field \"%s\" is not a bool", field)
   186  	}
   187  	return str
   188  }
   189  
   190  // File get a file field from the request data.
   191  // Panics if the field is not numeric.
   192  func (r *Request) File(field string) []filesystem.File {
   193  	str, ok := r.Data[field].([]filesystem.File)
   194  	if !ok {
   195  		ErrLogger.Panicf("Field \"%s\" is not a file", field)
   196  	}
   197  	return str
   198  }
   199  
   200  // Timezone get a timezone field from the request data.
   201  // Panics if the field is not a timezone.
   202  func (r *Request) Timezone(field string) *time.Location {
   203  	str, ok := r.Data[field].(*time.Location)
   204  	if !ok {
   205  		ErrLogger.Panicf("Field \"%s\" is not a timezone", field)
   206  	}
   207  	return str
   208  }
   209  
   210  // IP get an IP field from the request data.
   211  // Panics if the field is not an IP.
   212  func (r *Request) IP(field string) net.IP {
   213  	str, ok := r.Data[field].(net.IP)
   214  	if !ok {
   215  		ErrLogger.Panicf("Field \"%s\" is not an IP", field)
   216  	}
   217  	return str
   218  }
   219  
   220  // URL get a URL field from the request data.
   221  // Panics if the field is not a URL.
   222  func (r *Request) URL(field string) *url.URL {
   223  	str, ok := r.Data[field].(*url.URL)
   224  	if !ok {
   225  		ErrLogger.Panicf("Field \"%s\" is not a URL", field)
   226  	}
   227  	return str
   228  }
   229  
   230  // UUID get a UUID field from the request data.
   231  // Panics if the field is not a UUID.
   232  func (r *Request) UUID(field string) uuid.UUID {
   233  	str, ok := r.Data[field].(uuid.UUID)
   234  	if !ok {
   235  		ErrLogger.Panicf("Field \"%s\" is not an UUID", field)
   236  	}
   237  	return str
   238  }
   239  
   240  // Date get a date field from the request data.
   241  // Panics if the field is not a date.
   242  func (r *Request) Date(field string) time.Time {
   243  	str, ok := r.Data[field].(time.Time)
   244  	if !ok {
   245  		ErrLogger.Panicf("Field \"%s\" is not a date", field)
   246  	}
   247  	return str
   248  }
   249  
   250  // Object get an object field from the request data.
   251  // Panics if the field is not an object.
   252  func (r *Request) Object(field string) map[string]interface{} {
   253  	str, ok := r.Data[field].(map[string]interface{})
   254  	if !ok {
   255  		ErrLogger.Panicf("Field \"%s\" is not an object", field)
   256  	}
   257  	return str
   258  }
   259  
   260  // ToStruct map the request data to a struct.
   261  //  type UserInsertRequest struct {
   262  // 	 Username string
   263  // 	 Email string
   264  //  }
   265  //  //...
   266  //  userInsertRequest := UserInsertRequest{}
   267  //  if err := request.ToStruct(&userInsertRequest); err != nil {
   268  //   panic(err)
   269  //  }
   270  func (r *Request) ToStruct(dst interface{}) error {
   271  	return mergo.Map(dst, r.Data)
   272  }
   273  
   274  func (r *Request) validate() validation.Errors {
   275  	if r.Rules == nil {
   276  		return nil
   277  	}
   278  
   279  	contentType := r.httpRequest.Header.Get("Content-Type")
   280  	errors := validation.Validate(r.Data, r.Rules, strings.HasPrefix(contentType, "application/json"), r.Lang)
   281  	if len(errors) > 0 {
   282  		return errors
   283  	}
   284  
   285  	return nil
   286  }