Goを使って簡単なMVCのWebフレームワークを書く


Bingo
Golang中国語ネットで初登場
これは私が最近書き始めたものです.
PHPから回ってきたばかりで、Goの特性についてはまだよく分からないので、ginを適用してみました.いいと思いますが、Laravelほど便利ではありません.
だから車輪を作ってみたい...en .... みそにする
bingoはgo言語に基づく軽量レベルのAPIフレームワークでありrestfulAPIの構築に専念している.
GitHubアドレス:silsuer/bingo
最近私は多くの修正をして、gayhubの上のはこの文章ととても合わないで、だからここで更にcommitのリンクを貼ってここをクリックします~
ディレクトリ構造
  • app Webサイト関連コード
  • を配置
  • core配置フレームコアコードa
  • vendorサードパーティライブラリを配置しglideを使用してサードパーティライブラリ
  • を管理
  • public配置htmlコード
  • 開発プロセス
    goのnetパッケージは極めて使いやすく、それを使ってフレームワークを開発するのも極めて速い(phpフレームワークを書くよりも速いなんて...)
    まずmain関数を決定し、main関数で構造体をインスタンス化し、Run関数を呼び出せばよい.
    操作:
    func main() {
      // new   bingo  ,  bingo.Run()  
      //       ,    index.html ,      
      //   env  
        bingo := new(core.Bingo)
        bingo.Run(":12345")
     }
    

    次にbingoファイルを書きます.
     func (b *Bingo) Run(port string) {
         //        ,     ,       http  
     
         //           ,      、      
         //    router ,        router    json  ,    json    ,    
         //    env   config         ,    
         //       ,      ,       ,  http   
         http.ListenAndServe(port, bin)
         // TODO          
     }
    
    Run関数は非常に簡単で、1行のコードしかありません.Httpサーバを開き、受信したポートを傍受します.
    私たちは自分でいろいろなルートを制御しなければならないので、netパッケージに付属しているhttpサービスを使うことができません.ネット上には多くの原理がはっきりしています.
    Muxというルータインタフェースを実現するためにServeHTTPメソッドを実装する必要があるので、ServeHttpメソッドをもう1つ書きます.
        func (b *Bingo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
            flag := false   //                  
            //    http        ,     ,     URL,            
            params := []reflect.Value{reflect.ValueOf(w), reflect.ValueOf(r)}
            for _, v := range RoutesList {
                //      ,            ,         
                //     ,           
                if r.URL.Path == v.path && r.Method == v.method {
                      flag = true   //         ,         
                      
                    //TODO          ,                       
        
                    //              ,    ,    
                    for _, m := range v.middleware {
                        if mid, ok := MiddlewareMap[m]; ok { //             
                            rmid := reflect.ValueOf(mid)
                            params = rmid.MethodByName("Handle").Call(params) //      ,  values  
                            //          ,         
                            str := rmid.Elem().FieldByName("ResString").String()
                            if str != "" {
                                status := rmid.Elem().FieldByName("Status").Int()
                                //      ,     ,    500  
                                if status == 0 {
                                    status = 500
                                }
                                w.WriteHeader(int(status))
                                fmt.Fprint(w,str)
                                return
                            }
                        }
                    }
                    //     ,      
                    //              
                    if d, ok := ControllerMap[v.controller]; ok { //     c    ,  c      
                        reflect.ValueOf(d).MethodByName(v.function).Call(params)
                    }
                    //       
                    return
                }
            }
        
            //              ,         
            if !flag {
                 //         
                 http.ServeFile(w,r,GetPublicPath()+ r.URL.Path)
            }
            return
        }
    

    ブラウザがアクセスする異なるURLに基づいて,異なるコントローラやミドルウェアの構造体を反射することによって得,
    対応するメソッドを呼び出し、アクセスするURLが定義されていなければ、静的フォルダに行って探し、見つかったら静的ファイルを出力し、そうでなければ404ページを出力します.
    (P.S.は無状態のAPI高速開発フレームワークを実現するため、テンプレートレンダリングを必要とせず、すべてのデータがajaxを介してページに転送される)
    この関数ではMiddlewareMap[m]およびControllerMap[m]を使用していることに気づきました.これはミドルウェアおよびコントローラのmapで、プログラムの初期化時にメモリに格納されます.
    具体的な定義は次のとおりです.
        //                
        //    map
        var ControllerMap map[string]interface{}
        //    map
        var MiddlewareMap map[string]interface{}
        
        func init()  {
            ControllerMap = make(map[string]interface{})
            MiddlewareMap = make(map[string]interface{})
            //     map                  ,                  
            //      
            MiddlewareMap["WebMiddleware"] =&middleware.WebMiddleware{}
        
            //     
            ControllerMap["Controller"] = &controller.Controller{}
        }
    
    

    ここではapp/controllerおよびmiddlewareパッケージの構造体を使用します.ルーティングが完了すると、リクエストされたパスがここのmapに対応します.routerでルーティングコードを解析する方法を見てみましょう.
    type route struct {
    	path       string   //   
    	target     string   //          Controller@index      
    	method     string   //       get post     
    	alias      string   //      
    	middleware []string //      
    	controller string   //      
    	function   string   //             
    }
    
    type route_group struct {
    	root_path   string   //   
    	root_target string   //          Controller@index      
    	alias       string   //      
    	middleware  []string //      
    	routes      []route  //      
    }
    
    var Routes []route             //        
    var RoutesGroups []route_group //      
    var RoutesList []route         //       
    var R interface{}
    
    func init() {
    	//      ,      
    	//       ,              ,        ,         
    	routes_path := GetRoutesPath()
    	dir_list, err := ioutil.ReadDir(routes_path)
    	Check(err)
    	//   dir list            json  ,           
    	for _, v := range dir_list {
    		fmt.Println("        ........" + v.Name())
    		//       ,   json,       
    		content, err := FileGetContents(routes_path + "/" + v.Name())
    		Check(err)
    		err = json.Unmarshal([]byte(content), &R)
    		Check(err)
    		//     R,           
    		parse(R)
    	}
    }
    

    コンパイルを準備する段階でinit関数を実行し、ルーティングフォルダの下のすべてのルーティングリストを取得します.jsonフォーマットを使用してルーティングを組織し、解析したデータをRoutesListリストに格納します.
    解析コードは次のとおりです
    
    func parse(r interface{}) {
    	//    r            
    	m := r.(map[string]interface{})
    	//newRoute := route{}
    	for k, v := range m {
    		if k == "Routes" {
    			//       
    			parseRoutes(v)
    		}
    		if k == "RoutesGroups" {
    			//      
    			parseRoutesGroups(v)
    		}
    	}
    
    }
    
    //   json           
    func parseRoutes(r interface{}) {
    	m := r.([]interface{})
    	for _, v := range m {
    		// v         
    		simpleRoute := v.(map[string]interface{})
    		//          
    		newRoute := route{}
    		for kk, vv := range simpleRoute {
    			switch kk {
    			case "Route":
    				newRoute.path = vv.(string)
    				break
    			case "Target":
    				newRoute.target = vv.(string)
    				break
    			case "Method":
    				newRoute.method = vv.(string)
    				break
    			case "Alias":
    				newRoute.alias = vv.(string)
    				break
    			case "Middleware":
    				//newRoute.middleware = vv.([])
    				var mdw []string
    				vvm := vv.([]interface{})
    				for _, vvv := range vvm {
    					mdw = append(mdw, vvv.(string))
    				}
    				newRoute.middleware = mdw
    				break
    			default:
    				break
    			}
    		}
    
    		//  target         
    		cf := strings.Split(newRoute.target,"@")
    		if len(cf)==2 {
    			newRoute.controller = cf[0]
    			newRoute.function = cf[1]
    		}else{
    			fmt.Println("Target    !"+newRoute.target)
    			return
    		}
    
    		//        ,         ,         
    
    		Routes = append(Routes, newRoute)
    		RoutesList = append(RoutesList, newRoute)
    	}
    }
    
    func parseRoutesGroups(r interface{}) {
    	//      
    	m := r.([]interface{})
    	for _, v := range m {
    		group := v.(map[string]interface{})
    		for kk, vv := range group {
    			//           
    			var newGroup route_group
    			switch kk {
    			case "RootRoute":
    				newGroup.root_path = vv.(string)
    				break
    			case "RootTarget":
    				newGroup.root_target = vv.(string)
    				break
    			case "Middleware":
    				var mdw []string
    				vvm := vv.([]interface{})
    				for _, vvv := range vvm {
    					mdw = append(mdw, vvv.(string))
    				}
    				newGroup.middleware = mdw
    				break
    			case "Routes":
    				//              ,         parseRoutes  ,                
    				rs := parseRootRoute(group)
    				newGroup.routes = rs
    				break
    			default:
    				break
    			}
    			//    group      
    			RoutesGroups  = append(RoutesGroups,newGroup)
    		}
    	}
    }
    
    //                           inteface  ,           
    //         ,           
    func parseRootRoute(group map[string]interface{}) []route {
    	//              ,       
    	var tmpRoutes []route  //         
    	var route_root_path string
    	var target_root_path string
    	var public_middleware []string
    	for k, v := range group {
    		if k == "RootRoute" {
    			route_root_path = v.(string)
    		}
    		if k == "RootTarget" {
    			target_root_path = v.(string)
    		}
    		if k=="Middleware" {
    			vvm := v.([]interface{})
    			for _, vvv := range vvm {
    				public_middleware = append(public_middleware, vvv.(string))
    			}
    		}
    	}
    
    	//       
    	for k, s := range group {
    		if k == "Routes" {
    			m := s.([]interface{})
    			for _, v := range m {
    				// v         
    				simpleRoute := v.(map[string]interface{})
    				//          
    				newRoute := route{}
    				for kk, vv := range simpleRoute {
    					switch kk {
    					case "Route":
    						newRoute.path = route_root_path+ vv.(string)
    						break
    					case "Target":
    						newRoute.target = target_root_path+ vv.(string)
    						break
    					case "Method":
    						newRoute.method = vv.(string)
    						break
    					case "Alias":
    						newRoute.alias = vv.(string)
    						break
    					case "Middleware":
    						vvm := vv.([]interface{})
    						for _, vvv := range vvm {
    							newRoute.middleware = append(public_middleware,vvv.(string))//                  
    						}
    
    						break
    					default:
    						break
    					}
    				}
    				//  target         
    				cf := strings.Split(newRoute.target,"@")
    				if len(cf)==2 {
    					newRoute.controller = cf[0]
    					newRoute.function = cf[1]
    				}else{
    					fmt.Println("Target    !"+newRoute.target)
    					os.Exit(2)
    				}
    				//        ,       ,           ,       
    				RoutesList = append(RoutesList, newRoute)
    				tmpRoutes = append(tmpRoutes,newRoute)
    			}
    		}
    	}
       return tmpRoutes
    }
    

    jsonファイルを解析してルーティングリストを取得し、上のSerbeHttpファイルでルーティングリストと比較できます.
    これにより,簡単なGOを用いたWebフレームワークを実現した.
    現在、静的ページの表示とAPI応答のみが可能
    次に実現するのは、
  • 便利なORM
  • コントローラおよびミドルウェアを素早く追加するコマンド
  • を作成する.
  • データベース移行を実現
  • tokenベースのAPI認証
  • を実現
  • データキャッシュ
  • キュー
  • フック
  • 便利なファイルアップロード/ストレージ機能
  • starを求めて、PR~ハハハ(silsuer/bingo)を歓迎します
    これはずっと前に书いたもので、今はフレームワークがたくさん更新されていますが、原生开発にとってはまだ参考価値があるのではないでしょうか.