func (g *Group) Endpoint(prefix string) http.Handler {
	// trim prefix
	prefix = strings.Trim(prefix, "/")

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// create tracer
		tracer := NewTracerFromRequest(r, "fire/Group.Endpoint")
		defer tracer.Finish(true)

		// continue any previous aborts
		defer stack.Resume(func(err error) {
			// directly write jsonapi errors
			if jsonapiError, ok := err.(*jsonapi.Error); ok {
				_ = jsonapi.WriteError(w, jsonapiError)
				return
			}

			// set critical error on last span
			tracer.Tag("error", true)
			tracer.Log("error", err.Error())
			tracer.Log("stack", stack.Trace())

			// report critical errors if possible
			if g.Reporter != nil {
				g.Reporter(err)
			}

			// ignore errors caused by writing critical errors
			_ = jsonapi.WriteError(w, jsonapi.InternalServerError(""))
		})

		// trim path
		path := strings.Trim(r.URL.Path, "/")
		path = strings.TrimPrefix(path, prefix)
		path = strings.Trim(path, "/")

		// check path
		if path == "" {
			stack.Abort(jsonapi.NotFound("resource not found"))
		}

		// split path
		s := strings.Split(path, "/")

		// prepare context
		ctx := &Context{
			Data:           Map{},
			HTTPRequest:    r,
			ResponseWriter: w,
			Group:          g,
			Tracer:         tracer,
		}

		// get controller
		controller, ok := g.controllers[s[0]]
		if ok {
			// set controller
			ctx.Controller = controller

			// call controller with context
			controller.generalHandler(prefix, ctx)

			return
		}

		// get action
		action, ok := g.actions[s[0]]
		if ok {
			// check if action is allowed
			if Contains(action.Action.Methods, r.Method) {
				// check if action matches the context
				if action.Action.Callback.Matcher(ctx) {
					// run authorizers and handle errors
					for _, cb := range action.Authorizers {
						// check if callback should be run
						if !cb.Matcher(ctx) {
							continue
						}

						// call callback
						err := cb.Handler(ctx)
						if IsSafe(err) {
							stack.Abort(&jsonapi.Error{
								Status: http.StatusUnauthorized,
								Detail: err.Error(),
							})
						} else if err != nil {
							stack.Abort(err)
						}
					}

					// limit request body size
					LimitBody(ctx.ResponseWriter, ctx.HTTPRequest, int64(action.Action.BodyLimit))

					// call action with context
					stack.AbortIf(action.Action.Callback.Handler(ctx))

					return
				}
			}
		}

		// otherwise return error
		stack.Abort(jsonapi.NotFound("resource not found"))
	})
}
func (g *Group) Endpoint(prefix string) http.Handler {
	// trim prefix
	prefix = strings.Trim(prefix, "/")

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// create tracer
		tracer := NewTracerFromRequest(r, "fire/Group.Endpoint")
		defer tracer.Finish(true)

		// continue any previous aborts
		defer stack.Resume(func(err error) {
			// directly write jsonapi errors
			if jsonapiError, ok := err.(*jsonapi.Error); ok {
				_ = jsonapi.WriteError(w, jsonapiError)
				return
			}

			// set critical error on last span
			tracer.Tag("error", true)
			tracer.Log("error", err.Error())
			tracer.Log("stack", stack.Trace())

			// report critical errors if possible
			if g.Reporter != nil {
				g.Reporter(err)
			}

			// ignore errors caused by writing critical errors
			_ = jsonapi.WriteError(w, jsonapi.InternalServerError(""))
		})

		// trim path
		path := strings.Trim(r.URL.Path, "/")
		path = strings.TrimPrefix(path, prefix)
		path = strings.Trim(path, "/")

		// check path
		if path == "" {
			stack.Abort(jsonapi.NotFound("resource not found"))
		}

		// split path
		s := strings.Split(path, "/")

		// prepare context
		ctx := &Context{
			Data:           Map{},
			HTTPRequest:    r,
			ResponseWriter: w,
			Group:          g,
			Tracer:         tracer,
		}

		// get controller
		controller, ok := g.controllers[s[0]]
		if ok {
			// set controller
			ctx.Controller = controller

			// call controller with context
			controller.generalHandler(prefix, ctx)

			return
		}

		// get action
		action, ok := g.actions[s[0]]
		if ok {
			// check if action is allowed
			if Contains(action.Action.Methods, r.Method) {
				// check if action matches the context
				if action.Action.Callback.Matcher(ctx) {
					// run authorizers and handle errors
					for _, cb := range action.Authorizers {
						// check if callback should be run
						if !cb.Matcher(ctx) {
							continue
						}

						// call callback
						err := cb.Handler(ctx)
						if IsSafe(err) {
							stack.Abort(&jsonapi.Error{
								Status: http.StatusUnauthorized,
								Detail: err.Error(),
							})
						} else if err != nil {
							stack.Abort(err)
						}
					}

					// limit request body size
					LimitBody(ctx.ResponseWriter, ctx.HTTPRequest, int64(action.Action.BodyLimit))

					// call action with context
					stack.AbortIf(action.Action.Callback.Handler(ctx))

					return
				}
			}
		}

		// otherwise return error
		stack.Abort(jsonapi.NotFound("resource not found"))
	})
}
// Endpoint will return an http handler that serves requests for this group. The
// specified prefix is used to parse the requests and generate urls for the
// resources.