mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
add context#StatusCodeNotSuccessful for customize even the most customized clients that are not compatible with the standards and fix the SPA
if static file serve handlers are passed as its AssetHandler
as reported at the chat.iris-go.com
Former-commit-id: ccd0815a09b9305bfbeaad7b46559dd86f34f20b
This commit is contained in:
parent
e38ea65dc7
commit
019911237c
|
@ -21,7 +21,7 @@ func newApp() *iris.Application {
|
||||||
})
|
})
|
||||||
|
|
||||||
// or just serve index.html as it is:
|
// or just serve index.html as it is:
|
||||||
// app.Get("/", func(ctx iris.Context) {
|
// app.Get("/{f:path}", func(ctx iris.Context) {
|
||||||
// ctx.ServeFile("index.html", false)
|
// ctx.ServeFile("index.html", false)
|
||||||
// })
|
// })
|
||||||
|
|
||||||
|
|
|
@ -611,7 +611,8 @@ func main(){
|
||||||
app := iris.New()
|
app := iris.New()
|
||||||
app.OnErrorCode(iris.StatusNotFound, notFound)
|
app.OnErrorCode(iris.StatusNotFound, notFound)
|
||||||
app.OnErrorCode(iris.StatusInternalServerError, internalServerError)
|
app.OnErrorCode(iris.StatusInternalServerError, internalServerError)
|
||||||
// to register a handler for all status codes >=400:
|
// to register a handler for all "error" status codes(context.StatusCodeNotSuccessful)
|
||||||
|
// defaults to < 200 || >= 400:
|
||||||
// app.OnAnyErrorCode(handler)
|
// app.OnAnyErrorCode(handler)
|
||||||
app.Get("/", index)
|
app.Get("/", index)
|
||||||
app.Run(iris.Addr(":8080"))
|
app.Run(iris.Addr(":8080"))
|
||||||
|
|
|
@ -64,7 +64,8 @@ func (b *Bootstrapper) SetupWebsockets(endpoint string, onConnection websocket.C
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupErrorHandlers prepares the http error handlers (>=400).
|
// SetupErrorHandlers prepares the http error handlers
|
||||||
|
// `(context.StatusCodeNotSuccessful`, which defaults to < 200 || >= 400 but you can change it).
|
||||||
func (b *Bootstrapper) SetupErrorHandlers() {
|
func (b *Bootstrapper) SetupErrorHandlers() {
|
||||||
b.OnAnyErrorCode(func(ctx iris.Context) {
|
b.OnAnyErrorCode(func(ctx iris.Context) {
|
||||||
err := iris.Map{
|
err := iris.Map{
|
||||||
|
|
|
@ -460,13 +460,14 @@ type Configuration struct {
|
||||||
DisableBodyConsumptionOnUnmarshal bool `json:"disableBodyConsumptionOnUnmarshal,omitempty" yaml:"DisableBodyConsumptionOnUnmarshal" toml:"DisableBodyConsumptionOnUnmarshal"`
|
DisableBodyConsumptionOnUnmarshal bool `json:"disableBodyConsumptionOnUnmarshal,omitempty" yaml:"DisableBodyConsumptionOnUnmarshal" toml:"DisableBodyConsumptionOnUnmarshal"`
|
||||||
|
|
||||||
// DisableAutoFireStatusCode if true then it turns off the http error status code handler automatic execution
|
// DisableAutoFireStatusCode if true then it turns off the http error status code handler automatic execution
|
||||||
// from "context.StatusCode(>=400)" and instead app should manually call the "context.FireStatusCode(>=400)".
|
// from (`context.StatusCodeNotSuccessful`, defaults to < 200 || >= 400).
|
||||||
|
// If that is false then for a direct error firing, then call the "context#FireStatusCode(statusCode)" manually.
|
||||||
//
|
//
|
||||||
// By-default a custom http error handler will be fired when "context.StatusCode(code)" called,
|
// By-default a custom http error handler will be fired when "context.StatusCode(code)" called,
|
||||||
// code should be >=400 in order to be received as an "http error handler".
|
// code should be equal with the result of the the `context.StatusCodeNotSuccessful` in order to be received as an "http error handler".
|
||||||
//
|
//
|
||||||
// Developer may want this option to setted as true in order to manually call the
|
// Developer may want this option to setted as true in order to manually call the
|
||||||
// error handlers when needed via "context.FireStatusCode(>=400)".
|
// error handlers when needed via "context#FireStatusCode(< 200 || >= 400)".
|
||||||
// HTTP Custom error handlers are being registered via app.OnErrorCode(code, handler)".
|
// HTTP Custom error handlers are being registered via app.OnErrorCode(code, handler)".
|
||||||
//
|
//
|
||||||
// Defaults to false.
|
// Defaults to false.
|
||||||
|
|
|
@ -1042,6 +1042,21 @@ func (ctx *context) BeginRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx.writer.BeginResponse(w)
|
ctx.writer.BeginResponse(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StatusCodeNotSuccessful defines if a specific "statusCode" is not
|
||||||
|
// a valid status code for a successful response.
|
||||||
|
// It defaults to < 200 || >= 400
|
||||||
|
//
|
||||||
|
// Read more at `iris#DisableAutoFireStatusCode`, `iris/core/router#ErrorCodeHandler`
|
||||||
|
// and `iris/core/router#OnAnyErrorCode` for relative information.
|
||||||
|
//
|
||||||
|
// Do NOT change it.
|
||||||
|
//
|
||||||
|
// It's exported for extreme situations--special needs only, when the Iris server and the client
|
||||||
|
// is not following the RFC: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||||
|
var StatusCodeNotSuccessful = func(statusCode int) bool {
|
||||||
|
return statusCode < 200 || statusCode >= 400
|
||||||
|
}
|
||||||
|
|
||||||
// EndRequest is executing once after a response to the request was sent and this context is useless or released.
|
// EndRequest is executing once after a response to the request was sent and this context is useless or released.
|
||||||
//
|
//
|
||||||
// To follow the iris' flow, developer should:
|
// To follow the iris' flow, developer should:
|
||||||
|
@ -1049,7 +1064,7 @@ func (ctx *context) BeginRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
// 2. release the response writer
|
// 2. release the response writer
|
||||||
// and any other optional steps, depends on dev's application type.
|
// and any other optional steps, depends on dev's application type.
|
||||||
func (ctx *context) EndRequest() {
|
func (ctx *context) EndRequest() {
|
||||||
if ctx.GetStatusCode() >= 400 &&
|
if StatusCodeNotSuccessful(ctx.GetStatusCode()) &&
|
||||||
!ctx.Application().ConfigurationReadOnly().GetDisableAutoFireStatusCode() {
|
!ctx.Application().ConfigurationReadOnly().GetDisableAutoFireStatusCode() {
|
||||||
// author's note:
|
// author's note:
|
||||||
// if recording, the error handler can handle
|
// if recording, the error handler can handle
|
||||||
|
|
|
@ -189,7 +189,7 @@ func (w *ResponseRecorder) WriteTo(res ResponseWriter) {
|
||||||
|
|
||||||
if to, ok := res.(*ResponseRecorder); ok {
|
if to, ok := res.(*ResponseRecorder); ok {
|
||||||
|
|
||||||
// set the status code, to is first ( probably an error >=400)
|
// set the status code, to is first ( probably an error? (context.StatusCodeNotSuccessful, defaults to < 200 || >= 400).
|
||||||
if statusCode := w.ResponseWriter.StatusCode(); statusCode == defaultStatusCode {
|
if statusCode := w.ResponseWriter.StatusCode(); statusCode == defaultStatusCode {
|
||||||
to.WriteHeader(statusCode)
|
to.WriteHeader(statusCode)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ func (err TransactionErrResult) Error() string {
|
||||||
|
|
||||||
// IsFailure returns true if this is an actual error
|
// IsFailure returns true if this is an actual error
|
||||||
func (err TransactionErrResult) IsFailure() bool {
|
func (err TransactionErrResult) IsFailure() bool {
|
||||||
return err.StatusCode >= 400
|
return StatusCodeNotSuccessful(err.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTransactionErrResult returns a new transaction result with the given error message,
|
// NewTransactionErrResult returns a new transaction result with the given error message,
|
||||||
|
|
|
@ -686,16 +686,6 @@ func (api *APIBuilder) StaticWeb(requestPath string, systemPath string) *Route {
|
||||||
|
|
||||||
handler := func(ctx context.Context) {
|
handler := func(ctx context.Context) {
|
||||||
h(ctx)
|
h(ctx)
|
||||||
// if ctx.GetStatusCode() >= 200 && ctx.GetStatusCode() < 400 {
|
|
||||||
// // re-check the content type here for any case,
|
|
||||||
// // although the new code does it automatically but it's good to have it here.
|
|
||||||
// if _, exists := ctx.ResponseWriter().Header()["Content-Type"]; !exists {
|
|
||||||
// if fname := ctx.Params().Get(paramName); fname != "" {
|
|
||||||
// cType := TypeByFilename(fname)
|
|
||||||
// ctx.ContentType(cType)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
requestPath = joinPath(requestPath, WildcardParam(paramName))
|
requestPath = joinPath(requestPath, WildcardParam(paramName))
|
||||||
|
@ -703,7 +693,7 @@ func (api *APIBuilder) StaticWeb(requestPath string, systemPath string) *Route {
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnErrorCode registers an error http status code
|
// OnErrorCode registers an error http status code
|
||||||
// based on the "statusCode" >= 400.
|
// based on the "statusCode" < 200 || >= 400 (came from `context.StatusCodeNotSuccessful`).
|
||||||
// The handler is being wrapepd by a generic
|
// The handler is being wrapepd by a generic
|
||||||
// handler which will try to reset
|
// handler which will try to reset
|
||||||
// the body if recorder was enabled
|
// the body if recorder was enabled
|
||||||
|
@ -718,55 +708,16 @@ func (api *APIBuilder) OnErrorCode(statusCode int, handlers ...context.Handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnAnyErrorCode registers a handler which called when error status code written.
|
// OnAnyErrorCode registers a handler which called when error status code written.
|
||||||
// Same as `OnErrorCode` but registers all http error codes.
|
// Same as `OnErrorCode` but registers all http error codes based on the `context.StatusCodeNotSuccessful`
|
||||||
// See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
// which defaults to < 200 || >= 400 for an error code, any previos error code will be overriden,
|
||||||
|
// so call it first if you want to use any custom handler for a specific error status code.
|
||||||
|
//
|
||||||
|
// Read more at: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
||||||
func (api *APIBuilder) OnAnyErrorCode(handlers ...context.Handler) {
|
func (api *APIBuilder) OnAnyErrorCode(handlers ...context.Handler) {
|
||||||
// we could register all >=400 and <=511 but this way
|
for code := 100; code <= 511; code++ {
|
||||||
// could override custom status codes that iris developers can register for their
|
if context.StatusCodeNotSuccessful(code) {
|
||||||
// web apps whenever needed.
|
api.OnErrorCode(code, handlers...)
|
||||||
// There fore these are the hard coded http error statuses:
|
}
|
||||||
var errStatusCodes = []int{
|
|
||||||
http.StatusBadRequest,
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
http.StatusPaymentRequired,
|
|
||||||
http.StatusForbidden,
|
|
||||||
http.StatusNotFound,
|
|
||||||
http.StatusMethodNotAllowed,
|
|
||||||
http.StatusNotAcceptable,
|
|
||||||
http.StatusProxyAuthRequired,
|
|
||||||
http.StatusRequestTimeout,
|
|
||||||
http.StatusConflict,
|
|
||||||
http.StatusGone,
|
|
||||||
http.StatusLengthRequired,
|
|
||||||
http.StatusPreconditionFailed,
|
|
||||||
http.StatusRequestEntityTooLarge,
|
|
||||||
http.StatusRequestURITooLong,
|
|
||||||
http.StatusUnsupportedMediaType,
|
|
||||||
http.StatusRequestedRangeNotSatisfiable,
|
|
||||||
http.StatusExpectationFailed,
|
|
||||||
http.StatusTeapot,
|
|
||||||
http.StatusUnprocessableEntity,
|
|
||||||
http.StatusLocked,
|
|
||||||
http.StatusFailedDependency,
|
|
||||||
http.StatusUpgradeRequired,
|
|
||||||
http.StatusPreconditionRequired,
|
|
||||||
http.StatusTooManyRequests,
|
|
||||||
http.StatusRequestHeaderFieldsTooLarge,
|
|
||||||
http.StatusUnavailableForLegalReasons,
|
|
||||||
http.StatusInternalServerError,
|
|
||||||
http.StatusNotImplemented,
|
|
||||||
http.StatusBadGateway,
|
|
||||||
http.StatusServiceUnavailable,
|
|
||||||
http.StatusGatewayTimeout,
|
|
||||||
http.StatusHTTPVersionNotSupported,
|
|
||||||
http.StatusVariantAlsoNegotiates,
|
|
||||||
http.StatusInsufficientStorage,
|
|
||||||
http.StatusLoopDetected,
|
|
||||||
http.StatusNotExtended,
|
|
||||||
http.StatusNetworkAuthenticationRequired}
|
|
||||||
|
|
||||||
for _, statusCode := range errStatusCodes {
|
|
||||||
api.OnErrorCode(statusCode, handlers...)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -254,7 +254,7 @@ func (w *fsHandler) Build() context.Handler {
|
||||||
gzipEnabled)
|
gzipEnabled)
|
||||||
|
|
||||||
// check for any http errors after the file handler executed
|
// check for any http errors after the file handler executed
|
||||||
if prevStatusCode >= 400 { // error found (404 or 400 or 500 usually)
|
if context.StatusCodeNotSuccessful(prevStatusCode) { // error found (404 or 400 or 500 usually)
|
||||||
if writer, ok := ctx.ResponseWriter().(*context.GzipResponseWriter); ok && writer != nil {
|
if writer, ok := ctx.ResponseWriter().(*context.GzipResponseWriter); ok && writer != nil {
|
||||||
writer.ResetBody()
|
writer.ResetBody()
|
||||||
writer.Disable()
|
writer.Disable()
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
package router
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/kataras/iris/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AssetValidator returns true if "filename"
|
|
||||||
// is asset, i.e: strings.Contains(filename, ".").
|
|
||||||
type AssetValidator func(filename string) bool
|
|
||||||
|
|
||||||
// SPABuilder helps building a single page application server
|
|
||||||
// which serves both routes and files from the root path.
|
|
||||||
type SPABuilder struct {
|
|
||||||
IndexNames []string
|
|
||||||
AssetHandler context.Handler
|
|
||||||
AssetValidators []AssetValidator
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddIndexName will add an index name.
|
|
||||||
// If path == $filename then it redirects to "/".
|
|
||||||
//
|
|
||||||
// It can be called after the `BuildWrapper ` as well but BEFORE the server start.
|
|
||||||
func (s *SPABuilder) AddIndexName(filename string) *SPABuilder {
|
|
||||||
s.IndexNames = append(s.IndexNames, filename)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSPABuilder returns a new Single Page Application builder
|
|
||||||
// It does what StaticWeb or StaticEmbedded expected to do when serving files and routes at the same time
|
|
||||||
// from the root "/" path.
|
|
||||||
//
|
|
||||||
// Accepts a static asset handler, which can be an app.StaticHandler, app.StaticEmbeddedHandler...
|
|
||||||
func NewSPABuilder(assetHandler context.Handler) *SPABuilder {
|
|
||||||
if assetHandler == nil {
|
|
||||||
assetHandler = func(ctx context.Context) {
|
|
||||||
ctx.Writef("empty asset handler")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &SPABuilder{
|
|
||||||
IndexNames: nil,
|
|
||||||
// IndexNames is empty by-default,
|
|
||||||
// if the user wants to redirect to "/" from "/index.html" she/he can chage that to []string{"index.html"} manually.
|
|
||||||
AssetHandler: assetHandler,
|
|
||||||
AssetValidators: []AssetValidator{
|
|
||||||
func(path string) bool {
|
|
||||||
return true // returns true by-default, if false then it fires 404.
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SPABuilder) isAsset(reqPath string) bool {
|
|
||||||
for _, v := range s.AssetValidators {
|
|
||||||
if !v(reqPath) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handler serves the asset handler but in addition, it makes some checks before that,
|
|
||||||
// based on the `AssetValidators` and `IndexNames`.
|
|
||||||
func (s *SPABuilder) Handler(ctx context.Context) {
|
|
||||||
path := ctx.Path()
|
|
||||||
|
|
||||||
// make a validator call, by-default all paths are valid and this codeblock doesn't mean anything
|
|
||||||
// but for cases that users wants to bypass an asset she/he can do that by modifiying the `APIBuilder#AssetValidators` field.
|
|
||||||
//
|
|
||||||
// It's here for backwards compatibility as well, see #803.
|
|
||||||
if !s.isAsset(path) {
|
|
||||||
// it's not asset, execute the registered route's handlers
|
|
||||||
ctx.NotFound()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, index := range s.IndexNames {
|
|
||||||
if strings.HasSuffix(path, index) {
|
|
||||||
localRedirect(ctx, "./")
|
|
||||||
// "/" should be manually registered.
|
|
||||||
// We don't setup an index handler here,
|
|
||||||
// let full control to the user
|
|
||||||
// (use middleware, ctx.ServeFile or ctx.View and so on...)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s.AssetHandler(ctx)
|
|
||||||
}
|
|
167
core/router/spa.go
Normal file
167
core/router/spa.go
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/kataras/iris/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AssetValidator returns true if "filename"
|
||||||
|
// is asset, i.e: strings.Contains(filename, ".").
|
||||||
|
type AssetValidator func(filename string) bool
|
||||||
|
|
||||||
|
// SPABuilder helps building a single page application server
|
||||||
|
// which serves both routes and files from the root path.
|
||||||
|
type SPABuilder struct {
|
||||||
|
// Root defaults to "/", it's the root path that explicitly set-ed,
|
||||||
|
// this can be changed if more than SPAs are used on the same
|
||||||
|
// iris router instance.
|
||||||
|
Root string
|
||||||
|
// emptyRoot can be changed with `ChangeRoot` only,
|
||||||
|
// is, statically, true if root is empty
|
||||||
|
// and if root is empty then let 404 fire from server-side anyways if
|
||||||
|
// the passed `AssetHandler` returns 404 for a specific request path.
|
||||||
|
// Defaults to false.
|
||||||
|
emptyRoot bool
|
||||||
|
|
||||||
|
IndexNames []string
|
||||||
|
AssetHandler context.Handler
|
||||||
|
AssetValidators []AssetValidator
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddIndexName will add an index name.
|
||||||
|
// If path == $filename then it redirects to Root, which defaults to "/".
|
||||||
|
//
|
||||||
|
// It can be called BEFORE the server start.
|
||||||
|
func (s *SPABuilder) AddIndexName(filename string) *SPABuilder {
|
||||||
|
s.IndexNames = append(s.IndexNames, filename)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangeRoot modifies the `Root` request path that is
|
||||||
|
// explicitly set-ed if the `AssetHandler` gave a Not Found (404)
|
||||||
|
// previously, if request's path is the passed "path"
|
||||||
|
// then it explicitly sets that and it retries executing the `AssetHandler`.
|
||||||
|
//
|
||||||
|
// Empty Root means that let 404 fire from server-side anyways.
|
||||||
|
//
|
||||||
|
// Change it ONLY if you use more than one typical SPAs on the same Iris Application instance.
|
||||||
|
func (s *SPABuilder) ChangeRoot(path string) *SPABuilder {
|
||||||
|
s.Root = path
|
||||||
|
s.emptyRoot = path == ""
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSPABuilder returns a new Single Page Application builder
|
||||||
|
// It does what StaticWeb or StaticEmbedded expected to do when serving files and routes at the same time
|
||||||
|
// from the root "/" path.
|
||||||
|
//
|
||||||
|
// Accepts a static asset handler, which can be an app.StaticHandler, app.StaticEmbeddedHandler...
|
||||||
|
func NewSPABuilder(assetHandler context.Handler) *SPABuilder {
|
||||||
|
if assetHandler == nil {
|
||||||
|
assetHandler = func(ctx context.Context) {
|
||||||
|
ctx.Writef("empty asset handler")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &SPABuilder{
|
||||||
|
Root: "/",
|
||||||
|
IndexNames: nil,
|
||||||
|
// "IndexNames" are empty by-default,
|
||||||
|
// if the user wants to redirect to "/" from "/index.html" she/he can chage that to []string{"index.html"} manually
|
||||||
|
// or use the `StaticHandler` as "AssetHandler" which does that already.
|
||||||
|
AssetHandler: assetHandler,
|
||||||
|
AssetValidators: []AssetValidator{
|
||||||
|
func(path string) bool {
|
||||||
|
return true // returns true by-default, if false then it fires 404.
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SPABuilder) isAsset(reqPath string) bool {
|
||||||
|
for _, v := range s.AssetValidators {
|
||||||
|
if !v(reqPath) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler serves the asset handler but in addition, it makes some checks before that,
|
||||||
|
// based on the `AssetValidators` and `IndexNames`.
|
||||||
|
func (s *SPABuilder) Handler(ctx context.Context) {
|
||||||
|
path := ctx.Path()
|
||||||
|
|
||||||
|
// make a validator call, by-default all paths are valid and this codeblock doesn't mean anything
|
||||||
|
// but for cases that users wants to bypass an asset she/he can do that by modifiying the `APIBuilder#AssetValidators` field.
|
||||||
|
//
|
||||||
|
// It's here for backwards compatibility as well, see #803.
|
||||||
|
if !s.isAsset(path) {
|
||||||
|
// it's not asset, execute the registered route's handlers
|
||||||
|
ctx.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, index := range s.IndexNames {
|
||||||
|
if strings.HasSuffix(path, index) {
|
||||||
|
if s.emptyRoot {
|
||||||
|
ctx.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
localRedirect(ctx, "."+s.Root)
|
||||||
|
// s.Root should be manually registered to a route
|
||||||
|
// (not always, only if custom handler used).
|
||||||
|
// We don't setup an index handler here,
|
||||||
|
// let full control to the developer via "AssetHandler"
|
||||||
|
// (use of middleware, manually call of the ctx.ServeFile or ctx.View etc.)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.AssetHandler(ctx)
|
||||||
|
|
||||||
|
if context.StatusCodeNotSuccessful(ctx.GetStatusCode()) && !s.emptyRoot && path != s.Root {
|
||||||
|
// If file was not something like a javascript file, or a css or anything that
|
||||||
|
// the passed `AssetHandler` scan-ed then re-execute the `AssetHandler`
|
||||||
|
// using the `Root` as the request path (virtually).
|
||||||
|
//
|
||||||
|
// If emptyRoot is true then
|
||||||
|
// fire the response as it's, "AssetHandler" is fully responsible for it,
|
||||||
|
// client-side's router for invalid paths will not work here else read below.
|
||||||
|
//
|
||||||
|
// Author's notes:
|
||||||
|
// the server doesn't need to know all client routes,
|
||||||
|
// client-side router is responsible for any kind of invalid paths,
|
||||||
|
// so explicit set to root path.
|
||||||
|
//
|
||||||
|
// The most simple solution was to use a
|
||||||
|
// func(ctx iris.Context) { ctx.ServeFile("$PATH/index.html") } as the "AssetHandler"
|
||||||
|
// but many developers use the `StaticHandler` (as shown in the examples)
|
||||||
|
// but it was not working as expected because it (correctly) fires
|
||||||
|
// a 404 not found if a file based on the request path didn't found.
|
||||||
|
//
|
||||||
|
// We can't just do it before the "AssetHandler"'s execution
|
||||||
|
// for two main reasons:
|
||||||
|
// 1. if it's a file serve handler, like `StaticHandler` then it will never serve
|
||||||
|
// the corresponding files!
|
||||||
|
// 2. it may manually handle those things,
|
||||||
|
// don't forget that "AssetHandler" can be
|
||||||
|
// ANY iris handler, so we can't be sure what the developer may want to do there.
|
||||||
|
//
|
||||||
|
// "AssetHandler" as the "StaticHandler" a retry doesn't hurt,
|
||||||
|
// it will give us a 404 if the file didn't found very fast WITHOUT moving to the
|
||||||
|
// rest of its validation and serving implementation.
|
||||||
|
//
|
||||||
|
// Another idea would be to modify the "AssetHandler" on every `ChangeRoot`
|
||||||
|
// call, which may give us some performance (ns) benefits
|
||||||
|
// but this could be bad if root is set-ed before the "AssetHandler",
|
||||||
|
// so keep it as it's.
|
||||||
|
rootURL, err := ctx.Request().URL.Parse(s.Root)
|
||||||
|
if err == nil {
|
||||||
|
ctx.Request().URL = rootURL
|
||||||
|
s.AssetHandler(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,10 @@ import (
|
||||||
"github.com/kataras/iris/context"
|
"github.com/kataras/iris/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func statusCodeSuccessful(statusCode int) bool {
|
||||||
|
return !context.StatusCodeNotSuccessful(statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
// ErrorCodeHandler is the entry
|
// ErrorCodeHandler is the entry
|
||||||
// of the list of all http error code handlers.
|
// of the list of all http error code handlers.
|
||||||
type ErrorCodeHandler struct {
|
type ErrorCodeHandler struct {
|
||||||
|
@ -21,7 +25,7 @@ type ErrorCodeHandler struct {
|
||||||
func (ch *ErrorCodeHandler) Fire(ctx context.Context) {
|
func (ch *ErrorCodeHandler) Fire(ctx context.Context) {
|
||||||
// if we can reset the body
|
// if we can reset the body
|
||||||
if w, ok := ctx.IsRecording(); ok {
|
if w, ok := ctx.IsRecording(); ok {
|
||||||
if w.StatusCode() < 400 { // if not an error status code
|
if statusCodeSuccessful(w.StatusCode()) { // if not an error status code
|
||||||
w.WriteHeader(ch.StatusCode) // then set it manually here, otherwise it should be setted via ctx.StatusCode(...)
|
w.WriteHeader(ch.StatusCode) // then set it manually here, otherwise it should be setted via ctx.StatusCode(...)
|
||||||
}
|
}
|
||||||
// reset if previous content and it's recorder, keep the status code.
|
// reset if previous content and it's recorder, keep the status code.
|
||||||
|
@ -109,14 +113,14 @@ func (s *ErrorCodeHandlers) Get(statusCode int) *ErrorCodeHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register registers an error http status code
|
// Register registers an error http status code
|
||||||
// based on the "statusCode" >= 400.
|
// based on the "statusCode" < 200 || >= 400 (`context.StatusCodeNotSuccessful`).
|
||||||
// The handler is being wrapepd by a generic
|
// The handler is being wrapepd by a generic
|
||||||
// handler which will try to reset
|
// handler which will try to reset
|
||||||
// the body if recorder was enabled
|
// the body if recorder was enabled
|
||||||
// and/or disable the gzip if gzip response recorder
|
// and/or disable the gzip if gzip response recorder
|
||||||
// was active.
|
// was active.
|
||||||
func (s *ErrorCodeHandlers) Register(statusCode int, handlers ...context.Handler) *ErrorCodeHandler {
|
func (s *ErrorCodeHandlers) Register(statusCode int, handlers ...context.Handler) *ErrorCodeHandler {
|
||||||
if statusCode < 400 {
|
if statusCodeSuccessful(statusCode) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +149,7 @@ func (s *ErrorCodeHandlers) Register(statusCode int, handlers ...context.Handler
|
||||||
// then it creates & registers a new trivial handler on the-fly.
|
// then it creates & registers a new trivial handler on the-fly.
|
||||||
func (s *ErrorCodeHandlers) Fire(ctx context.Context) {
|
func (s *ErrorCodeHandlers) Fire(ctx context.Context) {
|
||||||
statusCode := ctx.GetStatusCode()
|
statusCode := ctx.GetStatusCode()
|
||||||
if statusCode < 400 {
|
if statusCodeSuccessful(statusCode) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ch := s.Get(statusCode)
|
ch := s.Get(statusCode)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user