2016-05-30 16:08:09 +02:00
/ *
2016-07-07 00:25:50 +02:00
Context . go Implements : . / context / context . go
2016-05-30 16:08:09 +02:00
* /
package iris
import (
2016-06-14 07:45:40 +02:00
"bufio"
"encoding/base64"
"encoding/json"
"encoding/xml"
"fmt"
"io"
"net"
"os"
"path"
2016-05-30 16:08:09 +02:00
"reflect"
"runtime"
2016-06-14 07:45:40 +02:00
"strconv"
"strings"
"sync"
"time"
2016-06-22 11:45:57 +02:00
2016-06-24 00:34:49 +02:00
"github.com/iris-contrib/errors"
2016-06-22 11:45:57 +02:00
"github.com/iris-contrib/formBinder"
"github.com/kataras/iris/config"
"github.com/kataras/iris/context"
"github.com/kataras/iris/utils"
"github.com/klauspost/compress/gzip"
"github.com/valyala/fasthttp"
2016-05-30 16:08:09 +02:00
)
const (
// DefaultUserAgent default to 'iris' but it is not used anywhere yet
2016-06-14 07:45:40 +02:00
defaultUserAgent = "iris"
2016-05-30 16:08:09 +02:00
// ContentType represents the header["Content-Type"]
2016-06-14 07:45:40 +02:00
contentType = "Content-Type"
2016-05-30 16:08:09 +02:00
// ContentLength represents the header["Content-Length"]
2016-06-14 07:45:40 +02:00
contentLength = "Content-Length"
2016-08-14 04:44:36 +02:00
// contentEncodingHeader represents the header["Content-Encoding"]
contentEncodingHeader = "Content-Encoding"
// varyHeader represents the header "Vary"
varyHeader = "Vary"
// acceptEncodingHeader represents the header key & value "Accept-Encoding"
acceptEncodingHeader = "Accept-Encoding"
2016-05-30 16:08:09 +02:00
// ContentHTML is the string of text/html response headers
2016-06-14 07:45:40 +02:00
contentHTML = "text/html"
Update to 4.0.0-alpha.3 - Response Engines, 'inject' the context.JSON/JSONP/Text/Data/Markdown/Render, Read HISTORY.md
## 4.0.0-alpha.2 -> 4.0.0-alpha.3
**New**
A **Response Engine** gives you the freedom to create/change the
render/response writer for
- `context.JSON`
- `context.JSONP`
- `context.XML`
- `context.Text`
- `context.Markdown`
- `context.Data`
- `context.Render("my_custom_type",mystructOrData{},
iris.RenderOptions{"gzip":false,"charset":"UTF-8"})`
- `context.MarkdownString`
- `iris.ResponseString(...)`
**Fix**
- https://github.com/kataras/iris/issues/294
**Small changes**
- `iris.Config.Charset`, before alpha.3 was `iris.Config.Rest.Charset` &
`iris.Config.Render.Template.Charset`, but you can override it at
runtime by passinth a map `iris.RenderOptions` on the `context.Render`
call .
- `iris.Config.IsDevelopment` , before alpha.1 was
`iris.Config.Render.Template.IsDevelopment`
**Websockets changes**
No need to import the `github.com/kataras/iris/websocket` to use the
`Connection` iteral, the websocket moved inside `kataras/iris` , now all
exported variables' names have the prefix of `Websocket`, so the old
`websocket.Connection` is now `iris.WebsocketConnection`.
Generally, no other changes on the 'frontend API', for response engines
examples and how you can register your own to add more features on
existing response engines or replace them, look
[here](https://github.com/iris-contrib/response).
**BAD SIDE**: E-Book is still pointing on the v3 release, but will be
updated soon.
2016-07-18 16:40:42 +02:00
// ContentBinary header value for binary data.
contentBinary = "application/octet-stream"
// ContentJSON header value for JSON data.
contentJSON = "application/json"
// ContentJSONP header value for JSONP data.
contentJSONP = "application/javascript"
// ContentText header value for Text data.
contentText = "text/plain"
// ContentXML header value for XML data.
contentXML = "text/xml"
// contentMarkdown custom key/content type, the real is the text/html
contentMarkdown = "text/markdown"
2016-05-30 16:08:09 +02:00
// LastModified "Last-Modified"
2016-06-14 07:45:40 +02:00
lastModified = "Last-Modified"
2016-05-30 16:08:09 +02:00
// IfModifiedSince "If-Modified-Since"
2016-06-14 07:45:40 +02:00
ifModifiedSince = "If-Modified-Since"
2016-05-30 16:08:09 +02:00
// ContentDisposition "Content-Disposition"
2016-06-14 07:45:40 +02:00
contentDisposition = "Content-Disposition"
2016-05-30 16:08:09 +02:00
// stopExecutionPosition used inside the Context, is the number which shows us that the context's middleware manualy stop the execution
stopExecutionPosition = 255
2016-07-02 15:02:33 +02:00
// used inside GetFlash to store the lifetime request flash messages
flashMessagesStoreContextKey = "_iris_flash_messages_"
2016-07-07 00:25:50 +02:00
flashMessageCookiePrefix = "_iris_flash_message_"
cookieHeaderID = "Cookie: "
cookieHeaderIDLen = len ( cookieHeaderID )
2016-05-30 16:08:09 +02:00
)
2016-06-14 07:45:40 +02:00
// this pool is used everywhere needed in the iris for example inside party-> Static
var gzipWriterPool = sync . Pool { New : func ( ) interface { } { return & gzip . Writer { } } }
// errors
var (
errTemplateExecute = errors . New ( "Unable to execute a template. Trace: %s" )
errFlashNotFound = errors . New ( "Unable to get flash message. Trace: Cookie does not exists" )
errSessionNil = errors . New ( "Unable to set session, Config().Session.Provider is nil, please refer to the docs!" )
errNoForm = errors . New ( "Request has no any valid form" )
errWriteJSON = errors . New ( "Before JSON be written to the body, JSON Encoder returned an error. Trace: %s" )
errRenderMarshalled = errors . New ( "Before +type Rendering, MarshalIndent returned an error. Trace: %s" )
errReadBody = errors . New ( "While trying to read %s from the request body. Trace %s" )
errServeContent = errors . New ( "While trying to serve content to the client. Trace %s" )
)
2016-05-30 16:08:09 +02:00
type (
2016-07-13 13:59:37 +02:00
2016-05-31 10:05:42 +02:00
// Map is just a conversion for a map[string]interface{}
2016-07-08 19:41:50 +02:00
// should not be used inside Render when PongoEngine is used.
2016-05-30 16:08:09 +02:00
Map map [ string ] interface { }
// Context is resetting every time a request is coming to the server
// it is not good practice to use this object in goroutines, for these cases use the .Clone()
Context struct {
* fasthttp . RequestCtx
2016-06-14 07:45:40 +02:00
Params PathParameters
framework * Framework
2016-05-30 16:08:09 +02:00
//keep track all registed middleware (handlers)
:rainbow: sessions were re-written, update to 4.0.0-alpha.2, read HISTORY.md
**Sessions were re-written **
- Developers can use more than one 'session database', at the same time,
to store the sessions
- Easy to develop a custom session database (only two functions are
required (Load & Update)), [learn
more](https://github.com/iris-contrib/sessiondb/blob/master/redis/database.go)
- Session databases are located
[here](https://github.com/iris-contrib/sessiondb), contributions are
welcome
- The only frontend deleted 'thing' is the: **config.Sessions.Provider**
- No need to register a database, the sessions works out-of-the-box
- No frontend/API changes except the
`context.Session().Set/Delete/Clear`, they doesn't return errors
anymore, btw they (errors) were always nil :)
- Examples (master branch) were updated.
```sh
$ go get github.com/iris-contrib/sessiondb/$DATABASE
```
```go
db := $DATABASE.New(configurationHere{})
iris.UseSessionDB(db)
```
> Note: Book is not updated yet, examples are up-to-date as always.
2016-07-15 19:50:36 +02:00
middleware Middleware
session * session
2016-05-30 16:08:09 +02:00
// pos is the position number of the Context, look .Next to understand
pos uint8
}
)
var _ context . IContext = & Context { }
// GetRequestCtx returns the current fasthttp context
func ( ctx * Context ) GetRequestCtx ( ) * fasthttp . RequestCtx {
return ctx . RequestCtx
}
// Do calls the first handler only, it's like Next with negative pos, used only on Router&MemoryRouter
func ( ctx * Context ) Do ( ) {
ctx . pos = 0
ctx . middleware [ 0 ] . Serve ( ctx )
}
// Next calls all the next handler from the middleware stack, it used inside a middleware
func ( ctx * Context ) Next ( ) {
//set position to the next
ctx . pos ++
midLen := uint8 ( len ( ctx . middleware ) )
//run the next
if ctx . pos < midLen {
ctx . middleware [ ctx . pos ] . Serve ( ctx )
}
}
// StopExecution just sets the .pos to 255 in order to not move to the next middlewares(if any)
func ( ctx * Context ) StopExecution ( ) {
ctx . pos = stopExecutionPosition
}
//
// IsStopped checks and returns true if the current position of the Context is 255, means that the StopExecution has called
func ( ctx * Context ) IsStopped ( ) bool {
return ctx . pos == stopExecutionPosition
}
// GetHandlerName as requested returns the stack-name of the function which the Middleware is setted from
func ( ctx * Context ) GetHandlerName ( ) string {
return runtime . FuncForPC ( reflect . ValueOf ( ctx . middleware [ len ( ctx . middleware ) - 1 ] ) . Pointer ( ) ) . Name ( )
}
2016-06-14 07:45:40 +02:00
/* Request */
// Param returns the string representation of the key's path named parameter's value
func ( ctx * Context ) Param ( key string ) string {
return ctx . Params . Get ( key )
}
// ParamInt returns the int representation of the key's path named parameter's value
func ( ctx * Context ) ParamInt ( key string ) ( int , error ) {
2016-06-26 07:44:10 +02:00
return strconv . Atoi ( ctx . Param ( key ) )
2016-06-14 07:45:40 +02:00
}
2016-06-26 07:46:04 +02:00
// ParamInt64 returns the int64 representation of the key's path named parameter's value
func ( ctx * Context ) ParamInt64 ( key string ) ( int64 , error ) {
return strconv . ParseInt ( ctx . Param ( key ) , 10 , 64 )
}
2016-06-14 07:45:40 +02:00
// URLParam returns the get parameter from a request , if any
func ( ctx * Context ) URLParam ( key string ) string {
return string ( ctx . RequestCtx . Request . URI ( ) . QueryArgs ( ) . Peek ( key ) )
}
// URLParams returns a map of a list of each url(query) parameter
func ( ctx * Context ) URLParams ( ) map [ string ] string {
urlparams := make ( map [ string ] string )
ctx . RequestCtx . Request . URI ( ) . QueryArgs ( ) . VisitAll ( func ( key , value [ ] byte ) {
urlparams [ string ( key ) ] = string ( value )
} )
return urlparams
}
2016-06-26 07:44:10 +02:00
// URLParamInt returns the url query parameter as int value from a request , returns error on parse fail
2016-06-14 07:45:40 +02:00
func ( ctx * Context ) URLParamInt ( key string ) ( int , error ) {
return strconv . Atoi ( ctx . URLParam ( key ) )
}
2016-06-26 07:44:10 +02:00
// URLParamInt64 returns the url query parameter as int64 value from a request , returns error on parse fail
func ( ctx * Context ) URLParamInt64 ( key string ) ( int64 , error ) {
2016-06-26 07:46:04 +02:00
return strconv . ParseInt ( ctx . URLParam ( key ) , 10 , 64 )
2016-06-26 07:44:10 +02:00
}
2016-06-14 07:45:40 +02:00
// MethodString returns the HTTP Method
func ( ctx * Context ) MethodString ( ) string {
return utils . BytesToString ( ctx . Method ( ) )
}
// HostString returns the Host of the request( the url as string )
func ( ctx * Context ) HostString ( ) string {
return utils . BytesToString ( ctx . Host ( ) )
}
// VirtualHostname returns the hostname that user registers, host path maybe differs from the real which is HostString, which taken from a net.listener
func ( ctx * Context ) VirtualHostname ( ) string {
realhost := ctx . HostString ( )
2016-06-30 04:58:04 +02:00
hostname := realhost
2016-07-07 17:17:34 +02:00
virtualhost := ctx . framework . Servers . Main ( ) . Hostname ( )
2016-06-30 04:58:04 +02:00
2016-06-14 07:45:40 +02:00
if portIdx := strings . IndexByte ( hostname , ':' ) ; portIdx > 0 {
hostname = hostname [ 0 : portIdx ]
}
2016-06-30 04:58:04 +02:00
if idxDotAnd := strings . LastIndexByte ( hostname , '.' ) ; idxDotAnd > 0 {
s := hostname [ idxDotAnd : ]
2016-07-20 23:03:36 +02:00
// means that we have the request's host mymachine.com or 127.0.0.1/0.0.0.0, but for the second option we will need to replace it with the hostname that the dev was registered
// this needed to parse correct the {{ url }} iris global template engine's function
2016-06-30 04:58:04 +02:00
if s == ".1" {
hostname = strings . Replace ( hostname , "127.0.0.1" , virtualhost , 1 )
2016-07-20 23:03:36 +02:00
} else if s == ".0" {
hostname = strings . Replace ( hostname , "0.0.0.0" , virtualhost , 1 )
2016-06-30 04:58:04 +02:00
}
2016-07-20 23:03:36 +02:00
//
2016-06-30 04:58:04 +02:00
} else {
hostname = strings . Replace ( hostname , "localhost" , virtualhost , 1 )
}
2016-06-14 07:45:40 +02:00
return hostname
}
// PathString returns the full escaped path as string
// for unescaped use: ctx.RequestCtx.RequestURI() or RequestPath(escape bool)
func ( ctx * Context ) PathString ( ) string {
2016-08-18 02:20:59 +02:00
return ctx . RequestPath ( ! ctx . framework . Config . DisablePathEscape )
2016-06-14 07:45:40 +02:00
}
// RequestPath returns the requested path
func ( ctx * Context ) RequestPath ( escape bool ) string {
if escape {
2016-08-18 02:20:59 +02:00
// return utils.BytesToString(ctx.RequestCtx.Path())
return utils . BytesToString ( ctx . RequestCtx . URI ( ) . PathOriginal ( ) )
2016-06-14 07:45:40 +02:00
}
return utils . BytesToString ( ctx . RequestCtx . RequestURI ( ) )
}
// RequestIP gets just the Remote Address from the client.
func ( ctx * Context ) RequestIP ( ) string {
if ip , _ , err := net . SplitHostPort ( strings . TrimSpace ( ctx . RequestCtx . RemoteAddr ( ) . String ( ) ) ) ; err == nil {
return ip
}
return ""
}
// RemoteAddr is like RequestIP but it checks for proxy servers also, tries to get the real client's request IP
func ( ctx * Context ) RemoteAddr ( ) string {
header := string ( ctx . RequestCtx . Request . Header . Peek ( "X-Real-Ip" ) )
realIP := strings . TrimSpace ( header )
if realIP != "" {
return realIP
}
realIP = string ( ctx . RequestCtx . Request . Header . Peek ( "X-Forwarded-For" ) )
idx := strings . IndexByte ( realIP , ',' )
if idx >= 0 {
realIP = realIP [ 0 : idx ]
}
realIP = strings . TrimSpace ( realIP )
if realIP != "" {
return realIP
}
return ctx . RequestIP ( )
}
// RequestHeader returns the request header's value
// accepts one parameter, the key of the header (string)
// returns string
func ( ctx * Context ) RequestHeader ( k string ) string {
return utils . BytesToString ( ctx . RequestCtx . Request . Header . Peek ( k ) )
}
2016-07-07 00:25:50 +02:00
// FormValueString returns a single value, as string, from post request's data
func ( ctx * Context ) FormValueString ( name string ) string {
2016-06-28 09:19:17 +02:00
return string ( ctx . FormValue ( name ) )
2016-06-14 07:45:40 +02:00
}
2016-07-07 02:36:48 +02:00
// FormValues returns a slice of string from post request's data
func ( ctx * Context ) FormValues ( name string ) [ ] string {
2016-06-14 07:45:40 +02:00
arrBytes := ctx . PostArgs ( ) . PeekMulti ( name )
arrStr := make ( [ ] string , len ( arrBytes ) )
for i , v := range arrBytes {
arrStr [ i ] = string ( v )
}
return arrStr
}
2016-07-28 11:13:54 +02:00
// PostValuesAll returns all post data values with their keys
// multipart, form data, get & post query arguments
func ( ctx * Context ) PostValuesAll ( ) ( valuesAll map [ string ] [ ] string ) {
reqCtx := ctx . RequestCtx
valuesAll = make ( map [ string ] [ ] string )
// first check if we have multipart form
multipartForm , err := reqCtx . MultipartForm ( )
if err == nil {
//we have multipart form
return multipartForm . Value
}
// if no multipart and post arguments ( means normal form)
if reqCtx . PostArgs ( ) . Len ( ) == 0 && reqCtx . QueryArgs ( ) . Len ( ) == 0 {
return // no found
}
reqCtx . PostArgs ( ) . VisitAll ( func ( k [ ] byte , v [ ] byte ) {
key := string ( k )
value := string ( v )
// for slices
if valuesAll [ key ] != nil {
valuesAll [ key ] = append ( valuesAll [ key ] , value )
} else {
valuesAll [ key ] = [ ] string { value }
}
} )
reqCtx . QueryArgs ( ) . VisitAll ( func ( k [ ] byte , v [ ] byte ) {
key := string ( k )
value := string ( v )
// for slices
if valuesAll [ key ] != nil {
valuesAll [ key ] = append ( valuesAll [ key ] , value )
} else {
valuesAll [ key ] = [ ] string { value }
}
} )
return
}
// PostValues returns the post data values as []string of a single key/name
func ( ctx * Context ) PostValues ( name string ) [ ] string {
values := make ( [ ] string , 0 )
if v := ctx . PostValuesAll ( ) ; v != nil && len ( v ) > 0 {
values = v [ name ]
}
return values
}
// PostValue returns the post data value of a single key/name
// returns an empty string if nothing found
func ( ctx * Context ) PostValue ( name string ) string {
if v := ctx . PostValues ( name ) ; len ( v ) > 0 {
return v [ 0 ]
}
return ""
}
2016-06-14 07:45:40 +02:00
// Subdomain returns the subdomain (string) of this request, if any
func ( ctx * Context ) Subdomain ( ) ( subdomain string ) {
host := ctx . HostString ( )
if index := strings . IndexByte ( host , '.' ) ; index > 0 {
subdomain = host [ 0 : index ]
}
return
}
// ReadJSON reads JSON from request's body
func ( ctx * Context ) ReadJSON ( jsonObject interface { } ) error {
data := ctx . RequestCtx . Request . Body ( )
decoder := json . NewDecoder ( strings . NewReader ( string ( data ) ) )
err := decoder . Decode ( jsonObject )
//err != nil fix by @shiena
if err != nil && err != io . EOF {
return errReadBody . Format ( "JSON" , err . Error ( ) )
}
return nil
}
// ReadXML reads XML from request's body
func ( ctx * Context ) ReadXML ( xmlObject interface { } ) error {
data := ctx . RequestCtx . Request . Body ( )
decoder := xml . NewDecoder ( strings . NewReader ( string ( data ) ) )
err := decoder . Decode ( xmlObject )
//err != nil fix by @shiena
if err != nil && err != io . EOF {
return errReadBody . Format ( "XML" , err . Error ( ) )
}
return nil
}
// ReadForm binds the formObject with the form data
// it supports any kind of struct
func ( ctx * Context ) ReadForm ( formObject interface { } ) error {
reqCtx := ctx . RequestCtx
// first check if we have multipart form
multipartForm , err := reqCtx . MultipartForm ( )
if err == nil {
//we have multipart form
return errReadBody . With ( formBinder . Decode ( multipartForm . Value , formObject ) )
}
// if no multipart and post arguments ( means normal form)
if reqCtx . PostArgs ( ) . Len ( ) == 0 && reqCtx . QueryArgs ( ) . Len ( ) == 0 {
return errReadBody . With ( errNoForm . Return ( ) )
}
form := make ( map [ string ] [ ] string , reqCtx . PostArgs ( ) . Len ( ) + reqCtx . QueryArgs ( ) . Len ( ) )
reqCtx . PostArgs ( ) . VisitAll ( func ( k [ ] byte , v [ ] byte ) {
key := string ( k )
value := string ( v )
// for slices
if form [ key ] != nil {
form [ key ] = append ( form [ key ] , value )
} else {
form [ key ] = [ ] string { value }
}
} )
reqCtx . QueryArgs ( ) . VisitAll ( func ( k [ ] byte , v [ ] byte ) {
key := string ( k )
value := string ( v )
// for slices
if form [ key ] != nil {
form [ key ] = append ( form [ key ] , value )
} else {
form [ key ] = [ ] string { value }
}
} )
return errReadBody . With ( formBinder . Decode ( form , formObject ) )
}
/* Response */
// SetContentType sets the response writer's header key 'Content-Type' to a given value(s)
func ( ctx * Context ) SetContentType ( s string ) {
ctx . RequestCtx . Response . Header . Set ( contentType , s )
}
// SetHeader write to the response writer's header to a given key the given value(s)
2016-06-19 09:32:20 +02:00
//
// Note: If you want to send a multi-line string as header's value use: strings.TrimSpace first.
2016-06-14 07:45:40 +02:00
func ( ctx * Context ) SetHeader ( k string , v string ) {
2016-06-19 09:32:20 +02:00
//v = strings.TrimSpace(v)
2016-06-14 07:45:40 +02:00
ctx . RequestCtx . Response . Header . Set ( k , v )
}
// Redirect redirect sends a redirect response the client
// accepts 2 parameters string and an optional int
// first parameter is the url to redirect
// second parameter is the http status should send, default is 302 (StatusFound), you can set it to 301 (Permant redirect), if that's nessecery
func ( ctx * Context ) Redirect ( urlToRedirect string , statusHeader ... int ) {
2016-07-07 01:20:04 +02:00
httpStatus := StatusFound // a 'temporary-redirect-like' wich works better than for our purpose
2016-06-14 07:45:40 +02:00
if statusHeader != nil && len ( statusHeader ) > 0 && statusHeader [ 0 ] > 0 {
httpStatus = statusHeader [ 0 ]
}
ctx . RequestCtx . Redirect ( urlToRedirect , httpStatus )
2016-08-18 02:20:59 +02:00
/ * you can use one of these if you want to customize the redirection :
1.
u := fasthttp . AcquireURI ( )
ctx . URI ( ) . CopyTo ( u )
u . Update ( urlToRedirect )
ctx . SetHeader ( "Location" , string ( u . FullURI ( ) ) )
fasthttp . ReleaseURI ( u )
ctx . SetStatusCode ( httpStatus )
2.
ctx . SetHeader ( "Location" , urlToRedirect )
ctx . SetStatusCode ( httpStatus )
* /
2016-06-14 07:45:40 +02:00
ctx . StopExecution ( )
}
// RedirectTo does the same thing as Redirect but instead of receiving a uri or path it receives a route name
func ( ctx * Context ) RedirectTo ( routeName string , args ... interface { } ) {
s := ctx . framework . URL ( routeName , args ... )
if s != "" {
ctx . Redirect ( s , StatusFound )
}
}
// NotFound emits an error 404 to the client, using the custom http errors
// if no custom errors provided then it sends the default error message
func ( ctx * Context ) NotFound ( ) {
ctx . framework . EmitError ( StatusNotFound , ctx )
}
// Panic emits an error 500 to the client, using the custom http errors
// if no custom errors rpovided then it sends the default error message
func ( ctx * Context ) Panic ( ) {
ctx . framework . EmitError ( StatusInternalServerError , ctx )
}
// EmitError executes the custom error by the http status code passed to the function
func ( ctx * Context ) EmitError ( statusCode int ) {
ctx . framework . EmitError ( statusCode , ctx )
ctx . StopExecution ( )
}
// Write writes a string to the client, something like fmt.Printf but for the web
func ( ctx * Context ) Write ( format string , a ... interface { } ) {
//this doesn't work with gzip, so just write the []byte better |ctx.ResponseWriter.WriteString(fmt.Sprintf(format, a...))
ctx . RequestCtx . WriteString ( fmt . Sprintf ( format , a ... ) )
}
2016-08-14 04:44:36 +02:00
func ( ctx * Context ) clientAllowsGzip ( ) bool {
if h := ctx . RequestHeader ( acceptEncodingHeader ) ; h != "" {
for _ , v := range strings . Split ( h , ";" ) {
if strings . Contains ( v , "gzip" ) { // we do Contains because sometimes browsers has the q=, we don't use it atm. || strings.Contains(v,"deflate"){
return true
}
}
}
return false
}
Update to 4.0.0-alpha.3 - Response Engines, 'inject' the context.JSON/JSONP/Text/Data/Markdown/Render, Read HISTORY.md
## 4.0.0-alpha.2 -> 4.0.0-alpha.3
**New**
A **Response Engine** gives you the freedom to create/change the
render/response writer for
- `context.JSON`
- `context.JSONP`
- `context.XML`
- `context.Text`
- `context.Markdown`
- `context.Data`
- `context.Render("my_custom_type",mystructOrData{},
iris.RenderOptions{"gzip":false,"charset":"UTF-8"})`
- `context.MarkdownString`
- `iris.ResponseString(...)`
**Fix**
- https://github.com/kataras/iris/issues/294
**Small changes**
- `iris.Config.Charset`, before alpha.3 was `iris.Config.Rest.Charset` &
`iris.Config.Render.Template.Charset`, but you can override it at
runtime by passinth a map `iris.RenderOptions` on the `context.Render`
call .
- `iris.Config.IsDevelopment` , before alpha.1 was
`iris.Config.Render.Template.IsDevelopment`
**Websockets changes**
No need to import the `github.com/kataras/iris/websocket` to use the
`Connection` iteral, the websocket moved inside `kataras/iris` , now all
exported variables' names have the prefix of `Websocket`, so the old
`websocket.Connection` is now `iris.WebsocketConnection`.
Generally, no other changes on the 'frontend API', for response engines
examples and how you can register your own to add more features on
existing response engines or replace them, look
[here](https://github.com/iris-contrib/response).
**BAD SIDE**: E-Book is still pointing on the v3 release, but will be
updated soon.
2016-07-18 16:40:42 +02:00
// Gzip accepts bytes, which are compressed to gzip format and sent to the client
func ( ctx * Context ) Gzip ( b [ ] byte , status int ) {
2016-08-14 04:44:36 +02:00
ctx . RequestCtx . Response . Header . Add ( varyHeader , acceptEncodingHeader )
if ctx . clientAllowsGzip ( ) {
_ , err := fasthttp . WriteGzip ( ctx . RequestCtx . Response . BodyWriter ( ) , b )
if err == nil {
ctx . SetHeader ( contentEncodingHeader , "gzip" )
}
2016-07-25 13:45:12 +02:00
}
2016-06-14 07:45:40 +02:00
}
Update to 4.0.0-alpha.3 - Response Engines, 'inject' the context.JSON/JSONP/Text/Data/Markdown/Render, Read HISTORY.md
## 4.0.0-alpha.2 -> 4.0.0-alpha.3
**New**
A **Response Engine** gives you the freedom to create/change the
render/response writer for
- `context.JSON`
- `context.JSONP`
- `context.XML`
- `context.Text`
- `context.Markdown`
- `context.Data`
- `context.Render("my_custom_type",mystructOrData{},
iris.RenderOptions{"gzip":false,"charset":"UTF-8"})`
- `context.MarkdownString`
- `iris.ResponseString(...)`
**Fix**
- https://github.com/kataras/iris/issues/294
**Small changes**
- `iris.Config.Charset`, before alpha.3 was `iris.Config.Rest.Charset` &
`iris.Config.Render.Template.Charset`, but you can override it at
runtime by passinth a map `iris.RenderOptions` on the `context.Render`
call .
- `iris.Config.IsDevelopment` , before alpha.1 was
`iris.Config.Render.Template.IsDevelopment`
**Websockets changes**
No need to import the `github.com/kataras/iris/websocket` to use the
`Connection` iteral, the websocket moved inside `kataras/iris` , now all
exported variables' names have the prefix of `Websocket`, so the old
`websocket.Connection` is now `iris.WebsocketConnection`.
Generally, no other changes on the 'frontend API', for response engines
examples and how you can register your own to add more features on
existing response engines or replace them, look
[here](https://github.com/iris-contrib/response).
**BAD SIDE**: E-Book is still pointing on the v3 release, but will be
updated soon.
2016-07-18 16:40:42 +02:00
// RenderWithStatus builds up the response from the specified template or a response engine.
// Note: the options: "gzip" and "charset" are built'n support by Iris, so you can pass these on any template engine or response engine
2016-07-13 05:28:09 +02:00
func ( ctx * Context ) RenderWithStatus ( status int , name string , binding interface { } , options ... map [ string ] interface { } ) error {
2016-06-14 07:45:40 +02:00
ctx . SetStatusCode ( status )
2016-07-26 17:36:03 +02:00
if strings . IndexByte ( name , '.' ) > - 1 { //we have template
Update to 4.0.0-alpha.3 - Response Engines, 'inject' the context.JSON/JSONP/Text/Data/Markdown/Render, Read HISTORY.md
## 4.0.0-alpha.2 -> 4.0.0-alpha.3
**New**
A **Response Engine** gives you the freedom to create/change the
render/response writer for
- `context.JSON`
- `context.JSONP`
- `context.XML`
- `context.Text`
- `context.Markdown`
- `context.Data`
- `context.Render("my_custom_type",mystructOrData{},
iris.RenderOptions{"gzip":false,"charset":"UTF-8"})`
- `context.MarkdownString`
- `iris.ResponseString(...)`
**Fix**
- https://github.com/kataras/iris/issues/294
**Small changes**
- `iris.Config.Charset`, before alpha.3 was `iris.Config.Rest.Charset` &
`iris.Config.Render.Template.Charset`, but you can override it at
runtime by passinth a map `iris.RenderOptions` on the `context.Render`
call .
- `iris.Config.IsDevelopment` , before alpha.1 was
`iris.Config.Render.Template.IsDevelopment`
**Websockets changes**
No need to import the `github.com/kataras/iris/websocket` to use the
`Connection` iteral, the websocket moved inside `kataras/iris` , now all
exported variables' names have the prefix of `Websocket`, so the old
`websocket.Connection` is now `iris.WebsocketConnection`.
Generally, no other changes on the 'frontend API', for response engines
examples and how you can register your own to add more features on
existing response engines or replace them, look
[here](https://github.com/iris-contrib/response).
**BAD SIDE**: E-Book is still pointing on the v3 release, but will be
updated soon.
2016-07-18 16:40:42 +02:00
return ctx . framework . templates . getBy ( name ) . execute ( ctx , name , binding , options ... )
}
return ctx . framework . responses . getBy ( name ) . render ( ctx , binding , options ... )
2016-06-14 07:45:40 +02:00
}
Update to 4.0.0-alpha.3 - Response Engines, 'inject' the context.JSON/JSONP/Text/Data/Markdown/Render, Read HISTORY.md
## 4.0.0-alpha.2 -> 4.0.0-alpha.3
**New**
A **Response Engine** gives you the freedom to create/change the
render/response writer for
- `context.JSON`
- `context.JSONP`
- `context.XML`
- `context.Text`
- `context.Markdown`
- `context.Data`
- `context.Render("my_custom_type",mystructOrData{},
iris.RenderOptions{"gzip":false,"charset":"UTF-8"})`
- `context.MarkdownString`
- `iris.ResponseString(...)`
**Fix**
- https://github.com/kataras/iris/issues/294
**Small changes**
- `iris.Config.Charset`, before alpha.3 was `iris.Config.Rest.Charset` &
`iris.Config.Render.Template.Charset`, but you can override it at
runtime by passinth a map `iris.RenderOptions` on the `context.Render`
call .
- `iris.Config.IsDevelopment` , before alpha.1 was
`iris.Config.Render.Template.IsDevelopment`
**Websockets changes**
No need to import the `github.com/kataras/iris/websocket` to use the
`Connection` iteral, the websocket moved inside `kataras/iris` , now all
exported variables' names have the prefix of `Websocket`, so the old
`websocket.Connection` is now `iris.WebsocketConnection`.
Generally, no other changes on the 'frontend API', for response engines
examples and how you can register your own to add more features on
existing response engines or replace them, look
[here](https://github.com/iris-contrib/response).
**BAD SIDE**: E-Book is still pointing on the v3 release, but will be
updated soon.
2016-07-18 16:40:42 +02:00
// Render same as .RenderWithStatus but with status to iris.StatusOK (200) if no previous status exists
// builds up the response from the specified template or a response engine.
// Note: the options: "gzip" and "charset" are built'n support by Iris, so you can pass these on any template engine or response engine
2016-07-13 05:28:09 +02:00
func ( ctx * Context ) Render ( name string , binding interface { } , options ... map [ string ] interface { } ) error {
2016-07-06 20:24:34 +02:00
errCode := ctx . RequestCtx . Response . StatusCode ( )
if errCode <= 0 {
errCode = StatusOK
}
2016-07-13 05:28:09 +02:00
return ctx . RenderWithStatus ( errCode , name , binding , options ... )
2016-06-14 07:45:40 +02:00
}
// MustRender same as .Render but returns 500 internal server http status (error) if rendering fail
Update to 4.0.0-alpha.3 - Response Engines, 'inject' the context.JSON/JSONP/Text/Data/Markdown/Render, Read HISTORY.md
## 4.0.0-alpha.2 -> 4.0.0-alpha.3
**New**
A **Response Engine** gives you the freedom to create/change the
render/response writer for
- `context.JSON`
- `context.JSONP`
- `context.XML`
- `context.Text`
- `context.Markdown`
- `context.Data`
- `context.Render("my_custom_type",mystructOrData{},
iris.RenderOptions{"gzip":false,"charset":"UTF-8"})`
- `context.MarkdownString`
- `iris.ResponseString(...)`
**Fix**
- https://github.com/kataras/iris/issues/294
**Small changes**
- `iris.Config.Charset`, before alpha.3 was `iris.Config.Rest.Charset` &
`iris.Config.Render.Template.Charset`, but you can override it at
runtime by passinth a map `iris.RenderOptions` on the `context.Render`
call .
- `iris.Config.IsDevelopment` , before alpha.1 was
`iris.Config.Render.Template.IsDevelopment`
**Websockets changes**
No need to import the `github.com/kataras/iris/websocket` to use the
`Connection` iteral, the websocket moved inside `kataras/iris` , now all
exported variables' names have the prefix of `Websocket`, so the old
`websocket.Connection` is now `iris.WebsocketConnection`.
Generally, no other changes on the 'frontend API', for response engines
examples and how you can register your own to add more features on
existing response engines or replace them, look
[here](https://github.com/iris-contrib/response).
**BAD SIDE**: E-Book is still pointing on the v3 release, but will be
updated soon.
2016-07-18 16:40:42 +02:00
// builds up the response from the specified template or a response engine.
// Note: the options: "gzip" and "charset" are built'n support by Iris, so you can pass these on any template engine or response engine
2016-07-13 05:28:09 +02:00
func ( ctx * Context ) MustRender ( name string , binding interface { } , options ... map [ string ] interface { } ) {
if err := ctx . Render ( name , binding , options ... ) ; err != nil {
2016-06-14 07:45:40 +02:00
ctx . Panic ( )
2016-06-28 17:06:52 +02:00
ctx . framework . Logger . Dangerf ( "MustRender panics for client with IP: %s On template: %s.Trace: %s\n" , ctx . RemoteAddr ( ) , name , err )
2016-06-14 07:45:40 +02:00
}
}
// TemplateString accepts a template filename, its context data and returns the result of the parsed template (string)
// if any error returns empty string
2016-07-13 05:28:09 +02:00
func ( ctx * Context ) TemplateString ( name string , binding interface { } , options ... map [ string ] interface { } ) string {
return ctx . framework . TemplateString ( name , binding , options ... )
2016-06-14 07:45:40 +02:00
}
Update to 4.0.0-alpha.3 - Response Engines, 'inject' the context.JSON/JSONP/Text/Data/Markdown/Render, Read HISTORY.md
## 4.0.0-alpha.2 -> 4.0.0-alpha.3
**New**
A **Response Engine** gives you the freedom to create/change the
render/response writer for
- `context.JSON`
- `context.JSONP`
- `context.XML`
- `context.Text`
- `context.Markdown`
- `context.Data`
- `context.Render("my_custom_type",mystructOrData{},
iris.RenderOptions{"gzip":false,"charset":"UTF-8"})`
- `context.MarkdownString`
- `iris.ResponseString(...)`
**Fix**
- https://github.com/kataras/iris/issues/294
**Small changes**
- `iris.Config.Charset`, before alpha.3 was `iris.Config.Rest.Charset` &
`iris.Config.Render.Template.Charset`, but you can override it at
runtime by passinth a map `iris.RenderOptions` on the `context.Render`
call .
- `iris.Config.IsDevelopment` , before alpha.1 was
`iris.Config.Render.Template.IsDevelopment`
**Websockets changes**
No need to import the `github.com/kataras/iris/websocket` to use the
`Connection` iteral, the websocket moved inside `kataras/iris` , now all
exported variables' names have the prefix of `Websocket`, so the old
`websocket.Connection` is now `iris.WebsocketConnection`.
Generally, no other changes on the 'frontend API', for response engines
examples and how you can register your own to add more features on
existing response engines or replace them, look
[here](https://github.com/iris-contrib/response).
**BAD SIDE**: E-Book is still pointing on the v3 release, but will be
updated soon.
2016-07-18 16:40:42 +02:00
// HTML writes html string with a http status
func ( ctx * Context ) HTML ( status int , htmlContents string ) {
if err := ctx . RenderWithStatus ( status , contentHTML , htmlContents ) ; err != nil {
2016-07-30 16:24:12 +02:00
// if no response engine found for text/html
Update to 4.0.0-alpha.3 - Response Engines, 'inject' the context.JSON/JSONP/Text/Data/Markdown/Render, Read HISTORY.md
## 4.0.0-alpha.2 -> 4.0.0-alpha.3
**New**
A **Response Engine** gives you the freedom to create/change the
render/response writer for
- `context.JSON`
- `context.JSONP`
- `context.XML`
- `context.Text`
- `context.Markdown`
- `context.Data`
- `context.Render("my_custom_type",mystructOrData{},
iris.RenderOptions{"gzip":false,"charset":"UTF-8"})`
- `context.MarkdownString`
- `iris.ResponseString(...)`
**Fix**
- https://github.com/kataras/iris/issues/294
**Small changes**
- `iris.Config.Charset`, before alpha.3 was `iris.Config.Rest.Charset` &
`iris.Config.Render.Template.Charset`, but you can override it at
runtime by passinth a map `iris.RenderOptions` on the `context.Render`
call .
- `iris.Config.IsDevelopment` , before alpha.1 was
`iris.Config.Render.Template.IsDevelopment`
**Websockets changes**
No need to import the `github.com/kataras/iris/websocket` to use the
`Connection` iteral, the websocket moved inside `kataras/iris` , now all
exported variables' names have the prefix of `Websocket`, so the old
`websocket.Connection` is now `iris.WebsocketConnection`.
Generally, no other changes on the 'frontend API', for response engines
examples and how you can register your own to add more features on
existing response engines or replace them, look
[here](https://github.com/iris-contrib/response).
**BAD SIDE**: E-Book is still pointing on the v3 release, but will be
updated soon.
2016-07-18 16:40:42 +02:00
ctx . SetContentType ( contentHTML + "; charset=" + ctx . framework . Config . Charset )
ctx . RequestCtx . SetStatusCode ( status )
ctx . RequestCtx . WriteString ( htmlContents )
}
}
// Data writes out the raw bytes as binary data.
func ( ctx * Context ) Data ( status int , v [ ] byte ) error {
return ctx . RenderWithStatus ( status , contentBinary , v )
}
2016-06-14 07:45:40 +02:00
// JSON marshals the given interface object and writes the JSON response.
func ( ctx * Context ) JSON ( status int , v interface { } ) error {
Update to 4.0.0-alpha.3 - Response Engines, 'inject' the context.JSON/JSONP/Text/Data/Markdown/Render, Read HISTORY.md
## 4.0.0-alpha.2 -> 4.0.0-alpha.3
**New**
A **Response Engine** gives you the freedom to create/change the
render/response writer for
- `context.JSON`
- `context.JSONP`
- `context.XML`
- `context.Text`
- `context.Markdown`
- `context.Data`
- `context.Render("my_custom_type",mystructOrData{},
iris.RenderOptions{"gzip":false,"charset":"UTF-8"})`
- `context.MarkdownString`
- `iris.ResponseString(...)`
**Fix**
- https://github.com/kataras/iris/issues/294
**Small changes**
- `iris.Config.Charset`, before alpha.3 was `iris.Config.Rest.Charset` &
`iris.Config.Render.Template.Charset`, but you can override it at
runtime by passinth a map `iris.RenderOptions` on the `context.Render`
call .
- `iris.Config.IsDevelopment` , before alpha.1 was
`iris.Config.Render.Template.IsDevelopment`
**Websockets changes**
No need to import the `github.com/kataras/iris/websocket` to use the
`Connection` iteral, the websocket moved inside `kataras/iris` , now all
exported variables' names have the prefix of `Websocket`, so the old
`websocket.Connection` is now `iris.WebsocketConnection`.
Generally, no other changes on the 'frontend API', for response engines
examples and how you can register your own to add more features on
existing response engines or replace them, look
[here](https://github.com/iris-contrib/response).
**BAD SIDE**: E-Book is still pointing on the v3 release, but will be
updated soon.
2016-07-18 16:40:42 +02:00
return ctx . RenderWithStatus ( status , contentJSON , v )
2016-06-14 07:45:40 +02:00
}
// JSONP marshals the given interface object and writes the JSON response.
func ( ctx * Context ) JSONP ( status int , callback string , v interface { } ) error {
Update to 4.0.0-alpha.3 - Response Engines, 'inject' the context.JSON/JSONP/Text/Data/Markdown/Render, Read HISTORY.md
## 4.0.0-alpha.2 -> 4.0.0-alpha.3
**New**
A **Response Engine** gives you the freedom to create/change the
render/response writer for
- `context.JSON`
- `context.JSONP`
- `context.XML`
- `context.Text`
- `context.Markdown`
- `context.Data`
- `context.Render("my_custom_type",mystructOrData{},
iris.RenderOptions{"gzip":false,"charset":"UTF-8"})`
- `context.MarkdownString`
- `iris.ResponseString(...)`
**Fix**
- https://github.com/kataras/iris/issues/294
**Small changes**
- `iris.Config.Charset`, before alpha.3 was `iris.Config.Rest.Charset` &
`iris.Config.Render.Template.Charset`, but you can override it at
runtime by passinth a map `iris.RenderOptions` on the `context.Render`
call .
- `iris.Config.IsDevelopment` , before alpha.1 was
`iris.Config.Render.Template.IsDevelopment`
**Websockets changes**
No need to import the `github.com/kataras/iris/websocket` to use the
`Connection` iteral, the websocket moved inside `kataras/iris` , now all
exported variables' names have the prefix of `Websocket`, so the old
`websocket.Connection` is now `iris.WebsocketConnection`.
Generally, no other changes on the 'frontend API', for response engines
examples and how you can register your own to add more features on
existing response engines or replace them, look
[here](https://github.com/iris-contrib/response).
**BAD SIDE**: E-Book is still pointing on the v3 release, but will be
updated soon.
2016-07-18 16:40:42 +02:00
return ctx . RenderWithStatus ( status , contentJSONP , v , map [ string ] interface { } { "callback" : callback } )
2016-06-14 07:45:40 +02:00
}
// Text writes out a string as plain text.
func ( ctx * Context ) Text ( status int , v string ) error {
Update to 4.0.0-alpha.3 - Response Engines, 'inject' the context.JSON/JSONP/Text/Data/Markdown/Render, Read HISTORY.md
## 4.0.0-alpha.2 -> 4.0.0-alpha.3
**New**
A **Response Engine** gives you the freedom to create/change the
render/response writer for
- `context.JSON`
- `context.JSONP`
- `context.XML`
- `context.Text`
- `context.Markdown`
- `context.Data`
- `context.Render("my_custom_type",mystructOrData{},
iris.RenderOptions{"gzip":false,"charset":"UTF-8"})`
- `context.MarkdownString`
- `iris.ResponseString(...)`
**Fix**
- https://github.com/kataras/iris/issues/294
**Small changes**
- `iris.Config.Charset`, before alpha.3 was `iris.Config.Rest.Charset` &
`iris.Config.Render.Template.Charset`, but you can override it at
runtime by passinth a map `iris.RenderOptions` on the `context.Render`
call .
- `iris.Config.IsDevelopment` , before alpha.1 was
`iris.Config.Render.Template.IsDevelopment`
**Websockets changes**
No need to import the `github.com/kataras/iris/websocket` to use the
`Connection` iteral, the websocket moved inside `kataras/iris` , now all
exported variables' names have the prefix of `Websocket`, so the old
`websocket.Connection` is now `iris.WebsocketConnection`.
Generally, no other changes on the 'frontend API', for response engines
examples and how you can register your own to add more features on
existing response engines or replace them, look
[here](https://github.com/iris-contrib/response).
**BAD SIDE**: E-Book is still pointing on the v3 release, but will be
updated soon.
2016-07-18 16:40:42 +02:00
return ctx . RenderWithStatus ( status , contentText , v )
2016-06-14 07:45:40 +02:00
}
// XML marshals the given interface object and writes the XML response.
func ( ctx * Context ) XML ( status int , v interface { } ) error {
Update to 4.0.0-alpha.3 - Response Engines, 'inject' the context.JSON/JSONP/Text/Data/Markdown/Render, Read HISTORY.md
## 4.0.0-alpha.2 -> 4.0.0-alpha.3
**New**
A **Response Engine** gives you the freedom to create/change the
render/response writer for
- `context.JSON`
- `context.JSONP`
- `context.XML`
- `context.Text`
- `context.Markdown`
- `context.Data`
- `context.Render("my_custom_type",mystructOrData{},
iris.RenderOptions{"gzip":false,"charset":"UTF-8"})`
- `context.MarkdownString`
- `iris.ResponseString(...)`
**Fix**
- https://github.com/kataras/iris/issues/294
**Small changes**
- `iris.Config.Charset`, before alpha.3 was `iris.Config.Rest.Charset` &
`iris.Config.Render.Template.Charset`, but you can override it at
runtime by passinth a map `iris.RenderOptions` on the `context.Render`
call .
- `iris.Config.IsDevelopment` , before alpha.1 was
`iris.Config.Render.Template.IsDevelopment`
**Websockets changes**
No need to import the `github.com/kataras/iris/websocket` to use the
`Connection` iteral, the websocket moved inside `kataras/iris` , now all
exported variables' names have the prefix of `Websocket`, so the old
`websocket.Connection` is now `iris.WebsocketConnection`.
Generally, no other changes on the 'frontend API', for response engines
examples and how you can register your own to add more features on
existing response engines or replace them, look
[here](https://github.com/iris-contrib/response).
**BAD SIDE**: E-Book is still pointing on the v3 release, but will be
updated soon.
2016-07-18 16:40:42 +02:00
return ctx . RenderWithStatus ( status , contentXML , v )
2016-06-14 07:45:40 +02:00
}
// MarkdownString parses the (dynamic) markdown string and returns the converted html string
func ( ctx * Context ) MarkdownString ( markdownText string ) string {
Update to 4.0.0-alpha.3 - Response Engines, 'inject' the context.JSON/JSONP/Text/Data/Markdown/Render, Read HISTORY.md
## 4.0.0-alpha.2 -> 4.0.0-alpha.3
**New**
A **Response Engine** gives you the freedom to create/change the
render/response writer for
- `context.JSON`
- `context.JSONP`
- `context.XML`
- `context.Text`
- `context.Markdown`
- `context.Data`
- `context.Render("my_custom_type",mystructOrData{},
iris.RenderOptions{"gzip":false,"charset":"UTF-8"})`
- `context.MarkdownString`
- `iris.ResponseString(...)`
**Fix**
- https://github.com/kataras/iris/issues/294
**Small changes**
- `iris.Config.Charset`, before alpha.3 was `iris.Config.Rest.Charset` &
`iris.Config.Render.Template.Charset`, but you can override it at
runtime by passinth a map `iris.RenderOptions` on the `context.Render`
call .
- `iris.Config.IsDevelopment` , before alpha.1 was
`iris.Config.Render.Template.IsDevelopment`
**Websockets changes**
No need to import the `github.com/kataras/iris/websocket` to use the
`Connection` iteral, the websocket moved inside `kataras/iris` , now all
exported variables' names have the prefix of `Websocket`, so the old
`websocket.Connection` is now `iris.WebsocketConnection`.
Generally, no other changes on the 'frontend API', for response engines
examples and how you can register your own to add more features on
existing response engines or replace them, look
[here](https://github.com/iris-contrib/response).
**BAD SIDE**: E-Book is still pointing on the v3 release, but will be
updated soon.
2016-07-18 16:40:42 +02:00
return ctx . framework . ResponseString ( contentMarkdown , markdownText )
2016-06-14 07:45:40 +02:00
}
// Markdown parses and renders to the client a particular (dynamic) markdown string
// accepts two parameters
// first is the http status code
// second is the markdown string
func ( ctx * Context ) Markdown ( status int , markdown string ) {
ctx . HTML ( status , ctx . MarkdownString ( markdown ) )
}
// ServeContent serves content, headers are autoset
2016-08-17 19:16:23 +02:00
// receives three parameters, it's low-level function, instead you can use .ServeFile(string,bool)/SendFile(string,string)
2016-06-14 07:45:40 +02:00
//
// You can define your own "Content-Type" header also, after this function call
2016-08-17 19:16:23 +02:00
// Doesn't implements resuming (by range), use ctx.SendFile instead
2016-06-14 07:45:40 +02:00
func ( ctx * Context ) ServeContent ( content io . ReadSeeker , filename string , modtime time . Time , gzipCompression bool ) error {
if t , err := time . Parse ( config . TimeFormat , ctx . RequestHeader ( ifModifiedSince ) ) ; err == nil && modtime . Before ( t . Add ( 1 * time . Second ) ) {
ctx . RequestCtx . Response . Header . Del ( contentType )
ctx . RequestCtx . Response . Header . Del ( contentLength )
ctx . RequestCtx . SetStatusCode ( StatusNotModified )
return nil
}
ctx . RequestCtx . Response . Header . Set ( contentType , utils . TypeByExtension ( filename ) )
ctx . RequestCtx . Response . Header . Set ( lastModified , modtime . UTC ( ) . Format ( config . TimeFormat ) )
ctx . RequestCtx . SetStatusCode ( StatusOK )
var out io . Writer
2016-08-14 04:44:36 +02:00
if gzipCompression && ctx . clientAllowsGzip ( ) {
ctx . RequestCtx . Response . Header . Add ( varyHeader , acceptEncodingHeader )
ctx . SetHeader ( contentEncodingHeader , "gzip" )
2016-06-14 07:45:40 +02:00
gzipWriter := gzipWriterPool . Get ( ) . ( * gzip . Writer )
gzipWriter . Reset ( ctx . RequestCtx . Response . BodyWriter ( ) )
defer gzipWriter . Close ( )
defer gzipWriterPool . Put ( gzipWriter )
out = gzipWriter
} else {
out = ctx . RequestCtx . Response . BodyWriter ( )
}
_ , err := io . Copy ( out , content )
return errServeContent . With ( err )
}
// ServeFile serves a view file, to send a file ( zip for example) to the client you should use the SendFile(serverfilename,clientfilename)
// receives two parameters
// filename/path (string)
// gzipCompression (bool)
//
// You can define your own "Content-Type" header also, after this function call
2016-08-17 19:16:23 +02:00
// This function doesn't implement resuming (by range), use ctx.SendFile/fasthttp.ServeFileUncompressed(ctx.RequestCtx,path)/fasthttpServeFile(ctx.RequestCtx,path) instead
//
// Use it when you want to serve css/js/... files to the client, for bigger files and 'force-download' use the SendFile
2016-06-14 07:45:40 +02:00
func ( ctx * Context ) ServeFile ( filename string , gzipCompression bool ) error {
f , err := os . Open ( filename )
if err != nil {
return fmt . Errorf ( "%d" , 404 )
}
defer f . Close ( )
fi , _ := f . Stat ( )
if fi . IsDir ( ) {
filename = path . Join ( filename , "index.html" )
f , err = os . Open ( filename )
if err != nil {
return fmt . Errorf ( "%d" , 404 )
}
fi , _ = f . Stat ( )
}
return ctx . ServeContent ( f , fi . Name ( ) , fi . ModTime ( ) , gzipCompression )
}
// SendFile sends file for force-download to the client
//
2016-08-17 19:16:23 +02:00
// Use this instead of ServeFile to 'force-download' bigger files to the client
func ( ctx * Context ) SendFile ( filename string , destinationName string ) {
ctx . RequestCtx . SendFile ( filename )
2016-06-14 07:45:40 +02:00
ctx . RequestCtx . Response . Header . Set ( contentDisposition , "attachment;filename=" + destinationName )
}
// Stream same as StreamWriter
func ( ctx * Context ) Stream ( cb func ( writer * bufio . Writer ) ) {
ctx . StreamWriter ( cb )
}
// StreamWriter registers the given stream writer for populating
// response body.
//
//
// This function may be used in the following cases:
//
// * if response body is too big (more than 10MB).
// * if response body is streamed from slow external sources.
// * if response body must be streamed to the client in chunks.
// (aka `http server push`).
func ( ctx * Context ) StreamWriter ( cb func ( writer * bufio . Writer ) ) {
ctx . RequestCtx . SetBodyStreamWriter ( cb )
}
// StreamReader sets response body stream and, optionally body size.
//
// If bodySize is >= 0, then the bodyStream must provide exactly bodySize bytes
// before returning io.EOF.
//
// If bodySize < 0, then bodyStream is read until io.EOF.
//
// bodyStream.Close() is called after finishing reading all body data
// if it implements io.Closer.
//
// See also StreamReader.
func ( ctx * Context ) StreamReader ( bodyStream io . Reader , bodySize int ) {
ctx . RequestCtx . Response . SetBodyStream ( bodyStream , bodySize )
}
/* Storage */
// Get returns the user's value from a key
// if doesn't exists returns nil
func ( ctx * Context ) Get ( key string ) interface { } {
return ctx . RequestCtx . UserValue ( key )
}
// GetFmt returns a value which has this format: func(format string, args ...interface{}) string
// if doesn't exists returns nil
func ( ctx * Context ) GetFmt ( key string ) func ( format string , args ... interface { } ) string {
if v , ok := ctx . Get ( key ) . ( func ( format string , args ... interface { } ) string ) ; ok {
return v
}
return func ( format string , args ... interface { } ) string { return "" }
}
// GetString same as Get but returns the value as string
// if nothing founds returns empty string ""
func ( ctx * Context ) GetString ( key string ) string {
if v , ok := ctx . Get ( key ) . ( string ) ; ok {
return v
}
return ""
}
// GetInt same as Get but returns the value as int
// if nothing founds returns -1
func ( ctx * Context ) GetInt ( key string ) int {
if v , ok := ctx . Get ( key ) . ( int ) ; ok {
return v
}
return - 1
}
// Set sets a value to a key in the values map
func ( ctx * Context ) Set ( key string , value interface { } ) {
ctx . RequestCtx . SetUserValue ( key , value )
}
2016-07-07 00:25:50 +02:00
// VisitAllCookies takes a visitor which loops on each (request's) cookie key and value
//
// Note: the method ctx.Request.Header.VisitAllCookie by fasthttp, has a strange bug which I cannot solve at the moment.
// This is the reason which this function exists and should be used instead of fasthttp's built'n.
func ( ctx * Context ) VisitAllCookies ( visitor func ( key string , value string ) ) {
// strange bug, this doesnt works also: cookieHeaderContent := ctx.Request.Header.Peek("Cookie")/User-Agent tested also
headerbody := string ( ctx . Request . Header . Header ( ) )
headerlines := strings . Split ( headerbody , "\n" )
for _ , s := range headerlines {
if len ( s ) > cookieHeaderIDLen {
if s [ 0 : cookieHeaderIDLen ] == cookieHeaderID {
contents := s [ cookieHeaderIDLen : ]
values := strings . Split ( contents , "; " )
for _ , s := range values {
keyvalue := strings . SplitN ( s , "=" , 2 )
visitor ( keyvalue [ 0 ] , keyvalue [ 1 ] )
}
}
}
}
}
2016-06-14 07:45:40 +02:00
// GetCookie returns cookie's value by it's name
// returns empty string if nothing was found
func ( ctx * Context ) GetCookie ( name string ) ( val string ) {
bcookie := ctx . RequestCtx . Request . Header . Cookie ( name )
if bcookie != nil {
val = string ( bcookie )
}
return
}
// SetCookie adds a cookie
func ( ctx * Context ) SetCookie ( cookie * fasthttp . Cookie ) {
ctx . RequestCtx . Response . Header . SetCookie ( cookie )
}
// SetCookieKV adds a cookie, receives just a key(string) and a value(string)
func ( ctx * Context ) SetCookieKV ( key , value string ) {
2016-08-17 23:28:03 +02:00
c := fasthttp . AcquireCookie ( )
// c := &fasthttp.Cookie{}
2016-06-14 07:45:40 +02:00
c . SetKey ( key )
c . SetValue ( value )
c . SetHTTPOnly ( true )
c . SetExpire ( time . Now ( ) . Add ( time . Duration ( 120 ) * time . Minute ) )
ctx . SetCookie ( c )
2016-08-17 23:28:03 +02:00
fasthttp . ReleaseCookie ( c )
2016-06-14 07:45:40 +02:00
}
// RemoveCookie deletes a cookie by it's name/key
func ( ctx * Context ) RemoveCookie ( name string ) {
2016-07-08 22:11:39 +02:00
ctx . Response . Header . DelCookie ( name )
2016-08-17 23:28:03 +02:00
cookie := fasthttp . AcquireCookie ( )
//cookie := &fasthttp.Cookie{}
2016-07-08 21:33:41 +02:00
cookie . SetKey ( name )
cookie . SetValue ( "" )
cookie . SetPath ( "/" )
cookie . SetHTTPOnly ( true )
2016-07-08 22:11:39 +02:00
exp := time . Now ( ) . Add ( - time . Duration ( 1 ) * time . Minute ) //RFC says 1 second, but let's do it 1 minute to make sure is working...
2016-07-08 21:33:41 +02:00
cookie . SetExpire ( exp )
2016-08-16 18:23:12 +02:00
ctx . SetCookie ( cookie )
2016-08-17 23:28:03 +02:00
fasthttp . ReleaseCookie ( cookie )
2016-07-08 22:11:57 +02:00
// delete request's cookie also, which is temporarly available
2016-07-08 22:11:39 +02:00
ctx . Request . Header . DelCookie ( name )
2016-06-14 07:45:40 +02:00
}
2016-07-07 00:25:50 +02:00
// GetFlashes returns all the flash messages for available for this request
func ( ctx * Context ) GetFlashes ( ) map [ string ] string {
// if already taken at least one time, this will be filled
if messages := ctx . Get ( flashMessagesStoreContextKey ) ; messages != nil {
if m , isMap := messages . ( map [ string ] string ) ; isMap {
return m
}
} else {
flashMessageFound := false
// else first time, get all flash cookie keys(the prefix will tell us which is a flash message), and after get all one-by-one using the GetFlash.
flashMessageCookiePrefixLen := len ( flashMessageCookiePrefix )
ctx . VisitAllCookies ( func ( key string , value string ) {
if len ( key ) > flashMessageCookiePrefixLen {
if key [ 0 : flashMessageCookiePrefixLen ] == flashMessageCookiePrefix {
unprefixedKey := key [ flashMessageCookiePrefixLen : ]
_ , err := ctx . GetFlash ( unprefixedKey ) // this func will add to the list (flashMessagesStoreContextKey) also
if err == nil {
flashMessageFound = true
}
}
}
} )
// if we found at least one flash message then re-execute this function to return the list
if flashMessageFound {
return ctx . GetFlashes ( )
}
}
return nil
}
func ( ctx * Context ) decodeFlashCookie ( key string ) ( string , string ) {
cookieKey := flashMessageCookiePrefix + key
cookieValue := string ( ctx . RequestCtx . Request . Header . Cookie ( cookieKey ) )
if cookieValue != "" {
v , e := base64 . URLEncoding . DecodeString ( cookieValue )
if e == nil {
return cookieKey , string ( v )
}
}
return "" , ""
}
2016-06-14 07:45:40 +02:00
// GetFlash get a flash message by it's key
2016-07-02 15:02:33 +02:00
// returns the value as string and an error
//
// if the cookie doesn't exists the string is empty and the error is filled
2016-07-02 15:16:42 +02:00
// after the request's life the value is removed
2016-07-07 00:25:50 +02:00
func ( ctx * Context ) GetFlash ( key string ) ( string , error ) {
2016-07-02 15:02:33 +02:00
// first check if flash exists from this request's lifetime, if yes return that else continue to get the cookie
storeExists := false
2016-07-07 00:25:50 +02:00
2016-07-02 15:02:33 +02:00
if messages := ctx . Get ( flashMessagesStoreContextKey ) ; messages != nil {
m , isMap := messages . ( map [ string ] string )
if ! isMap {
2016-07-07 00:25:50 +02:00
return "" , fmt . Errorf ( "Flash store is not a map[string]string. This suppose will never happen, please report this bug." )
2016-07-02 15:02:33 +02:00
}
storeExists = true // in order to skip the check later
for k , v := range m {
if k == key && v != "" {
return v , nil
}
}
2016-06-14 07:45:40 +02:00
}
2016-07-07 00:25:50 +02:00
cookieKey , cookieValue := ctx . decodeFlashCookie ( key )
2016-06-14 07:45:40 +02:00
if cookieValue == "" {
2016-07-07 00:25:50 +02:00
return "" , errFlashNotFound . Return ( )
}
// store this flash message to the lifetime request's local storage,
// I choose this method because no need to store it if not used at all
if storeExists {
ctx . Get ( flashMessagesStoreContextKey ) . ( map [ string ] string ) [ key ] = cookieValue
2016-06-14 07:45:40 +02:00
} else {
2016-07-07 00:25:50 +02:00
flashStoreMap := make ( map [ string ] string )
flashStoreMap [ key ] = cookieValue
ctx . Set ( flashMessagesStoreContextKey , flashStoreMap )
2016-06-14 07:45:40 +02:00
}
2016-07-07 00:25:50 +02:00
//remove the real cookie, no need to have that, we stored it on lifetime request
ctx . RemoveCookie ( cookieKey )
return cookieValue , nil
//it should'b be removed until the next reload, so we don't do that: ctx.Request.Header.SetCookie(key, "")
2016-06-14 07:45:40 +02:00
}
// SetFlash sets a flash message, accepts 2 parameters the key(string) and the value(string)
2016-07-02 15:02:33 +02:00
// the value will be available on the NEXT request
2016-06-14 07:45:40 +02:00
func ( ctx * Context ) SetFlash ( key string , value string ) {
2016-08-16 18:23:12 +02:00
cKey := flashMessageCookiePrefix + key
cValue := base64 . URLEncoding . EncodeToString ( [ ] byte ( value ) )
2016-08-17 23:28:03 +02:00
c := fasthttp . AcquireCookie ( )
2016-08-16 18:23:12 +02:00
c . SetKey ( cKey )
c . SetValue ( cValue )
2016-06-14 07:45:40 +02:00
c . SetPath ( "/" )
c . SetHTTPOnly ( true )
ctx . RequestCtx . Response . Header . SetCookie ( c )
fasthttp . ReleaseCookie ( c )
2016-08-17 23:28:03 +02:00
// if any bug on the future: this works, and the above:
2016-08-16 18:23:12 +02:00
//ctx.RequestCtx.Request.Header.SetCookie(cKey, cValue)
//ctx.RequestCtx.Response.Header.Add("Set-Cookie", cKey+"="+cValue+"; Path:/; HttpOnly")
//
2016-08-17 23:28:03 +02:00
/ * c := & fasthttp . Cookie { }
2016-08-16 18:23:12 +02:00
c . SetKey ( cKey )
c . SetValue ( cValue )
c . SetPath ( "/" )
c . SetHTTPOnly ( true )
2016-08-17 23:28:03 +02:00
ctx . SetCookie ( c ) * /
2016-08-16 18:23:12 +02:00
2016-06-14 07:45:40 +02:00
}
:rainbow: sessions were re-written, update to 4.0.0-alpha.2, read HISTORY.md
**Sessions were re-written **
- Developers can use more than one 'session database', at the same time,
to store the sessions
- Easy to develop a custom session database (only two functions are
required (Load & Update)), [learn
more](https://github.com/iris-contrib/sessiondb/blob/master/redis/database.go)
- Session databases are located
[here](https://github.com/iris-contrib/sessiondb), contributions are
welcome
- The only frontend deleted 'thing' is the: **config.Sessions.Provider**
- No need to register a database, the sessions works out-of-the-box
- No frontend/API changes except the
`context.Session().Set/Delete/Clear`, they doesn't return errors
anymore, btw they (errors) were always nil :)
- Examples (master branch) were updated.
```sh
$ go get github.com/iris-contrib/sessiondb/$DATABASE
```
```go
db := $DATABASE.New(configurationHere{})
iris.UseSessionDB(db)
```
> Note: Book is not updated yet, examples are up-to-date as always.
2016-07-15 19:50:36 +02:00
// Session returns the current session
2016-07-26 18:36:31 +02:00
func ( ctx * Context ) Session ( ) context . Session {
:rainbow: sessions were re-written, update to 4.0.0-alpha.2, read HISTORY.md
**Sessions were re-written **
- Developers can use more than one 'session database', at the same time,
to store the sessions
- Easy to develop a custom session database (only two functions are
required (Load & Update)), [learn
more](https://github.com/iris-contrib/sessiondb/blob/master/redis/database.go)
- Session databases are located
[here](https://github.com/iris-contrib/sessiondb), contributions are
welcome
- The only frontend deleted 'thing' is the: **config.Sessions.Provider**
- No need to register a database, the sessions works out-of-the-box
- No frontend/API changes except the
`context.Session().Set/Delete/Clear`, they doesn't return errors
anymore, btw they (errors) were always nil :)
- Examples (master branch) were updated.
```sh
$ go get github.com/iris-contrib/sessiondb/$DATABASE
```
```go
db := $DATABASE.New(configurationHere{})
iris.UseSessionDB(db)
```
> Note: Book is not updated yet, examples are up-to-date as always.
2016-07-15 19:50:36 +02:00
if ctx . framework . sessions == nil { // this should never return nil but FOR ANY CASE, on future changes.
2016-06-14 07:45:40 +02:00
return nil
}
:rainbow: sessions were re-written, update to 4.0.0-alpha.2, read HISTORY.md
**Sessions were re-written **
- Developers can use more than one 'session database', at the same time,
to store the sessions
- Easy to develop a custom session database (only two functions are
required (Load & Update)), [learn
more](https://github.com/iris-contrib/sessiondb/blob/master/redis/database.go)
- Session databases are located
[here](https://github.com/iris-contrib/sessiondb), contributions are
welcome
- The only frontend deleted 'thing' is the: **config.Sessions.Provider**
- No need to register a database, the sessions works out-of-the-box
- No frontend/API changes except the
`context.Session().Set/Delete/Clear`, they doesn't return errors
anymore, btw they (errors) were always nil :)
- Examples (master branch) were updated.
```sh
$ go get github.com/iris-contrib/sessiondb/$DATABASE
```
```go
db := $DATABASE.New(configurationHere{})
iris.UseSessionDB(db)
```
> Note: Book is not updated yet, examples are up-to-date as always.
2016-07-15 19:50:36 +02:00
if ctx . session == nil {
ctx . session = ctx . framework . sessions . start ( ctx )
2016-06-14 07:45:40 +02:00
}
:rainbow: sessions were re-written, update to 4.0.0-alpha.2, read HISTORY.md
**Sessions were re-written **
- Developers can use more than one 'session database', at the same time,
to store the sessions
- Easy to develop a custom session database (only two functions are
required (Load & Update)), [learn
more](https://github.com/iris-contrib/sessiondb/blob/master/redis/database.go)
- Session databases are located
[here](https://github.com/iris-contrib/sessiondb), contributions are
welcome
- The only frontend deleted 'thing' is the: **config.Sessions.Provider**
- No need to register a database, the sessions works out-of-the-box
- No frontend/API changes except the
`context.Session().Set/Delete/Clear`, they doesn't return errors
anymore, btw they (errors) were always nil :)
- Examples (master branch) were updated.
```sh
$ go get github.com/iris-contrib/sessiondb/$DATABASE
```
```go
db := $DATABASE.New(configurationHere{})
iris.UseSessionDB(db)
```
> Note: Book is not updated yet, examples are up-to-date as always.
2016-07-15 19:50:36 +02:00
return ctx . session
2016-06-14 07:45:40 +02:00
}
// SessionDestroy destroys the whole session, calls the provider's destroy and remove the cookie
func ( ctx * Context ) SessionDestroy ( ) {
:rainbow: sessions were re-written, update to 4.0.0-alpha.2, read HISTORY.md
**Sessions were re-written **
- Developers can use more than one 'session database', at the same time,
to store the sessions
- Easy to develop a custom session database (only two functions are
required (Load & Update)), [learn
more](https://github.com/iris-contrib/sessiondb/blob/master/redis/database.go)
- Session databases are located
[here](https://github.com/iris-contrib/sessiondb), contributions are
welcome
- The only frontend deleted 'thing' is the: **config.Sessions.Provider**
- No need to register a database, the sessions works out-of-the-box
- No frontend/API changes except the
`context.Session().Set/Delete/Clear`, they doesn't return errors
anymore, btw they (errors) were always nil :)
- Examples (master branch) were updated.
```sh
$ go get github.com/iris-contrib/sessiondb/$DATABASE
```
```go
db := $DATABASE.New(configurationHere{})
iris.UseSessionDB(db)
```
> Note: Book is not updated yet, examples are up-to-date as always.
2016-07-15 19:50:36 +02:00
if sess := ctx . Session ( ) ; sess != nil {
ctx . framework . sessions . destroy ( ctx )
2016-06-14 07:45:40 +02:00
}
}
// Log logs to the iris defined logger
func ( ctx * Context ) Log ( format string , a ... interface { } ) {
ctx . framework . Logger . Printf ( format , a ... )
}