Go での REST API のテスト: Go の標(biāo)準(zhǔn)テスト ライブラリを使用した単體テストと統(tǒng)合テストのガイド
Nov 17, 2024 am 01:33 AM導(dǎo)入
この記事では、単體テストと統(tǒng)合テストを使用して、golang で REST API を作成する際の開発エクスペリエンスを向上させる方法を説明します。
単體テストは、アプリケーションの最小の個(gè)々の部分の機(jī)能を検証するように設(shè)計(jì)されており、多くの場合、単一の関數(shù)またはメソッドに焦點(diǎn)を當(dāng)てています。これらのテストは、各コンポーネントが単獨(dú)で期待どおりに動(dòng)作することを確認(rèn)するために、コードの他の部分から分離して実行されます。
一方、統(tǒng)合テスト は、アプリケーションのさまざまなモジュールやコンポーネントがどのように連攜して動(dòng)作するかを評価します。この記事では、Go アプリケーションの統(tǒng)合テストに焦點(diǎn)を當(dāng)て、特に SQL クエリを正常に作成して実行することで、PostgreSQL データベースと正しく対話することを確認(rèn)します。
この記事は、読者が golang と golang で REST API を作成する方法に精通していることを前提としています。主な焦點(diǎn)は、ルートのテスト (単體テスト) の作成と SQL クエリ関數(shù)のテスト (統(tǒng)合テスト) です。參考として、github にアクセスしてください。プロジェクトを見てみましょう。
セットアップ
上にリンクしたものと同様のプロジェクトを設(shè)定していると仮定すると、次のようなフォルダー構(gòu)造になります
test_project |__cmd |__api |__api.go |__main.go |__db |___seed.go |__internal |___db |___db.go |___services |___records |___routes_test.go |___routes.go |___store_test.go |___store.go |___user |___routes_test.go |___routes.go |___store_test.go |___store.go |__test_data |__docker-compose.yml |__Dockerfile |__Makefile
golang でのテストは、テストの作成に必要なツールを提供する組み込みのテスト パッケージがあるため、これまでに遭遇した可能性のある他の言語と比較して簡単です。
テスト ファイルには _test.go という接尾辭が付いた名前が付けられ、go test コマンドを?qū)g行するときに、このファイルを?qū)g行対象として指定できます。
プロジェクトのエントリポイントは、cmd フォルダーにある main.go ファイルです
// main.go package main import ( "log" "finance-crud-app/cmd/api" "finance-crud-app/internal/db" "github.com/gorilla/mux" "github.com/jmoiron/sqlx" _ "github.com/lib/pq" ) type Server struct { db *sqlx.DB mux *mux.Router } func NewServer(db *sqlx.DB, mux *mux.Router) *Server { return &Server{ db: db, mux: mux, } } func main() { connStr := "postgres://postgres:Password123@localhost:5432/crud_db?sslmode=disable" dbconn, err := db.NewPGStorage(connStr) if err != nil { log.Fatal(err) } defer dbconn.Close() server := api.NewAPIServer(":8085", dbconn) if err := server.Run(); err != nil { log.Fatal(err) } }
コードから、データベース接続とポート番號を渡して新しい API サーバーを作成していることがわかります。サーバーを作成したら、指定されたポートでサーバーを?qū)g行します。
NewAPIServer コマンドは、api.go ファイルから取得されます。
// api.go package api import ( "finance-crud-app/internal/services/records" "finance-crud-app/internal/services/user" "log" "net/http" "github.com/gorilla/mux" "github.com/jmoiron/sqlx" ) type APIServer struct { addr string db *sqlx.DB } func NewAPIServer(addr string, db *sqlx.DB) *APIServer { return &APIServer{ addr: addr, db: db, } } func (s *APIServer) Run() error { router := mux.NewRouter() subrouter := router.PathPrefix("/api/v1").Subrouter() userStore := user.NewStore(s.db) userHandler := user.NewHandler(userStore) userHandler.RegisterRoutes(subrouter) recordsStore := records.NewStore(s.db) recordsHandler := records.NewHandler(recordsStore, userStore) recordsHandler.RegisterRoutes(subrouter) log.Println("Listening on", s.addr) return http.ListenAndServe(s.addr, router) }
この API では、http ルーターとして mux を使用しています。
結(jié)合テスト
ユーザー エンティティに関連する SQL クエリを処理するユーザー ストア構(gòu)造體があります。
// store.go package user import ( "errors" "finance-crud-app/internal/types" "fmt" "log" "github.com/jmoiron/sqlx" ) var ( CreateUserError = errors.New("cannot create user") RetrieveUserError = errors.New("cannot retrieve user") DeleteUserError = errors.New("cannot delete user") ) type Store struct { db *sqlx.DB } func NewStore(db *sqlx.DB) *Store { return &Store{db: db} } func (s *Store) CreateUser(user types.User) (user_id int, err error) { query := ` INSERT INTO users (firstName, lastName, email, password) VALUES (, , , ) RETURNING id` var userId int err = s.db.QueryRow(query, user.FirstName, user.LastName, user.Email, user.Password).Scan(&userId) if err != nil { return -1, CreateUserError } return userId, nil } func (s *Store) GetUserByEmail(email string) (types.User, error) { var user types.User err := s.db.Get(&user, "SELECT * FROM users WHERE email = ", email) if err != nil { return types.User{}, RetrieveUserError } if user.ID == 0 { log.Fatalf("user not found") return types.User{}, RetrieveUserError } return user, nil } func (s *Store) GetUserByID(id int) (*types.User, error) { var user types.User err := s.db.Get(&user, "SELECT * FROM users WHERE id = ", id) if err != nil { return nil, RetrieveUserError } if user.ID == 0 { return nil, fmt.Errorf("user not found") } return &user, nil } func (s *Store) DeleteUser(email string) error { user, err := s.GetUserByEmail(email) if err != nil { return DeleteUserError } // delete user records first _, err = s.db.Exec("DELETE FROM records WHERE userid = ", user.ID) if err != nil { return DeleteUserError } _, err = s.db.Exec("DELETE FROM users WHERE email = ", email) if err != nil { return DeleteUserError } return nil }
上記のファイルには、3 つのポインター レシーバー メソッドがあります。
- ユーザーの作成
- GetUserByEmail
- GetUserById
これらのメソッドが機(jī)能を?qū)g行するには、外部システム (この場合は Postgres DB) と対話する必要があります。
このメソッドをテストするには、まずstore_test.go ファイルを作成します。 Go では通常、テスト対象のファイルにちなんでテスト ファイルに名前を付け、サフィックス _test.go .
を追加します。
// store_test.go package user_test import ( "finance-crud-app/internal/db" "finance-crud-app/internal/services/user" "finance-crud-app/internal/types" "log" "os" "testing" "github.com/jmoiron/sqlx" _ "github.com/lib/pq" ) var ( userTestStore *user.Store testDB *sqlx.DB ) func TestMain(m *testing.M) { // database ConnStr := "postgres://postgres:Password123@localhost:5432/crud_db?sslmode=disable" testDB, err := db.NewPGStorage(ConnStr) if err != nil { log.Fatalf("could not connect %v", err) } defer testDB.Close() userTestStore = user.NewStore(testDB) code := m.Run() os.Exit(code) } func TestCreateUser(t *testing.T) { test_data := map[string]struct { user types.User result any }{ "should PASS valid user email used": { user: types.User{ FirstName: "testfirsjjlkjt-1", LastName: "testlastkjh-1", Email: "validuser@email.com", Password: "00000000", }, result: nil, }, "should FAIL invalid user email used": { user: types.User{ FirstName: "testFirstName1", LastName: "testLastName1", Email: "test1@email.com", Password: "800890", }, result: user.CreateUserError, }, } for name, tc := range test_data { t.Run(name, func(t *testing.T) { value, got := userTestStore.CreateUser(tc.user) if got != tc.result { t.Errorf("test fail expected %v got %v instead and value %v", tc.result, got, value) } }) } t.Cleanup(func() { err := userTestStore.DeleteUser("validuser@email.com") if err != nil { t.Errorf("could not delete user %v got error %v", "validuser@email.com", err) } }) } func TestGetUserByEmail(t *testing.T) { test_data := map[string]struct { email string result any }{ "should pass valid user email address used": { email: "test1@email.com", result: nil, }, "should fail invalid user email address used": { email: "validuser@email.com", result: user.RetrieveUserError, }, } for name, tc := range test_data { got, err := userTestStore.GetUserByEmail(tc.email) if err != tc.result { t.Errorf("test fail expected %v instead got %v", name, got) } } } func TestGetUserById(t *testing.T) { testUserId, err := userTestStore.CreateUser(types.User{ FirstName: "userbyid", LastName: "userbylast", Email: "unique_email", Password: "unique_password", }) if err != nil { log.Panicf("got %v when creating testuser", testUserId) } test_data := map[string]struct { user_id int result any }{ "should pass valid user id used": { user_id: testUserId, result: nil, }, "should fail invalid user id used": { user_id: 0, result: user.RetrieveUserError, }, } for name, tc := range test_data { t.Run(name, func(t *testing.T) { _, got := userTestStore.GetUserByID(tc.user_id) if got != tc.result { t.Errorf("error retrieving user by id got %v want %v", got, tc.result) } }) } t.Cleanup(func() { err := userTestStore.DeleteUser("unique_email") if err != nil { t.Errorf("could not delete user %v got error %v", "unique_email", err) } }) } func TestDeleteUser(t *testing.T) { testUserId, err := userTestStore.CreateUser(types.User{ FirstName: "userbyid", LastName: "userbylast", Email: "delete_user@email.com", Password: "unique_password", }) if err != nil { log.Panicf("got %v when creating testuser", testUserId) } test_data := map[string]struct { user_email string result error }{ "should pass user email address used": { user_email: "delete_user@email.com", result: nil, }, } for name, tc := range test_data { t.Run(name, func(t *testing.T) { err = userTestStore.DeleteUser(tc.user_email) if err != tc.result { t.Errorf("error deletig user got %v instead of %v", err, tc.result) } }) } t.Cleanup(func() { err := userTestStore.DeleteUser("delete_user@email.com") if err != nil { log.Printf("could not delete user %v got error %v", "delete_user@email.com", err) } }) }
ファイル全體を見て、各セクションの內(nèi)容を見てみましょう。
最初のアクションは、変數(shù) userTestStore と testDB を宣言することです。これらの変數(shù)は、それぞれユーザー ストアとデータベースへのポインターを格納するために使用されます。これらをグローバル ファイル スコープで宣言した理由は、テスト ファイル內(nèi)のすべての関數(shù)がポインターにアクセスできるようにするためです。
TestMain 関數(shù)を使用すると、メインのテストを?qū)g行する前にいくつかの設(shè)定アクションを?qū)g行できます。最初に postgres ストアに接続し、ポインターをグローバル変數(shù)に保存します。
そのポインターを使用して、接続しようとしている SQL クエリを?qū)g行するために使用する userTestStore を作成しました。
defer testDB.Close() はテスト完了後にデータベース接続を閉じます
code := m.Run() は、戻って終了する前に殘りのテスト関數(shù)を?qū)g行します。
TestCreateUser 関數(shù)は、create_user 関數(shù)のテストを処理します。私たちの目標(biāo)は、一意の電子メールが渡された場合に関數(shù)がユーザーを作成するかどうかをテストすることです。また、一意でない電子メールがすでに別のユーザーの作成に使用されている場合、関數(shù)はユーザーを作成できないようにする必要があります。
まず、両方のケースのシナリオをテストするために使用するテスト データを作成します。
test_project |__cmd |__api |__api.go |__main.go |__db |___seed.go |__internal |___db |___db.go |___services |___records |___routes_test.go |___routes.go |___store_test.go |___store.go |___user |___routes_test.go |___routes.go |___store_test.go |___store.go |__test_data |__docker-compose.yml |__Dockerfile |__Makefile
テスト日付をパラメータとして create_user 関數(shù)を?qū)g行してマップをループし、返された値が期待する結(jié)果と同じかどうかを比較します
// main.go package main import ( "log" "finance-crud-app/cmd/api" "finance-crud-app/internal/db" "github.com/gorilla/mux" "github.com/jmoiron/sqlx" _ "github.com/lib/pq" ) type Server struct { db *sqlx.DB mux *mux.Router } func NewServer(db *sqlx.DB, mux *mux.Router) *Server { return &Server{ db: db, mux: mux, } } func main() { connStr := "postgres://postgres:Password123@localhost:5432/crud_db?sslmode=disable" dbconn, err := db.NewPGStorage(connStr) if err != nil { log.Fatal(err) } defer dbconn.Close() server := api.NewAPIServer(":8085", dbconn) if err := server.Run(); err != nil { log.Fatal(err) } }
返された結(jié)果が期待した結(jié)果と異なる場合、テストは失敗します
この関數(shù)の最後の部分では、組み込みのテスト パッケージ関數(shù) Cleanup を使用します。この関數(shù)は、テスト內(nèi)のすべての関數(shù)がすでに実行されたときに呼び出される関數(shù)を登録しました。ここの例では、このテスト関數(shù)の実行中に使用されたユーザー データをクリアする関數(shù)を使用しています。
単體テスト
単體テストでは、API のルート ハンドラーをテストします。この場合、ルートはユーザー エンティティに関連します。以下を參照してください。
// api.go package api import ( "finance-crud-app/internal/services/records" "finance-crud-app/internal/services/user" "log" "net/http" "github.com/gorilla/mux" "github.com/jmoiron/sqlx" ) type APIServer struct { addr string db *sqlx.DB } func NewAPIServer(addr string, db *sqlx.DB) *APIServer { return &APIServer{ addr: addr, db: db, } } func (s *APIServer) Run() error { router := mux.NewRouter() subrouter := router.PathPrefix("/api/v1").Subrouter() userStore := user.NewStore(s.db) userHandler := user.NewHandler(userStore) userHandler.RegisterRoutes(subrouter) recordsStore := records.NewStore(s.db) recordsHandler := records.NewHandler(recordsStore, userStore) recordsHandler.RegisterRoutes(subrouter) log.Println("Listening on", s.addr) return http.ListenAndServe(s.addr, router) }
ここにはテストしたい関數(shù)が 3 つあります
- ハンドルログイン
- ハンドルレジスタ
- HandleGetUser
ハンドル取得ユーザー
このハンドラーの handleGetUser 関數(shù)は、HTTP リクエスト URL で提供されたユーザー ID に基づいてユーザーの詳細(xì)を取得します。まず、多重化ルーターを使用してリクエスト パス変數(shù)からユーザー ID を抽出します。ユーザー ID が欠落しているか無効 (非整數(shù)) の場合、400 Bad Request エラーが返されます。検証が完了すると、関數(shù)はデータ ストアの GetUserByID メソッドを呼び出してユーザー情報(bào)を取得します。取得中にエラーが発生した場合は、500 Internal Server Error が返されます。成功すると、200 OK ステータスで応答し、応答本文でユーザーの詳細(xì)を JSON として送信します。
前に述べたように、ハンドラー関數(shù)をテストするには、routes_test.go を作成する必要があります。以下の私のものを參照してください
test_project |__cmd |__api |__api.go |__main.go |__db |___seed.go |__internal |___db |___db.go |___services |___records |___routes_test.go |___routes.go |___store_test.go |___store.go |___user |___routes_test.go |___routes.go |___store_test.go |___store.go |__test_data |__docker-compose.yml |__Dockerfile |__Makefile
新しいハンドラー関數(shù)では、ハンドラー構(gòu)造體を作成するためのパラメーターとしてユーザー ストアが必要です。
実際のストアは必要ないため、モック構(gòu)造體を作成し、実際の構(gòu)造體の関數(shù)をモックするレシーバー関數(shù)を作成します。これを行うのは、ストア関數(shù)のテストを個(gè)別に処理しているため、ハンドラー テストでコードのその部分をテストする必要がないためです。
テスト関數(shù) TestGetUserHandler は 2 つのケース シナリオをテストします。1 つ目は、ユーザー ID を提供せずにユーザーを取得しようとするものです
// main.go package main import ( "log" "finance-crud-app/cmd/api" "finance-crud-app/internal/db" "github.com/gorilla/mux" "github.com/jmoiron/sqlx" _ "github.com/lib/pq" ) type Server struct { db *sqlx.DB mux *mux.Router } func NewServer(db *sqlx.DB, mux *mux.Router) *Server { return &Server{ db: db, mux: mux, } } func main() { connStr := "postgres://postgres:Password123@localhost:5432/crud_db?sslmode=disable" dbconn, err := db.NewPGStorage(connStr) if err != nil { log.Fatal(err) } defer dbconn.Close() server := api.NewAPIServer(":8085", dbconn) if err := server.Run(); err != nil { log.Fatal(err) } }
http リクエストが 400 ステータス コードで応答した場合、テストは合格すると予想されます。
2 番目のテスト ケース シナリオは、有効なユーザー ID を含む正しい URL を使用してユーザー情報(bào)を取得するケースです。このテスト ケースでは、ステータス コード 200 の応答が期待されました。そうでない場合、そのテストは失敗します。
// api.go package api import ( "finance-crud-app/internal/services/records" "finance-crud-app/internal/services/user" "log" "net/http" "github.com/gorilla/mux" "github.com/jmoiron/sqlx" ) type APIServer struct { addr string db *sqlx.DB } func NewAPIServer(addr string, db *sqlx.DB) *APIServer { return &APIServer{ addr: addr, db: db, } } func (s *APIServer) Run() error { router := mux.NewRouter() subrouter := router.PathPrefix("/api/v1").Subrouter() userStore := user.NewStore(s.db) userHandler := user.NewHandler(userStore) userHandler.RegisterRoutes(subrouter) recordsStore := records.NewStore(s.db) recordsHandler := records.NewHandler(recordsStore, userStore) recordsHandler.RegisterRoutes(subrouter) log.Println("Listening on", s.addr) return http.ListenAndServe(s.addr, router) }
結(jié)論
ルート ハンドラーのテストを作成することで、プロジェクトに単體テストを?qū)g裝することができました。コードの小さな単位のみをテストするためにモックを使用する方法を見てきました。 Postgresql DB と対話する機(jī)能の統(tǒng)合テストを開発できました。
プロジェクト コードを?qū)g際に使用したい場合は、ここから github からリポジトリをクローンしてください
以上がGo での REST API のテスト: Go の標(biāo)準(zhǔn)テスト ライブラリを使用した単體テストと統(tǒng)合テストのガイドの詳細(xì)內(nèi)容です。詳細(xì)については、PHP 中國語 Web サイトの他の関連記事を參照してください。

ホットAIツール

Undress AI Tool
脫衣畫像を無料で

Undresser.AI Undress
リアルなヌード寫真を作成する AI 搭載アプリ

AI Clothes Remover
寫真から衣服を削除するオンライン AI ツール。

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中國語版
中國語版、とても使いやすい

ゼンドスタジオ 13.0.1
強(qiáng)力な PHP 統(tǒng)合開発環(huán)境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

Golangは主にバックエンド開発に使用されますが、フロントエンドフィールドで間接的な役割を果たすこともできます。その設(shè)計(jì)目標(biāo)は、高性能、同時(shí)処理、システムレベルのプログラミングに焦點(diǎn)を當(dāng)てており、APIサーバー、マイクロサービス、分散システム、データベース操作、CLIツールなどのバックエンドアプリケーションの構(gòu)築に適しています。 GolangはWebフロントエンドの主流言語ではありませんが、Gopherjsを介してJavaScriptにコンパイルしたり、Tinygoを介してWebAssemblyで実行したり、テンプレートエンジンを備えたHTMLページを生成してフロントエンド開発に參加できます。ただし、最新のフロントエンド開発は、JavaScript/TypeScriptとそのエコシステムに依存する必要があります。したがって、Golangは、コアとして高性能バックエンドを備えたテクノロジースタック選択により適しています。

GOでGraphQlapiを構(gòu)築するには、GQLGenライブラリを使用して開発効率を向上させることをお?jiǎng)幛幛筏蓼埂?1.最初に、スキーマに基づいた自動(dòng)コード生成をサポートするGQLGENなどの適切なライブラリを選択します。 2。次に、graphqlschemaを定義し、投稿の種類やクエリメソッドの定義など、API構(gòu)造とクエリポータルを説明します。 3。次に、プロジェクトを初期化し、基本コードを生成して、リゾルバにビジネスロジックを?qū)g裝します。 4.最後に、graphqlhandlerをhttpserverに接続し、組み込みの遊び場を介してAPIをテストします。メモには、プロジェクトのメンテナンスを確保するためのフィールドネーミング仕様、エラー処理、パフォーマンスの最適化、セキュリティ設(shè)定が含まれます

GOをインストールするための鍵は、正しいバージョンを選択し、環(huán)境変數(shù)を構(gòu)成し、インストールを検証することです。 1.公式Webサイトにアクセスして、対応するシステムのインストールパッケージをダウンロードします。 Windowsは.msiファイルを使用し、macosは.pkgファイルを使用し、Linuxは.tar.gzファイルを使用し、 /usr /localディレクトリに解凍します。 2.環(huán)境変數(shù)を構(gòu)成し、linux/macOSで?/.bashrcまたは?/.zshrcを編集してパスとgopathを追加し、Windowsがシステムプロパティに移動(dòng)するパスを設(shè)定します。 3.政府コマンドを使用してインストールを確認(rèn)し、テストプログラムを?qū)g行してhello.goを?qū)g行して、編集と実行が正常であることを確認(rèn)します。プロセス全體のパス設(shè)定とループ

sync.waitgroupは、ゴルチンのグループがタスクを完了するのを待つために使用されます。そのコアは、3つの方法で協(xié)力することです。追加、完了、待機(jī)です。 1.ADD(n)待機(jī)するゴルチンの數(shù)を設(shè)定します。 2.done()は各ゴルチンの端で呼び出され、カウントは1つ減少します。 3.wait()すべてのタスクが完了するまでメインコルーチンをブロックします。使用する場合は、注意してください。Goroutineの外部で追加する必要があります。重複を避け、Donが呼び出されていることを確認(rèn)してください。 Deferで使用することをお?jiǎng)幛幛筏蓼埂¥长欷?、Webページの同時(shí)クロール、バッチデータ処理、その他のシナリオで一般的であり、並行性プロセスを効果的に制御できます。

オーディオとビデオ?jiǎng)I理の中核は、基本的なプロセスと最適化方法を理解することにあります。 1.基本的なプロセスには、取得、エンコード、送信、デコード、再生が含まれ、各リンクには技術(shù)的な困難があります。 2。オーディオおよびビデオの異常、遅延、音のノイズ、ぼやけた畫像などの一般的な問題は、同期調(diào)整、コーディング最適化、ノイズ減少モジュール、パラメーター調(diào)整などを通じて解決できます。 3. FFMPEG、OPENCV、WeBRTC、GSTREAMER、およびその他のツールを使用して機(jī)能を達(dá)成することをお?jiǎng)幛幛筏蓼埂?4.パフォーマンス管理の観點(diǎn)から、ハードウェアの加速、解像度フレームレートの合理的な設(shè)定、並行性の制御、およびメモリの漏れの問題に注意を払う必要があります。これらの重要なポイントを習(xí)得すると、開発効率とユーザーエクスペリエンスの向上に役立ちます。

Goの埋め込みパッケージを使用すると、靜的リソースをバイナリに簡単に埋め込み、Webサービスに適しており、HTML、CSS、寫真、その他のファイルをパッケージ化できます。 1。追加する埋め込みリソースを宣言します// go:embed comment hello.txtを埋め込むなど、変數(shù)の前に埋め込みます。 2。static/*などのディレクトリ全體に埋め込み、embed.fsを介してマルチファイルパッケージを?qū)g現(xiàn)できます。 3.効率を改善するために、ビルドタグまたは環(huán)境変數(shù)を介してディスクロードモードを切り替えることをお?jiǎng)幛幛筏蓼埂?4.パスの精度、ファイルサイズの制限、埋め込みリソースの読み取り専用特性に注意してください。埋め込みの合理的な使用は、展開を簡素化し、プロジェクト構(gòu)造を最適化することができます。

GOで書かれたWebサーバーを構(gòu)築することは難しくありません。コアは、Net/HTTPパッケージを使用して基本サービスを?qū)g裝することにあります。 1. Net/HTTPを使用して最もシンプルなサーバーを起動(dòng)します。処理機(jī)能を登録し、數(shù)行のコードを介してポートをリッスンします。 2。ルーティング管理:Servemuxを使用して、構(gòu)造化された管理を容易にするために複數(shù)のインターフェイスパスを整理します。 3。共通の実踐:機(jī)能モジュールによるグループルーティング、およびサードパーティライブラリを使用して複雑なマッチングをサポートします。 4.靜的ファイルサービス:http.fileserverを介してHTML、CSS、JSファイルを提供します。 5。パフォーマンスとセキュリティ:HTTPSを有効にし、リクエスト本體のサイズを制限し、セキュリティとパフォーマンスを改善するためのタイムアウトを設(shè)定します。これらの重要なポイントを習(xí)得した後、機(jī)能を拡大する方が簡単になります。

Select Plusのデフォルトの目的は、他のブランチがプログラムブロッキングを避ける準(zhǔn)備ができていない場合にデフォルトの動(dòng)作を?qū)g行できるようにすることです。 1.ブロックせずにチャネルからデータを受信すると、チャネルが空の場合、デフォルトのブランチに直接入力されます。 2。時(shí)間と組み合わせて。後またはティッカー、定期的にデータを送信してみてください。チャネルがいっぱいの場合、ブロックしてスキップしません。 3.デッドロックを防ぎ、チャネルが閉じられているかどうかが不確かなときにプログラムが詰まっていることを避けます。それを使用する場合、デフォルトのブランチはすぐに実行され、亂用することはできず、デフォルトとケースは相互に排他的であり、同時(shí)に実行されないことに注意してください。
