본문 바로가기

Language/Go

[Go] - web programming : request 처리 및 response 작성

Go 를 통해 클라이언트 요청을 받아 처리하고 응답하는 방법을 익히자.

 

1. Go를 이용한 요청과 응답

2. Go를 이용한 HTML 폼 처리

3. ResponseWriter를 이용해 클라이언트에게 다시 응답 보내기

 

 

 

1, 2 Go를 이용한 요청과 응답 및 HTML 폼 처리

- 요청 URL 필드 구조

type URL struct {
	Scheme     string
	Opaque     string    // encoded opaque data
	User       *Userinfo // username and password information
	Host       string    // host or host:port
	Path       string    // path (relative paths may omit leading slash)
	RawPath    string    // encoded path hint (see EscapedPath method)
	ForceQuery bool      // append a query ('?') even if RawQuery is empty
	RawQuery   string    // encoded query values, without '?'
	Fragment   string    // fragment for references, without '#'
}

 

html form을 이용한 client 요청

<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html" charset="UTF-8">
    <title>Go web Programming</title>
</head>
  <body>
    <form action="http://127.0.0.1:8080/process?hello=world&thread=123" method="post" enctype="application/x-www-form-urlencoded">
        <input type="text" name="hello" value="dksshddl"/>
        <input type="text" name="post" value="456"/>
        <input type="submit"/>
    </form>
  </body>
</html>

- URL로 http://127.0.0.1:8080/process?hello=world&thread=123 을 post method로 서버에 요청

- Content-type --> application/x-wwwform-urlencoded

- 두개의 키-값 쌍을 서버로 전송 hello=dksshddl와 post=456

 

package main

import (
	"fmt"
	"net/http"
)

func process(w http.ResponseWriter, r *http.Request)  {
  r.ParseForm()
  fmt.Fprintln(w, r.Form)
}

func main() {
  server := http.Server{
  Addr:    "127.0.0.1:8080",
  Handler: nil,
  }
  http.HandleFunc("/process", process)
  server.ListenAndServe()
}

- 서버에서는 request url을 parse하고, 그 Form을 출력할 수 있다.

- r.Form은 map으로 저장되기 떄문에, map[key]로 접근할 수 있다.

- post로 전송된 값 만을 가지고 오고 싶을 떄에는 r.Form -> r.PostForm으로 바꿔 접근할 수있다.

 

url에 저장된 key-value 값과 form으로 전송한 key-value가 포함

다양한 폼 및 multipart/form-data 사용

package main

import (
	"fmt"
	"net/http"
)

func process(w http.ResponseWriter, r *http.Request)  {
	r.ParseMultipartForm(1024)
	fmt.Fprintln(w, "(1)", r.FormValue("hello"))
	fmt.Fprintln(w, "(2)", r.Form)
	fmt.Fprintln(w, "(3)", r.PostFormValue("hello"))
	fmt.Fprintln(w, "(4)", r.PostForm)
	fmt.Fprintln(w, "(5)", r.MultipartForm)
}

func main() {
		server := http.Server{
			Addr:    "127.0.0.1:8080",
			Handler: nil,
		}
		http.HandleFunc("/process", process)
		server.ListenAndServe()
}

 

다양한 form 메소드 사용

 

  키-값 쌍 콘텐츠 타입
필드 호출해야 할 메소드 URL Form 인코딩 URL Multipart
Form ParseForm O O O -
PostForm Form - O O -
MultipartForm ParseMultipartForm - O - O
FormValue N/A O O O -
PostFormValue N/A - O O -

 

파일

<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html" charset="UTF-8">
    <title>Go web Programming</title>
</head>
<body>
<form action="http://127.0.0.1:8080/process?hello=world&thread=123" method="post" enctype="multipart/form-data">
    <input type="text" name="hello" value="dksshddl"/>
    <input type="text" name="post" value="456"/>
    <input type="file" name="uploaded">
    <input type="submit"/>
</form>
</body>
</html>

form 에 file type input 추가

 

package main

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

func process(w http.ResponseWriter, r *http.Request)  {
	r.ParseMultipartForm(1024)
	fileHeader := r.MultipartForm.File["uploaded"][0]
	file, err := fileHeader.Open()
	if err == nil {
		data, err := ioutil.ReadAll(file)
		if err == nil {
			fmt.Fprintln(w, string(data))
		}
	}
}

func main() {
		server := http.Server{
			Addr:    "127.0.0.1:8080",
			Handler: nil,
		}
		http.HandleFunc("/process", process)
		server.ListenAndServe()
}

서버 쪽에서는 제출 된 form에서 file을 읽어낸다.

 

3. ResponseWriter

 

- 클라이언트에게 응답을 하기 위해서는 Response 구조를 작성해야 한다.

- Response내의 응답할 데이터를 설정해 주어야 한다.

- ResponseWriter는 인터페이스이며 핸들러가 HTTP 응답을 만들 때 사용

- ResponseWriter는 Write, WriteHeader, Header 메소드로 구성

 

Write 메소드는 바이트 배열을 취함 - Write가 호출되기까지 콘텐츠 타입이 없다면, 첫 번째 512바이트 데이터에 대한 만큼만 콘텐츠 타입으로 감지

 

package main

import (
	"net/http"
)

func writeExample(w http.ResponseWriter, r * http.Request) {
	str := "<html>" +
		"<head><title>Go Web Programming</title></head>" +
		"<body><h1>Hello World</h1></body></html>"
	w.Write([]byte(str))

}
func main() {
		server := http.Server{
			Addr:    "127.0.0.1:8080",
			Handler: nil,
		}
		http.HandleFunc("/write", writeExample)
		server.ListenAndServe()
}

string 데이터를  response에 작성하여 보내보자.

 

그 후, http 메세지를 확인해보자

linux

curl -i 127.0.0.1:8080/write

window powershell 

curl -Uri 127.0.0.1:8080/wirte

 

Response http msg

 

WriteHeader 메소드는 응답 코드를 작성하는 것 실제 header 작성하는 부분이 아니다.

 

func writeHeaderExample(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(501)
	fmt.Fprintln(w, "No such service, try next door")
}

 

501 status code response msg

window powershell 에서는 501을 에러로 출력과 함께 작성한 메세지를 출력해준다.

 

 

헤더 수정하기

 

func headerExample(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Location", "http://google.com")
	w.WriteHeader(302)
}

응답이 들어오면, Header의 Location을 구글 사이트로 이동한다.

 

다시 작성된 http msg

 

클라이언트에게 JSON 응답 주기

 

JSON 데이터 구조를 전송할 Post라는 구조 가정

type Post struct {
	User string
	Threads []string
}

 

json으로 encoding 하여 전송

func jsonExample(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	post := &Post{
		User: "dksshddl",
		Threads: []string{"first", "second", "third"},
	}
	json, _ := json.Marshal(post)
	w.Write(json)
}

encoding/json 패키지의 Marshal 메소드를 이용하여 post 데이터 구조를 json 구조로 변환

 

 

Content에 json 문자열이 가지고 있는것을 확인할 수 있다.