Former-commit-id: 2b48b9b912e9eb8f5f2c6d413907dbfdae857e08
This commit is contained in:
Gerasimos (Makis) Maropoulos 2019-06-14 11:26:56 +03:00
parent 272566950d
commit 0f907a3dae
8 changed files with 87 additions and 23 deletions

View File

@ -798,7 +798,9 @@ func (api *APIBuilder) Favicon(favPath string, requestPath ...string) *Route {
//
// As a special case, the returned file server redirects any request
// ending in "/index.html" to the same path, without the final
// "index.html".
// "/index.html", if `index.html` should be served then register a
// new route for it, i.e
// `app.Get("/static", func(ctx iris.Context){ ctx.ServeFile("./static/index.html", false) })`.
//
// StaticWeb calls the `StripPrefix(fullpath, NewStaticHandlerBuilder(systemPath).Listing(false).Build())`.
//

View File

@ -127,7 +127,7 @@ func StaticEmbeddedHandler(vdir string, assetFn func(name string) ([]byte, error
// fileserver := iris.StaticHandler("./static_files", false, false)
// h := router.StripPrefix("/static", fileserver)
// /* http://mydomain.com/static/css/style.css */
// app.Get("/static", h)
// app.Get("/static/{file:path}", h)
// ...
//
func StaticHandler(systemPath string, showList bool, gzip bool) context.Handler {

View File

@ -82,7 +82,7 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
registeredRoutes := provider.GetRoutes()
h.trees = h.trees[0:0] // reset, inneed when rebuilding.
// sort, subdomains goes first.
// sort, subdomains go first.
sort.Slice(registeredRoutes, func(i, j int) bool {
first, second := registeredRoutes[i], registeredRoutes[j]
lsub1 := len(first.Subdomain)

View File

@ -28,7 +28,8 @@ type Route struct {
// temp storage, they're appended to the Handlers on build.
// Execution happens after Begin and main Handler(s), can be empty.
doneHandlers context.Handlers
Path string `json:"path"` // "/api/user/:id"
Path string `json:"path"` // "/api/user/{id:uint64}"
// FormattedPath all dynamic named parameters (if any) replaced with %v,
// used by Application to validate param values of a Route based on its name.
FormattedPath string `json:"formattedPath"`

View File

@ -93,7 +93,7 @@ func (router *Router) BuildRouter(cPool *context.Pool, requestHandler RequestHan
}
// Downgrade "downgrades", alters the router supervisor service(Router.mainHandler)
// algorithm to a custom one,
// algorithm to a custom one,
// be aware to change the global variables of 'ParamStart' and 'ParamWildcardStart'.
// can be used to implement a custom proxy or
// a custom router which should work with raw ResponseWriter, *Request

View File

@ -73,6 +73,7 @@ func IndirectValue(v reflect.Value) reflect.Value {
if k := v.Kind(); k == reflect.Ptr { //|| k == reflect.Interface {
return v.Elem()
}
return v
}
@ -106,6 +107,17 @@ func IndirectType(typ reflect.Type) reflect.Type {
return typ
}
// IsNil same as `reflect.IsNil` but a bit safer to use, returns false if not a correct type.
func IsNil(v reflect.Value) bool {
k := v.Kind()
switch k {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
return v.IsNil()
default:
return false
}
}
func goodVal(v reflect.Value) bool {
switch v.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:

View File

@ -211,7 +211,6 @@ func DispatchFuncResult(ctx context.Context, errorHandler ErrorHandler, values [
)
for _, v := range values {
// order of these checks matters
// for example, first we need to check for status code,
// secondly the string (for content type and content)...
@ -310,27 +309,46 @@ func DispatchFuncResult(ctx context.Context, errorHandler ErrorHandler, values [
// it's raw content, get the latest
content = value
case compatibleErr:
if value != nil { // it's always not nil but keep it here.
if errorHandler != nil {
errorHandler.HandleError(ctx, value)
break
}
err = value
if statusCode < 400 {
statusCode = DefaultErrStatusCode
}
break // break on first error, error should be in the end but we
// need to know break the dispatcher if any error.
// at the end; we don't want to write anything to the response if error is not nil.
if value == nil || di.IsNil(v) {
continue
}
if errorHandler != nil {
errorHandler.HandleError(ctx, value)
break
}
err = value
if statusCode < 400 {
statusCode = DefaultErrStatusCode
}
break // break on first error, error should be in the end but we
// need to know break the dispatcher if any error.
// at the end; we don't want to write anything to the response if error is not nil.
default:
// else it's a custom struct or a dispatcher, we'll decide later
// because content type and status code matters
// do that check in order to be able to correctly dispatch:
// (customStruct, error) -> customStruct filled and error is nil
if custom == nil && f != nil {
custom = f
if custom == nil {
// if it's a pointer to struct/map.
if di.IsNil(v) {
// if just a ptr to struct with no content type given
// then try to get the previous response writer's content type,
// and if that is empty too then force-it to application/json
// as the default content type we use for structs/maps.
contentType = ctx.GetContentType()
if contentType == "" {
contentType = context.ContentJSONHeaderValue
}
continue
}
if value != nil {
custom = value // content type will be take care later on.
}
}
}
}

View File

@ -70,7 +70,7 @@ func GetCustomStructWithContentType() (testCustomStruct, string) {
return testCustomStruct{"Iris", 2}, "text/xml"
}
func GetCustomStructWithError(ctx iris.Context) (s testCustomStruct, err error) {
func GetCustomStructWithError(ctx context.Context) (s testCustomStruct, err error) {
s = testCustomStruct{"Iris", 2}
if ctx.URLParamExists("err") {
err = errors.New("omit return of testCustomStruct and fire error")
@ -86,7 +86,7 @@ type err struct {
Message string `json:"message"`
}
func (e err) Dispatch(ctx iris.Context) {
func (e err) Dispatch(ctx context.Context) {
// write the status code based on the err's StatusCode.
ctx.StatusCode(e.Status)
// send to the client the whole object as json
@ -97,6 +97,22 @@ func GetCustomErrorAsDispatcher() err {
return err{iris.StatusBadRequest, "this is my error as json"}
}
func GetCustomTypedNilEmptyResponse() iris.Map {
return nil
}
func GetCustomTypedPtrNilEmptyResponse() *iris.Map {
return nil
}
func GetCustomMapNilEmptyResponse() map[string]interface{} {
return nil
}
func GetCustomPtrStructNilEmptyResponse() *testCustomStruct {
return nil
}
func TestFuncResult(t *testing.T) {
app := iris.New()
h := New()
@ -120,6 +136,11 @@ func TestFuncResult(t *testing.T) {
app.Get("/custom/struct/with/error", h.Handler(GetCustomStructWithError))
app.Get("/custom/error/as/dispatcher", h.Handler(GetCustomErrorAsDispatcher))
app.Get("/custom/nil/typed", h.Handler(GetCustomTypedNilEmptyResponse))
app.Get("/custom/nil/typed/ptr", h.Handler(GetCustomTypedPtrNilEmptyResponse))
app.Get("/custom/nil/map", h.Handler(GetCustomMapNilEmptyResponse))
app.Get("/custom/nil/struct", h.Handler(GetCustomPtrStructNilEmptyResponse))
e := httptest.New(t, app)
e.GET("/text").Expect().Status(iris.StatusOK).
@ -172,4 +193,14 @@ func TestFuncResult(t *testing.T) {
// the content should be not JSON it should be the status code's text
// it will fire the error's text
JSON().Equal(err{iris.StatusBadRequest, "this is my error as json"})
// its result is nil should give an empty response but content-type is set correctly.
e.GET("/custom/nil/typed").Expect().
Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().Empty()
e.GET("/custom/nil/typed/ptr").Expect().
Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().Empty()
e.GET("/custom/nil/map").Expect().
Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().Empty()
e.GET("/custom/nil/struct").Expect().
Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().Empty()
}