一个Go(Golang)语言轻量级MVC框架:utron
utron是一个Go(Golang)语言轻量级MVC框架。用于构建快速,可扩展和健壮的数据库驱动Web应用。
特性
- Postgres, MySQL和Foundation 数据库支持.
- 模块化(你可以选择要使用的组件)
- 中间件的支持。所有爱alice兼容的中间件开箱的。
- Gopher spirit (write golang, use all the golang libraries you like)
- Lightweight. Only MVC.
- Multiple configuration files support (currently json, yaml and toml)
Overview
utronis a lightweight MVC framework. It is based on the premise of simplicity, relevance and elegancy.
-
Simplicity. The design is simple, easy to understand and less layers from the standard library. It is in the spirit of the project that, users should understand the whole framework in a single day.
-
Relevance.utrondoesn't assume anything. We focus on things that matter, this way, we are able to achieve good maintenance and keeping the system well organized, well planned and sweet like.
-
Elegancy.utronuses golang best practises. We are not afraid of heights, its just that we need a parachute in our backpack. The source code is heavily documented, any trick should be well explained and well tested.
Motivation
After two years of playing with golang. I have looked on some of my projects and asked myself, how golang is that?
So,utronis my reimagination of lightweight MVC, that maintains the golang spirit, and works seamlessly with the current libraries.
Installation
$ go get github.com/gernest/utron
The MVC
There is nothing revolutionary about MVC thatutronbrings on the table.
-
M is for models, it is the data structures that helps in data persistance, utron uses an already established Object Relational Mapper for golang gorm. So if you are familiar with gorm then you are good on the M part.
-
V is for Views. Views are templates, that renders the final output.utronuses golang standard templates. so you don't have to learn anything new, just the text/template package to master views.
-
C is for controllers. This is where the aplication logic stands, in order to achieve modularity, there are some facts that utron enforces to controllers. This subject is explained in more details below.
With the power of composition and inheriance,utronachieves a beautiful MVC workflow. I recommend you read the source code, it is well documented so as to demistify any magical unicorns.
We will create a TODO List application inutronto explore all components that makesutronMVC tick the source code of the final application is included in this repository and can be found here utron todoMVC
TODO list application withutron
Project structure
This is the structure of thetodolist application that will showcase how you can build web apps withutron.
todo ├── config │ ├── app.json │ ├── app.toml │ └── app.yml ├── controllers │ └── todo.go ├── models │ └── todo.go ├── static │ └── todo.css ├── views │ ├── error.html │ └── index.html └── main.go 5 directories, 9 files
I have included three configuration files for clarity, but you are better off with just one.
Configurations
utron support yaml, json and toml configurations files. In our todo app, we put the configuration files in the config directory. I have included all three formats for clarity, you can be just fine with either one of them.
utronsearches for a file namedapp.json, orapp.ymlorapp.tomlin the config directory. The first to be found is the one to be used.
This is the content ofconfig/app.jsonfile
{ "app_name": "utron web app", "base_url": "http://localhost:8090", "port": 8090, "verbose": false, "static_dir": "static", "view_dir": "views", "database": "postgres", "database_conn": "postgres://postgres:postgres@localhost/todo" }
setting | details |
---|---|
app_name | application name |
base_url | the base url to use in your views |
port | port number where the server will be listening to |
verbose | true value will make every state information logged to stdout |
static_dir | directory to serve static files e.g images, js or css |
view_dir | directory to look for views |
database | the name of the database you use, e.g postgres, mysql, foundation |
database_conn | connection string to your database |
If you haven't specified explicitly the location of the configuration directory, then it defaults to the directory namedconfigin the current working directory.
Models
utronuses gorm library as its Object Relational Mapper. So, you won't need to learn anything fancy. In our todo app, we need to defineTodomodel that will be used to store our todo details.
In the filemodels/todo.gowe define our todo model like this
package models import ( "time" "github.com/gernest/utron" ) type Todo struct { ID int `schema: "-"` Body string `schema:"body"` CreatedAt time.Time `schema:"-"` UpdatedAt time.Time `schema:"-"` } func init() { utron.RegisterModels(&Todo{}) }
Notice that, we need to register our model, by callingutron.RegisterModels(&Todo{})in theinitfunction otherwiseutronwon't be aware of the model.
utronwill automatically create the tabletodosif it doesn't exist yet.
Don't be confused by theschematag, I just added them since we will use schema package to decode form values(this has nothing to do withutron, you can use whatever form library you fancy)
Controllers
utroncontrollers are structs that implement theControllerinterface. To help makeutronusable,utronprovides aBaseControllerwhich implements theControllerinterface and offers additional conveniences to help in composing reusable code.
You get all the benefits ofBaseControllerby embeding it in your struct. ourTODOController is in thecontroller/todo.go
package controllers import ( "net/http" "strconv" "github.com/gernest/utron" "github.com/gernest/utron/fixtures/todo/models" "github.com/gorilla/schema" ) var decoder = schema.NewDecoder() type TODO struct { *utron.BaseController Routes []string } func (t *TODO) Home() { todos := []*models.Todo{} t.Ctx.DB.Order("created_at desc").Find(&todos) t.Ctx.Data["List"] = todos t.Ctx.Template = "index" t.HTML(http.StatusOK) } func (t *TODO) Create() { todo := &models.Todo{} req := t.Ctx.Request() req.ParseForm() if err := decoder.Decode(todo, req.PostForm); err != nil { t.Ctx.Data["Message"] = err.Error() t.Ctx.Template = "error" t.HTML(http.StatusInternalServerError) return } t.Ctx.DB.Create(todo) t.Ctx.Redirect("/", http.StatusFound) } func (t *TODO) Delete() { todoID := t.Ctx.Params["id"] ID, err := strconv.Atoi(todoID) if err != nil { t.Ctx.Data["Message"] = err.Error() t.Ctx.Template = "error" t.HTML(http.StatusInternalServerError) return } t.Ctx.DB.Delete(&models.Todo{ID: ID}) t.Ctx.Redirect("/", http.StatusFound) } func NewTDDO() *TODO { return &TODO{ Routes: []string{ "get;/;Home", "post;/create;Create", "get;/delete/{id};Delete", }, } } func init() { utron.RegisterController(NewTDDO()) }
Note we registered our controller by callingutron.RegisterController(NewTDDO())in theinitfunction so as to makeutronaware of our controller. See Routing section below for more explanation of what the controller is doing.