FirstApp
├── appobj
│ ├── appconf.go
│ ├── appobj.go
| └── lead_set_get.go
├── controllers
│ ├── authc.go
│ ├── controllerfuncs.go
│ ├── groupauthc.go
│ ├── person_relationsc.go
│ ├── personc.go
│ ├── usrc.go
│ ├── usr_groupc.go
│ └── ext
│ ├── extc_interfaces.go
│ └── personc_ext.go
├── group
│ ├── ...
│ │ ├── ...
. . .
.
.
├── .dev.config.json
├── .prd.config.json
├── main_test.go
└── main.go
A controller is created for each entity that has been declared in the model files, as well as a number of static controllers that are used to handle the application’s users and authorization objects.
Controllers act as a bridge between an entity’s routes and its model layer. Each entity mux route is assigned a method in their respective controller based on the intent of that route. For example, to create a new new ‘Library’ entity the following POST could be made:
https://servername:port/library {JSON body} + POST
The route for this call is defined in appobj.go as follows, where ‘a’ is the one-and-only instance of the AppObj:
a.router.HandleFunc("/library", requireUserMw.ApplyFn(a.libraryC.Create)).Methods("POST")
The ‘/library’-POST route is assigned a HandleFunc belonging to the instance of the LibraryController that has been created on the appobj. Controller method a.libraryC.Create is called for the ‘library’ route when the http method equals ‘POST’. The route contains some additional code related to authentication and authorization of the requester but this can be ignored for now. The handler function for a mux.route must conform to the standard go http.Handler interface:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
This interface facilitates the passing of the incoming request header and body to the controller method, as well as the passing of the formatted response back to the router. With this out of out the way, let’s look at generated Controller method LibraryController.Create:
// Create facilitates the creation of a new Library. This method is bound
// to the gorilla.mux router in main.go.
//
// POST /library
func (lc *LibraryController) Create(w http.ResponseWriter, r *http.Request) {
var l models.Library
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&l); err != nil {
log.Println("Library Create:", err)
respondWithError(w, http.StatusBadRequest, "libraryc: Invalid request payload")
return
}
defer r.Body.Close()
// fill the model
library := models.Library{
Name: l.Name,
City: l.City,
}
// build a base urlString for the JSON Body self-referencing Href tag
urlString := buildHrefStringFromCRUDReq(r, true)
// call the Create method on the library model
err := lc.ls.Create(&library)
if err != nil {
log.Println("Library Create:", err)
respondWithError(w, http.StatusBadRequest, err.Error())
return
}
library.Href = urlString + strconv.FormatUint(uint64(library.ID), 10)
respondWithJSON(w, http.StatusCreated, library)
}
The complete Library.Create(http.Handler) controller method is shown exactly as it has been generated.
Each section of the method is broken down in the following subsets of commented code:
// declare a local variable of struct type models.Library to hold the decoded
// JSON body provided in the request.Body.
var l models.Library
// create a new JSON decoder passing in the request.Body
decoder := json.NewDecoder(r.Body)
// call the Decoder.Decode(interface{}) method passing a reference to the
// locally declared models.Library struct 'l'. if the decoder is able to
// decode the JSON contained in the request.Body, the member fields of 'l'
// will be populated. if the decoder fails to parse and map the incoming
// JSON to the models.Library struct, it will return an error. The problem
// will be logged to stdout (for now) on the server-instance, and a response
// conforming to the http.Handler interface will be constructed and passed
// back to the router. if the JSON was parsed closed upon exit of the method.
if err := decoder.Decode(&l); err != nil {
log.Println("Library Create:", err)
respondWithError(w, http.StatusBadRequest, "libraryc: Invalid request payload")
return
}
defer r.Body.Close()
// fill the model with the parsed content of the JSON body. this step looks
// redundant, but can be thought of as a way to separate the incoming data
// from the response. going forward from this point, 'l' is ignored and
// all data transformation occurs on the 'library' variable.
library := models.Library{
Name: l.Name,
City: l.City,
}
// build a base urlString for the JSON Body self-referencing Href tag
urlString := buildHrefStringFromCRUDReq(r, true)
// call the Create method on the library model. each controller contains an
// instance of the Service for it's respective entity. the Create method on
// the service is called, passing a reference to the 'library' data structure.
// recall that the Service for an entity provides the link to that entity's
// model-layer by way of the entity's validator. lc.ls.Create(&library) will
// result in a call the model Validator Create() method for the Library
// entity, and in-turn, call to the enitity's model.Create() method where
// the data will be passed to the ORM-layer. if the Create() call returns
// an error, the problem will be logged to stdout (for now) on the server-
// instance, and a response conforming to the http.Handler interface will be
// constructed and passed back to the router.
err := lc.ls.Create(&library)
if err != nil {
log.Println("Library Create:", err)
respondWithError(w, http.StatusBadRequest, err.Error())
return
}
// if the call to the model-layer was successful, it indicates that a new
// Library entity was created in the DBMS. the 'library' reference passsed
// to the Create() method(s) in the model-layer will now contiain the new
// Library's information. first, the ID for the new Library will be added
// to the urlString and assigned to the library struct's Href member field.
// Href is another injected field in the entity and fullfills the purpose
// of providing a direct URI for the returned entity. finally the populated
// 'library' struct is formatted as a JSON response and passed back to the
// router along with an http status-code indicating success.
library.Href = urlString + strconv.FormatUint(uint64(library.ID), 10)
respondWithJSON(w, http.StatusCreated, library)
}
The controllers folder also contains an ‘ext’ sub-directory which is used to hold the interface definitions for controller extension-points as well as the associated empty implementation for each entity. See the Controller Extension Points section of this document for more details.