Add registers ctrl. It takes additional comma separated list of middleware. middlewares are of type


utron uses the alice package to chain middlewares, this means all alice compatible middleware works out of the box

Add is referenced in 1 repository


func (r *Router) Add(ctrl Controller, middlewares ...interface{}) error {
	var (

		// routes is a slice of all routes associated
		// with ctrl
		routes = struct {
			inCtrl, standard []*route

		// baseController is the name of the Struct BaseController
		// when users embed the BaseController, an anonymous field
		// BaseController is added, and here we are referring to the name of the
		// anonymous field
		baseController = "BaseController"

		// routePaths is the name of the field that allows uses to add Routes information
		routePaths = "Routes"

	baseCtr := reflect.ValueOf(&BaseController{})
	ctrlVal := reflect.ValueOf(ctrl)

	bTyp := baseCtr.Type()
	cTyp := ctrlVal.Type()

	numCtr := cTyp.NumMethod()

	ctrlName := getTypName(cTyp) // The name of the controller

	for v := range make([]struct{}, numCtr) {
		method := cTyp.Method(v)

		// skip methods defined by the base controller
		if _, bok := bTyp.MethodByName(method.Name); bok {

		// patt composes pattern. This can be overridden by routes defined in the Routes
		// field of the controller.
		// By default the path is of the form /:controller/:method. All http methods will be registered
		// for this pattern, meaning it is up to the user to filter out what he/she wants, the easier way
		// is to use the Routes field instead
		// TODD: figure out the way of passing parameters to the method arguments?
		patt := "/" + strings.ToLower(ctrlName) + "/" + strings.ToLower(method.Name)

		r := &route{
			pattern: patt,
			ctrl:    ctrlName,
			fn:      method.Name,
		routes.standard = append(routes.standard, r)

	// ultimate returns the actual value stored in rVals this means if rVals is a pointer,
	// then we return the value that is pointed to. We are dealing with structs, so the returned
	// value is of kind reflect.Struct
	ultimate := func(rVals reflect.Value) reflect.Value {
		val := rVals
		switch val.Kind() {
		case reflect.Ptr:
			val = val.Elem()
		return val

	uCtr := ultimate(ctrlVal) // actual value after dereferencing the pointer

	uCtrTyp := uCtr.Type() // we store the type, so we can use in the next iterations

	for k := range make([]struct{}, uCtr.NumField()) {
		// We iterate in all fields, to filter out the user defined methods. We are aware
		// of methods inherited from the BaseController. Since we recommend user Controllers
		// should embed BaseController

		field := uCtrTyp.Field(k)

		// If we find any field matching BaseController
		// we initialize its value.
		if field.Name == baseController {
			fieldVal := uCtr.Field(k)

		// If there is any field named Routes, and it is of signature []string
		// then the field's value is used to override the patterns defined earlier.
		// It is not necessary for every user implementation to define method named Routes
		// If we can't find it then we just ignore its use and fall-back to defaults.
		// Route strings, are of the form "httpMethods;path;method"
		// where httMethod: is a comma separated http method strings
		//                  e.g GET,POST,PUT.
		//                  The case does not matter, you can use lower case or upper case characters
		//                  or even mixed case, that is get,GET,gET and GeT will all be treated as GET
		//        path:     Is a url path or pattern, utron uses gorilla mux package. So, everything you can do
		//                  with gorilla mux url path then you can do here.
		//                  e.g /hello/{world}
		//                  Don't worry about the params, they will be accessible via .Ctx.Params field in your
		//                  controller.
		//        method:   The name of the user Controller method to execute for this route.
		if field.Name == routePaths {
			fieldVal := uCtr.Field(k)
			switch fieldVal.Kind() {
			case reflect.Slice:
				if data, ok := fieldVal.Interface().([]string); ok {
					for _, d := range data {
						rt, err := splitRoutes(d)
						if err != nil {
						routes.inCtrl = append(routes.inCtrl, rt)



	for _, v := range routes.standard {

		var found bool

		// use routes from the configuration file first
		for _, rFile := range r.routes {
			if rFile.ctrl == v.ctrl && rFile.fn == v.fn {
				if err := r.add(rFile, ctrl, middlewares...); err != nil {
					return err
				found = true

		// if there is no match from the routes file, use the routes defined in the Routes field
		if !found {
			for _, rFile := range routes.inCtrl {
				if rFile.fn == v.fn {
					if err := r.add(rFile, ctrl, middlewares...); err != nil {
						return err
					found = true

		// resolve to sandard when everything else never matched
		if !found {
			if err := r.add(v, ctrl, middlewares...); err != nil {
				return err

	return nil