GO http 3일차 - HTTP Method와 request body, form data 받기

0

GO http

목록 보기
3/5

HTTP Method GET, POST, PUT, DELETE

golang에서 http method를 사용하는 방법은 매우 특이하고 재밌는데 routing단계가 아니라 handler 내부에서 구분하여 로직을 처리한다.

func main() {
	fmt.Println("start server!")

	mux := http.NewServeMux()
	mux.HandleFunc("/go", goHandler)

	err := http.ListenAndServe(":8888", mux)
	if err != nil {
		fmt.Println(err)
	}
}

다음의 코드를 보면, /go url만 열려있고 goHandler로 연결된다. 만약, /go url을 요청할 때 http method에 따라 결과를 다르게 반환하고, 입력을 달리주고 싶다면 어떻게 해야할까? 즉, RESTful 제약을 지킨 api를 만들기 위해 http method를 사용하려면 어떻게 해야할까??

대부분의 언어들은 mux.HandleFunc("/go", goHandler)와 같은 routing 단계에서 http method를 명시해주어 구분하는데 golang는 그렇지 않고, handler인 goHandler 내부에서 구분해야한다.

func goHandler(res http.ResponseWriter, req *http.Request) {
	switch req.Method {
	case "GET":
		io.WriteString(res, "GET\n")
	case "POST":
		io.WriteString(res, "POST\n")
	case "PUT":
		io.WriteString(res, "PUT\n")
	case "DELETE":
		io.WriteString(res, "DELETE\n")
	default:
		io.WriteString(res, "FAILURE\n")
	}
}

http.Request내부에 Methodhttp method를 알아낼 수 있다. switch-case를 사용하여 어떤 http method인지, 확인할 수 있고 지원하는 http method라면 결과를 반환해주면된다. 한가지 알아두어야 할 것은 golangswitch-casebreak가 없어도 하나의 case에 들어가면 switch문에서 빠져나온다.

curlhttp method를 바꿔가며 테스트해보도록 하자

curl -X GET  "http://localhost:8888/go"
GET

curl -X POST  "http://localhost:8888/go"
POST

curl -X PUT  "http://localhost:8888/go"
PUT

curl -X DELETE  "http://localhost:8888/go"
DELETE

curl -X trace  "http://localhost:8888/go"
FAILURE

잘 동작하는 것을 확인할 수 있다.

Reading a Request Body

REST API와 같은 HTTP API를 만들 때, 클라이언트들은 URL length limit 이상의 데이터를 전송하고 싶을 때가 많이 있을 것이다. query parameter로는 도저히 넘기기에는 너무 많은 데이터들이 있는데 대부분 json이나 form과 같은 형식을 한다. 가령, 유저의 인적정보를 넘긴다고 할 때, 이름, 주소, 나이, 직장, 경력, 취미 등등의 정보는 query parameter로 넘기기 어렵다. 그렇기 때문에 request's body에 정보를 포함하여 전송하는 방법을 많이 사용한다. 이때 사용되는 http method가 대부분 POST, PUT이다.

위에서 보았듯이 go에서는 http.HandleFunc안의 *http.Request를 사용하여, 들어오는 request에 대한 정보에 접근할 수 있다. *http.Request에는 Body 필드가 있는데, 이를 통해서 request body에 접근할 수 있다.

func goHandler(res http.ResponseWriter, req *http.Request) {
	switch req.Method {
	case "GET":
		io.WriteString(res, "GET\n")
	case "POST":
		body, err := ioutil.ReadAll(req.Body)
		if err != nil {
			fmt.Printf("Could not read body: %s\n", err)
		}
		fmt.Printf("%s: got request", body)
		io.WriteString(res, "POST\n")
	case "PUT":
		io.WriteString(res, "PUT\n")
	case "DELETE":
		io.WriteString(res, "DELETE\n")
	default:
		io.WriteString(res, "FAILURE\n")
	}
}

req.Bodyio.ReadCloser라는 인터페이스를 가지고 있는데, 내부적으로 Reader, Closer를 가지고 있어 io.Reader에 호환이 가능하다. 따라서 ioutil.ReadAll() 파라미터에 req.body를 넣어 데이터를 바이트로 가져올 수 있다.

body, err := ioutil.ReadAll(req.Body)

이제 해당 데이터를 읽어오기만하면 된다.

한번 req.body에 데이터를 전송해보자.

curl -X POST -d "hello world"  "http://localhost:8888/go"

fmt로 서버에 찍은 결과가 나온 것을 확인해보자.

hello world: got request

잘 찍힌 것을 확인할 수 있다.

이번에는 json 데이터를 전송해보자.

curl -X POST -d '{"name":"John", "age":30, "car":null}'  "http://localhost:8888/go"

서버에 찍힌 로그를 확인하면

{"name":"John", "age":30, "car":null}: got request

정상적으로 전달된 것을 확인할 수 있다.

Retrieving From Data

website에서 form 태그로 만들어진 데이터를 받은다고 하자. 비록 form 태그로 데이터를 받는 방식이 이제는 더 이상 메이저한 방법이 아니지만 아직도 많이 사용되고 예전에 만들어진 website에서의 호환을 위해서 구현해야한다.

이 역시도 *http.Request값을 통해서 접근할 수 있고, query parameter, request body와 비슷한 방식으로 접근할 수 있다.

req.PostFormValue()을 통해 접근할 수 있다. form의 특성상 name=value형식이기 때문에 req.PostFormValue()name부분을 넣으면 value를 반환해준다.

func goHandler(res http.ResponseWriter, req *http.Request) {
	switch req.Method {
	case "GET":
		io.WriteString(res, "GET\n")
	case "POST":
		myName := req.PostFormValue("name")
		if myName == "" {
			myName = "HTTP"
		}
		io.WriteString(res, fmt.Sprintf("hello %s\n", myName))
	case "PUT":
		io.WriteString(res, "PUT\n")
	case "DELETE":
		io.WriteString(res, "DELETE\n")
	default:
		io.WriteString(res, "FAILURE\n")
	}
}

myName := req.PostFormValue("name")다음과 같이 "name"이라는 form dataname부분을 넣어주면 value를 반환해준다. 이를 http request를 보내어 확인해보도록 하자.

curl-F 옵션을 넣으면 form data를 전송할 수 있다.

curl -X POST -F 'name=Sammy' 'http://localhost:8888/go'

결과로 form 데이터의 value인 Sammy를 반환해준다.

hello Sammy

0개의 댓글