Golang中的REST应用程序文件夹结构

I'm just getting started with Go and as a first project I decided to make a simple REST API server. Unfortunately, I've hit a problem as I can't seem to find the best way to structure my project.

So far, I'm using the same structure as a Rails project meaning having controllers and models in their own folders, but it doesn't as idiomatic as go code should be.

Does anyone have any advice on how to properly structure a project?

Keep your project in one folder. Go treats sub-folders as separate packages. Here's an example CMS application: https://github.com/calebdoxsey/tutorials/tree/master/appengine/cms.

I created a file that defines the routes in an init function: (using httprouter)

router.GET("/api/documents/:id", serveDocumentsGet)

Basically each endpoint gets its own function:

func serveDocumentsGet(res http.ResponseWriter, req *http.Request, params httprouter.Params) {
    serveAPI(res, req, func() interface{} {
        ctx := appengine.NewContext(req)
        session, _ := sessionStore.Get(req, "session")
        email, ok := session.Values["email"].(string)
        if !ok {
            return HTTPError{403, "access denied"}
        }
        userKey := datastore.NewKey(ctx, "User", email, 0, nil)
        docKey := datastore.NewKey(ctx, "Document", params.ByName("id"), 0, userKey)
        var document Document
        err := datastore.Get(ctx, docKey, &document)
        if err != nil {
            return err
        }
        return document
    })
}

serveAPI is just a convenience function to handle json encoding and errors:

func serveAPI(res http.ResponseWriter, req *http.Request, handler func() interface{}) {
    res.Header().Set("Content-Type", "application/json")

    result := handler()
    if err, ok := result.(HTTPError); ok {
        res.WriteHeader(err.Code)
        json.NewEncoder(res).Encode(map[string]string{
            "error": err.Message,
        })
    } else if err, ok := result.(error); ok {
        res.WriteHeader(500)
        json.NewEncoder(res).Encode(map[string]string{
            "error": err.Error(),
        })
    } else if rc, ok := result.(io.ReadCloser); ok {
        io.Copy(res, rc)
        rc.Close()
    } else {
        json.NewEncoder(res).Encode(result)
    }
}

Basically I try to avoid magic so all the endpoints are pretty explicit.

This gives you controllers and models (for more complex models I break them into separate files... see the User example).

For views you can use the built-in go template package. Create a folder (maybe static/tpl/yourtemplate.gohtml) and use template.ParseFiles and tpl.Execute to render them. (once again this is explicit... no magic)

But increasingly I find that most of my views are done client-side with javascript. If you do this then all you have to do is serve files:

static := http.FileServer(http.Dir("static"))
router.GET("/static/*filepath", func(res http.ResponseWriter, req *http.Request, params httprouter.Params) {
    filepath := params.ByName("filepath")
    req.URL.Path = filepath
    static.ServeHTTP(res, req)
})

It's probably do-able, but because of static compilation you're going to have a bit of a mess if you want a lot of Rails' automagic for figuring out routes.

I'd probably just have a models package with all the database models, and then something based on Gorilla's mux to bind some routes to some actions.

If things are getting complicated you can try a larger framework like Revel, which might be more suitable for a larger MVC application.