├── LICENSE ├── Readme.md └── assets └── foods.xml /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Debasish Sahoo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | A simple list of concepts and code snippets that would help in learning Golang and apply in Web Development :tada:. I tried to jot down when I was learning. It might be helpful for beginners who want to learn Go for web development. 5 |
6 |
7 | 8 | [![Open Source](https://badges.frapsoft.com/os/v1/open-source.svg?v=103)](https://opensource.org/) 9 | [![GitHub license](https://img.shields.io/github/license/debck/Learning-Go?style=flat-square)](https://github.com/debck/Learning-Go/blob/master/LICENSE) ![Maintenance](https://img.shields.io/maintenance/yes/2020?style=flat-square) ![GitHub last commit](https://img.shields.io/github/last-commit/debck/Learning-Go?style=flat-square) 10 | [![HitCount](http://hits.dwyl.com/debck/Learning-Go.svg)](http://hits.dwyl.com/debck/Learning-Go) 11 | 12 | 13 | > ✌️ Hope you find something useful. If you like it please give it a 🌟. 14 | 15 | ## Contents 16 | * [Installation](#Installation) 17 | * [Concepts to learn before diving into Web](#Initial-Concepts-to-Study-before-diving-deep) 18 | * [Basic Hello World](#Basic-Hello-World) 19 | * [Adding static assets](#Adding-static-asset) 20 | * [Creating Routes](#Adding-Routes) 21 | * [Adding Forms](#Adding-Forms) 22 | * [Adding MiddleWare](#Adding-MiddleWare) 23 | * [Sessions Management](#Sessions-Management) 24 | * [Adding Database](#Adding-Database) 25 | * [MongoDB](#MongoDB) 26 | * [Writing Unit Test](#Writing-Unit-Test) 27 | * [Parsing XML Data](#Parsing-XML-Data) 28 | * [File Uploading](#File-Uploading) 29 | 30 | ## Installation 31 | 32 | > Follow the [official doc](https://golang.org/doc/install) and setup Go depending on your OS (ie. Windows , Linux, OS X) 33 | 34 | ## Initial Concepts to Study before diving deep 35 | 36 | * Basic Understanding of 37 | * Variables 38 | * Constants 39 | * Packages and import/export 40 | * Functions 41 | * Pointers 42 | * Mutability 43 | * Types 44 | * Type Conversion 45 | * Type assertion** 46 | * Structs 47 | * Composition 48 | * Collection Types 49 | * Arrays 50 | * Slicing 51 | * Range & Maps 52 | * Control Flow 53 | * If, For, Switch statement 54 | * Methods 55 | * Interfaces 56 | * Concurrency 57 | * Goroutines 58 | * Channels 59 | 60 | 61 | 62 | 63 | 64 | ## Basic Hello World 65 | 66 | ```go 67 | package main 68 | 69 | import ( 70 | "fmt" 71 | "net/http" 72 | ) 73 | 74 | func main() { 75 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 76 | fmt.Fprintf(w, "Hello, World !") 77 | }) 78 | 79 | http.ListenAndServe(":8000", nil) 80 | } 81 | ``` 82 | [Go back to top ↑](#Contents) 83 | 84 | ## Adding static asset 85 | 86 | When we want to serve static files like CSS, JavaScript or images to Web. 87 | 88 | ```go 89 | func main() { 90 | http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) { 91 | fmt.Fprintf(w, "Hello World!") 92 | }) 93 | 94 | fs := http.FileServer(http.Dir("./static")) 95 | http.Handle("/static/", http.StripPrefix("/static/", fs)) 96 | 97 | http.ListenAndServe(":8000", nil) 98 | } 99 | ``` 100 | [Go back to top ↑](#Contents) 101 | 102 | ## Adding Routes 103 | 104 | ```go 105 | package main 106 | 107 | import ( 108 | "fmt" 109 | "encoding/json" 110 | "net/http" 111 | "github.com/gorilla/mux" 112 | ) 113 | 114 | 115 | type Tasks struct { 116 | ID string `json:"id,omitempty"` 117 | TASKNAME string `json:"task,omitempty"` 118 | } 119 | 120 | var task []Tasks 121 | 122 | func getAllTask(w http.ResponseWriter, r *http.Request) { 123 | json.NewEncoder(w).Encode(task) 124 | } 125 | 126 | 127 | func getTask(w http.ResponseWriter, r *http.Request) { 128 | params := mux.Vars(r) 129 | for _,item := range task { 130 | if item.ID == params["id"] { 131 | json.NewEncoder(w).Encode(item) 132 | return 133 | } 134 | } 135 | json.NewEncoder(w).Encode(&Tasks{}) 136 | } 137 | 138 | 139 | func main() { 140 | router := mux.NewRouter() 141 | router.HandleFunc("/task", getAllTask).Methods("GET") 142 | router.HandleFunc("/task/{id}", getTask).Methods("GET") 143 | 144 | http.ListenAndServe(":8000", router) 145 | } 146 | 147 | ``` 148 | 149 | [Go back to top ↑](#Contents) 150 | 151 | ## Adding Forms 152 | 153 | Considering the form has 2 fields `Email` and `Message`. 154 | 155 | ```go 156 | 157 | package main 158 | 159 | import ( 160 | "log" 161 | "fmt" 162 | "net/http" 163 | ) 164 | 165 | type Details struct { 166 | Email string 167 | Message string 168 | } 169 | 170 | func messageHandle(w http.ResponseWriter, r *http.Request) { 171 | if err := r.ParseForm(); err != nil { 172 | fmt.Fprintf(w, "ParseForm() err: %v", err) 173 | return 174 | } 175 | 176 | data := Details{ 177 | Email: r.FormValue("email"), 178 | Message: r.FormValue("message"), 179 | } 180 | 181 | // do something with the data 182 | } 183 | 184 | func main() { 185 | http.HandleFunc("/", messageHandle) 186 | 187 | if err := http.ListenAndServe(":8080", nil); err != nil { 188 | log.Fatal(err) 189 | } 190 | } 191 | 192 | 193 | ``` 194 | 195 | [Go back to top ↑](#Contents) 196 | 197 | ## Adding MiddleWare 198 | 199 | Here, the `Middleware` function allows adding more than one layer of middleware and handle them appropriately. 200 | `SomeMiddleware` is the middleware function which gets called before the route handler function `getAllTask` 201 | 202 | ```go 203 | 204 | package main 205 | 206 | import ( 207 | "encoding/json" 208 | "log" 209 | "net/http" 210 | "github.com/gorilla/mux" 211 | ) 212 | 213 | func getAllTask(w http.ResponseWriter, r *http.Request) { 214 | // ... do something inside this route 215 | } 216 | 217 | 218 | // Function allows adding more than one layer of middleware and handle them appropriately 219 | 220 | func Middleware(h http.Handler, middleware ...func(http.Handler) http.Handler) http.Handler { 221 | for _, mw := range middleware { 222 | h = mw(h) 223 | } 224 | return h 225 | } 226 | 227 | // Middlware function 228 | 229 | func SomeMiddleware(next http.Handler) http.Handler { 230 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 231 | 232 | // ... do middleware things 233 | 234 | next.ServeHTTP(w, r) 235 | }) 236 | } 237 | 238 | func main() { 239 | 240 | router := mux.NewRouter() 241 | router.Handle("/task", Middleware( 242 | http.HandlerFunc(getAllTask), 243 | SomeMiddleware, 244 | )) 245 | log.Fatal(http.ListenAndServe(":8000", router)) 246 | } 247 | 248 | 249 | ``` 250 | 251 | [Go back to top ↑](#Contents) 252 | 253 | ## Sessions Management 254 | 255 | ```go 256 | 257 | import ( 258 | "os" 259 | "log" 260 | "net/http" 261 | "github.com/gorilla/sessions" 262 | ) 263 | 264 | var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY"))) 265 | 266 | func Handlerfunction(w http.ResponseWriter, r *http.Request) { 267 | session, err := store.Get(r, "session-name") 268 | if err != nil { 269 | http.Error(w, err.Error(), http.StatusInternalServerError) 270 | return 271 | } 272 | 273 | // Set some session values. 274 | session.Values["hello"] = "world" 275 | // Save it 276 | session.Save(r, w) 277 | } 278 | 279 | 280 | func main() { 281 | http.HandleFunc("/", Handlerfunction) 282 | 283 | if err := http.ListenAndServe(":8080", nil); err != nil { 284 | log.Fatal(err) 285 | } 286 | } 287 | 288 | ``` 289 | 290 | [Go back to top ↑](#Contents) 291 | 292 | 293 | ## Adding Database 294 | 295 | ### MongoDB 296 | 297 | Here in this example we have connected MongoDB with our application and saved sample data into the collection 298 | 299 | ```go 300 | 301 | package main 302 | 303 | import ( 304 | "context" 305 | "fmt" 306 | "os" 307 | "time" 308 | "go.mongodb.org/mongo-driver/mongo" 309 | "go.mongodb.org/mongo-driver/mongo/options" 310 | ) 311 | 312 | type Data struct { 313 | ID int `json:"Field Int"` 314 | Task string `json:"Field Str"` 315 | } 316 | 317 | func main() { 318 | 319 | clientOptions := options.Client().ApplyURI("") 320 | 321 | // Connect to the MongoDB 322 | client, err := mongo.Connect(context.TODO(), clientOptions) 323 | 324 | if err != nil { 325 | fmt.Println("mongo.Connect() ERROR:", err) 326 | os.Exit(1) 327 | } 328 | 329 | // To manage multiple API requests 330 | ctx, _ := context.WithTimeout(context.Background(), 15*time.Second) 331 | 332 | // Access a MongoDB collection through a database 333 | col := client.Database("DATABASE_NAME").Collection("COLLECTION_NAME") 334 | 335 | // Declare a MongoDB struct instance for the document's fields and data 336 | newData := Data{ 337 | ID: 12, 338 | Task: "Learn Go", 339 | } 340 | 341 | result, err := col.InsertOne(ctx, newData) 342 | if err != nil { 343 | fmt.Println("ERROR:", err) 344 | os.Exit(1) 345 | 346 | } else { 347 | fmt.Println("Result:", result) 348 | } 349 | } 350 | 351 | ``` 352 | 353 | [Go back to top ↑](#Contents) 354 | 355 | 356 | ## Writing Unit Test 357 | 358 | Consider the [Adding Routes](#Adding-Routes) section for testing. The below test case is for the `/task` route which returns an array of tasks created by users. 359 | 360 | ```go 361 | 362 | package main 363 | 364 | import ( 365 | "net/http" 366 | "testing" 367 | "net/http/httptest" 368 | "strings" 369 | ) 370 | 371 | func TestGetAllTask(t *testing.T) { 372 | req, err := http.NewRequest("GET", "http://localhost:8000/task", nil) 373 | if err != nil { 374 | t.Fatal(err) 375 | } 376 | res := httptest.NewRecorder() 377 | handler := http.HandlerFunc(getAllTask) 378 | handler.ServeHTTP(res, req) 379 | 380 | if status := res.Code; status != http.StatusOK { 381 | t.Errorf("Wrong Status Code: got %v want %v", 382 | status, http.StatusOK) 383 | } 384 | 385 | // Check the response body is what we expect. 386 | expected := `[{"id":"1","task":"Hello"},{"id":"2","task":"World"},{"id":"3","task":"yeah"}]` 387 | 388 | if strings.TrimRight(res.Body.String(),"\n") != expected { 389 | t.Errorf("ERROR: got %v want %v", 390 | res.Body.String(), expected) 391 | } 392 | } 393 | 394 | ``` 395 | Remember Test file should be name of original file + test like: `base.go` - `base_test.go`.(good practice) 396 | 397 | After running the above test case by `go test -v` command, the following output will appear 398 | 399 | ```bash 400 | F:\Go\src\Rest_API>go test -v 401 | === RUN TestGetAllTask 402 | --- PASS: TestGetAllTask (0.00s) 403 | PASS 404 | ok Rest_API 0.454s 405 | ``` 406 | 407 | [Go back to top ↑](#Contents) 408 | 409 | 410 | ## Parsing XML Data 411 | 412 | For example we have taken [this](https://github.com/debck/Learning-Go/blob/master/assets/foods.xml) XML file for parsing. 413 | 414 | ```go 415 | package main 416 | 417 | import ( 418 | "encoding/xml" 419 | "fmt" 420 | "io/ioutil" 421 | "os" 422 | ) 423 | 424 | // struct which contains the array of all Foods in the file. 425 | type Foods struct { 426 | Foods []Food `xml:"food"` 427 | } 428 | 429 | // struct which contains the details of one food. 430 | type Food struct { 431 | Name string `xml:"name"` 432 | Price string `xml:"price"` 433 | Calories string `xml:"calories"` 434 | } 435 | 436 | func main() { 437 | // Open xml file 438 | xmlFile, err := os.Open("foods.xml") 439 | 440 | if err != nil { 441 | fmt.Println(err) 442 | } 443 | 444 | // defer the closing of our xmlfile so that we can parse later 445 | defer xmlFile.Close() 446 | 447 | // read the xml file as a byte array. 448 | byteValue, _ := ioutil.ReadAll(xmlFile) 449 | 450 | var f Foods 451 | 452 | xml.Unmarshal(byteValue, &f) 453 | 454 | // Do something with the info..... 455 | 456 | // Here, we print out the Foods as just an example 457 | 458 | fmt.Println("______MENU______") 459 | for i := 0; i < len(f.Foods); i++ { 460 | fmt.Println(f.Foods[i].Name + " " + f.Foods[i].Price + " " + f.Foods[i].Calories) 461 | } 462 | 463 | } 464 | 465 | ``` 466 | After running the above program, the following output will appear 467 | 468 | ```bash 469 | F:\Go\src\Code>go run parsexml.go 470 | ______MENU______ 471 | Belgian Waffles $5.95 650 472 | French Toast $4.50 600 473 | ``` 474 | 475 | [Go back to top ↑](#Contents) 476 | 477 | ## File Uploading 478 | 479 | > Todo 480 | 481 | ## Contribute 482 | 483 | Contributions are always welcome! Please open an [issue](https://github.com/debck/Learning-Go/issues/new) if you think something should be added to the list. 484 | 485 | ## Licence 486 | 487 | MIT © [Debasish Sahoo](https://github.com/debck/Learning-Go/blob/master/LICENSE) 488 | 489 | -------------------------------------------------------------------------------- /assets/foods.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Belgian Waffles 5 | $5.95 6 | 650 7 | 8 | 9 | French Toast 10 | $4.50 11 | 600 12 | 13 | 14 | --------------------------------------------------------------------------------