-- Go

Go net/http ile Web Server Oluşturma

Merhabalar,

Go programlama dilini kullanmamız için gerekli nedenlerin başında hiç şüphesiz yüksek performans ve hızlı programlama yetenekleri gelmektedir. Daha önce bu amaca yönelik geliştirilen ve basit bir şekilde web server oluşturmayı sağlayan Node.js teknolojisine aşinaysanız Go dilindeki web server oluşturma yetenekleri hoşunuza gidecektir.

Go ile bir web server oluşturmak için öncelikle en basit ve temel bir uygulama geliştireceğiz. Sonrasında üzerine farklı örnekler ekleyerek ya da daha gelişmiş örneklerle devam edeceğiz.

Go programlamada web işlemlerini temel ve yüksek performansla geliştirebilmek için gelen built-in bir paket mevcuttur. Bu paketin adı ise; “net/http”

Paket ile ilgili dökümantasyonu okumak için; https://golang.org/pkg/net/http/

Öncelikle şunu belirtelim; net/http paketi bu örnekte anlatılanlardan çok daha fazla özelliğe sahiptir. Burada sadece bazı işlevlerini örneklendireceğiz. Sonraki makalelerde ise farklı yeteneklerini incelemeye devam edeceğiz.

# Örnek 1:

İlk örneğimizde en basit haliyle bir web server oluşturacağız. Bu server localhost:9000 üzerinden yayın verecektir. Ve sonra gene basit bir şekilde URI üzerinden basit bir parametre geçip bu elde ettiğimiz veriyi de sayfaya ekleyerek bir metnin tamamlanmasını sağlayacağız.

Aşağıdaki kodu çalıştırıp 9000 portu üzerindeki site çalıştırılabilir hale gelecektir. Siteyi açtığınızda ise sadece Merhaba yazdığını göreceksiniz. Bu uygulamanın sorunsuz çalıştığını gösterir. Sonra URI de /cihan şeklinde bir metin girip klavyeden Enter tuşuna basarak sayfanın post olmasını sağlayın. Artık arayüzde “Merhaba Cihan” şeklinde bir yazı yazacaktır.

package main

import (
	"fmt"
	"net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Merhaba %s", r.URL.Path[1:])
}

func main() {
	http.HandleFunc("/", handler)
	http.ListenAndServe(":9000", nil)

	fmt.Println("Web Server")
}

Peki bu temel uygulama nasıl çalışıyor?

Öncelikle main() metodunda uygulama için iki temel kod mevcut. Biri http.HandleFunc() diğeri de ListenAndServer() kodlarıdır.

HandleFunc’a bir handler nesnesi verilmelidir. Yukarıdaki handler() fonksiyonu bu iş için oluşturuldu. handler() çalıştığında parametre olarak ResponseWriter ve gelen Request’in bir pointer’ını almaktadır. Gelen istek(Request) üzerinden bir http isteği üzerinde gerekli ve elde edebileceğiniz tüm bilgileri alıp kullanabilirsiniz. Biz bu örnekte sadece istek üzerinden URL’i alıp Path nesnesi üstünden slice ile bir kesme yapıyoruz. Bu işlem birinci elemandan sonraki tüm URI verisini getir demektir. Yani www.xyz.com/abc URI’sinde bu abc verisine denk gelmektedir. Ve sonrasında da bunu ekrana yazdırmasını istiyoruz. Hepsi bu kadar…

ListenAndServe() fonksiyonu ise server uygulamasının ayağa kalkmasını ve 9000. portu dinleyen bir listener atanmasını sağlamaktadır. Bu portu dinler ve istekleri handler üzerinde elde etmenizi sağlar.

# Örnek 2:

Bu örneğimizde de önceki örneğe sadece bir html çıktısı ekleyeceğiz.

2

Bu örneğimizin çıktısı da aşağıdaki gibi olacaktır;

Bu bloğa hoş geldin
Yazar : Cihan Özhan
Yaş : 28

# Örnek 3:

Bu örneğimizde işi biraz daha geliştirip küçük bir HTML sayfa olarak tasarladığımız sayfayı Go içerisinden çağırarak sayfaya include edip basit bir template mantığını oluşturacağız.

Uygulama klasörümüzün ağaç yapısı aşağıdaki gibidir;

  • page.html
  • webServer.go

Öncelikle HTML sayfamızı oluşturalım;

3

Şimdi de webServer.go uygulamasının Go kodlarını yazalım;

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func loadFile(fileName string) (string, error) {
	bytes, err := ioutil.ReadFile(fileName)
	if err != nil {
		return "", err
	}
	return string(bytes), nil
}

func handler(w http.ResponseWriter, r *http.Request) {
	var body, _ = loadFile("page.html")
	fmt.Fprintf(w, body)
}

func main() {
	http.HandleFunc("/", handler)
	http.ListenAndServe(":9000", nil)
}

Uygulamayı çalıştırdığımızda bir önceki sayfa çıktısının aynısını verecektir. Tek farkı; HTML kodlarını ayrı bir HTML sayfası oluşturarak uygulama içerisinden çağırmış olmamızdır.

# Örnek 4:

Bu örneğimizde bir önceki uygulamanın gerçekten bir template mantığını kazanmasını sağlayacağız. Go programlama da template için HTML içerisinde kullanılabilen ve Angular.js’ye benzeyen bir söz dizimi vardır. Basit bir mantığa sahip olduğu için HTML tarafındaki kodları anlatmaya gerek duymuyorum.

Uygulama klasörümüzün ağaç yapısı aşağıdaki gibidir;

  • page.html
  • webServer.go

4

Yukarıda gördüğünüz gibi template mantığında {{}} şeklinde Angular benzeri bir yapı var. Angular’ın da geliştiricisi Google olduğu için bu alışkanlığı devam ettirmek istemiş olsa gerek.

Uygulamanın Go kodları ise aşağıdaki gibidir;

package main

import (
	"bytes"
	"html/template"
	"io/ioutil"
	"net/http"
)

type Page struct {
	Title           string
	Author          string
	Header          string
	PageDescription string
	Content         string
	URI             string
}

func loadFile(fileName string) (string, error) {
	bytes, err := ioutil.ReadFile(fileName)
	if err != nil {
		return "", err
	}
	return string(bytes), nil
}

func handler(w http.ResponseWriter, r *http.Request) {

	// String birleştirme işlemi
	var builder bytes.Buffer
	builder.WriteString("KodLab yayınevinden çıkardığımız Yazılımcılar İçin İleri Seviye T-SQL kitabımın özellikleri aşağıdaki gibidir;\n")
	builder.WriteString("704 Sayfa\n")
	builder.WriteString("ISBN: 9.786.055.201.142\n")
	builder.WriteString("Fiyat: 37 TL\n")
	builder.WriteString("Boyut: 15 x 21\n")
	builder.WriteString("2. Baskı\n")

	uri := "www.cihanozhan.com/yazilimcilar-icin-ileri-seviye-t-sql-programlama-kitabi/"

	page := Page{
		Title:           "Kitap : İleri Seviye T-SQL Programlama",
		Author:          "Cihan Özhan",
		Header:          "İleri Seviye T-SQL Programlama",
		PageDescription: "İleri Seviye T-SQL Programlama kitap tanıtım sayfası",
		Content:         builder.String(),
		URI:             "http://" + uri}
	t, _ := template.ParseFiles("page.html")
	t.Execute(w, page)
}

func main() {
	http.HandleFunc("/", handler)
	http.ListenAndServe(":9000", nil)
}

Go kodları kendi içerisinde anlaşılır ve programlama tecrübesi olanların genel olarak anlayacağı içeriklere sahiptir. Makaleyi çok uzatmamak için detaylandırmayacağım. Ancak anlamadığınız yer olursa sorabilirsiniz.

# Örnek 5:

Makalenin gidişatından her örnekte uygulamanın daha karmaşık ve kapsamlı hale geldiğini fark etmiş olmalısınız. Bu makaledeki son ve en gelişmiş örneğimizi sona sakladım.

Bu uygulamada temel olarak standart Go kodlamanın yanında gene basit template oluşturma mantığı bulunmaktadır. Diğer örneklerden tek farkı örnek projenin daha kapsamlı olmasıdır.

Uygulama klasörümüzün ağaç yapısı aşağıdaki gibidir;

5

Uygulamamıza örnek verileri oluşturmakla başlayacağız. Bunun için json klasörü altındaki json dosyalarını oluşturalım.

  • users.json
[
    {
        "ID": 1,
        "Username": "CihanOzhan",
        "FirstName": "Cihan",
        "LastName": "Özhan",
        "Interest": "C#, GO, SQL Server, Oracle, Machine Learning, BigData",
        "Profile": "http://www.dijibil.com/cihanozhan"
    },
    {
        "ID": 2,
        "Username": "KerimFirat",
        "FirstName": "Kerim",
        "LastName": "Fırat",
        "Interest": "C, C++, Java, Linux, Android",
        "Profile": "http://www.dijibil.com/kerimfirat"
    },
    {
        "ID": 3,
        "Username": "BarisOzhan",
        "FirstName": "Barış",
        "LastName": "Özhan",
        "Interest": "Web Tasarım, SEO, SEM",
        "Profile": "http://www.dijibil.com/barisozhan"
    }
]
  • interests.json
[
    {
        "ID": 1,
        "Name": "C#"
    },
    {
        "ID": 2,
        "Name": "GO"
    },
    {
        "ID": 3,
        "Name": "SQL Server"
    },
    {
        "ID": 4,
        "Name": "Oracle"
    },
    {
        "ID": 5,
        "Name": "BigData"
    },
    {
        "ID": 6,
        "Name": "Machine Learning"
    },
    {
        "ID": 7,
        "Name": "C"
    },
    {
        "ID": 8,
        "Name": "C++"
    },
    {
        "ID": 9,
        "Name": "Java"
    },
    {
        "ID": 10,
        "Name": "Linux"
    },
    {
        "ID": 11,
        "Name": "Android"
    },
    {
        "ID": 12,
        "Name": "Web Tasarım"
    },
    {
        "ID": 13,
        "Name": "SEO"
    },
    {
        "ID": 14,
        "Name": "SEM"
    }
]
  • userInterestMappings.json
[
    {
        "UserID": 1,
        "InterestID": 1
    },
    {
        "UserID": 1,
        "InterestID": 2
    },
    {
        "UserID": 1,
        "InterestID": 3
    },
    {
        "UserID": 1,
        "InterestID": 4
    },
    {
        "UserID": 1,
        "InterestID": 5
    },
    {
        "UserID": 1,
        "InterestID": 6
    },
    {
        "UserID": 2,
        "InterestID": 7
    },
    {
        "UserID": 2,
        "InterestID": 8
    },
    {
        "UserID": 2,
        "InterestID": 9
    },
    {
        "UserID": 2,
        "InterestID": 10
    },
    {
        "UserID": 2,
        "InterestID": 11
    },
    {
        "UserID": 3,
        "InterestID": 12
    },
    {
        "UserID": 3,
        "InterestID": 13
    },
    {
        "UserID": 3,
        "InterestID": 14
    }
]

JSON dosyalarımızı oluşturduk. Şimdi template klasörü altındaki page.html template dosyamızı oluşturalım.6

Uygulamamız genel hatlarıyla ortaya çıkmaya başladı. Şimdi Go kodlarına geçerek öncelikle uygulama içerisinde kullanacağımız models klasörü altındaki struct’ları oluşturuyoruz.

  • Interest.go
package models

type Interest struct {
	ID   int
	Name string
}
  • InterestMapping.go
package models

type InterestMapping struct {
	UserID     int
	InterestID int
}
  • Page.go
package models

type Page struct {
	ID          int
	Name        string
	Description string
	URI         string
}
  • User.go
package models

type User struct {
	ID        int
	Username  string
	FirstName string
	LastName  string
	Profile   string
	Interests []Interest
}
  • UserViewModel.go
package models

type UserViewModel struct {
	Page  Page
	Users []User
}

Evet, Go struct’larımız da hazır. Artık ana uygulamamızı geliştirebiliriz.

  • webServer.go
package main

import (
	"encoding/json"
	"html/template"
	"io/ioutil"
	"net/http"

	model "./models"
)

func main() {
	http.HandleFunc("/", handler)
	http.ListenAndServe(":9000", nil)
}

func handler(w http.ResponseWriter, r *http.Request) {

	page := model.Page{ID: 3, Name: "Kullanıcılar", Description: "Kullanıcı Listesi", URI: "/users"}
	users := loadUsers()
	interests := loadInterests()
	interestMappings := loadInterestMappings()

	var newUsers []model.User

	for _, user := range users {

		for _, interestMapping := range interestMappings {
			if user.ID == interestMapping.UserID {
				for _, interest := range interests {
					if interestMapping.InterestID == interest.ID {
						user.Interests = append(user.Interests, interest)
					}
				}
			}
		}
		newUsers = append(newUsers, user)
	}

	viewModel := model.UserViewModel{Page: page, Users: newUsers}

	t, _ := template.ParseFiles("template/page.html")
	t.Execute(w, viewModel)
}

func loadFile(fileName string) (string, error) {
	bytes, err := ioutil.ReadFile(fileName)
	if err != nil {
		return "", err
	}
	return string(bytes), nil
}

func loadUsers() []model.User {
	bytes, _ := ioutil.ReadFile("json/users.json")
	var users []model.User
	json.Unmarshal(bytes, &users)
	return users
}

func loadInterests() []model.Interest {
	bytes, _ := ioutil.ReadFile("json/interests.json")
	var interests []model.Interest
	json.Unmarshal(bytes, &interests)
	return interests
}

func loadInterestMappings() []model.InterestMapping {
	bytes, _ := ioutil.ReadFile("json/userInterestMappings.json")
	var interestMappings []model.InterestMapping
	json.Unmarshal(bytes, &interestMappings)
	return interestMappings
}

Uygulamayı çalıştırdığımızda aşağıdaki gibi bir çıktı elde ederiz;

7

Github: https://github.com/cihanozhan/golang-webapi-samples

İyi Çalışmalar.
Cihan Özhan

Yorumla

Yorum