오늘은 지난 포스팅에 이어서 RESTful 로 구성하는 것을 마무리 해볼 예정이다.
이제 남은 기능은 Delete랑, 전체 list를 가져오는 거, 데이터 수정(update)하는 기능만 구현하면 끝난다.
func deleteUserHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, err)
return
}
_, ok := userMap[id]
if !ok {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "No User ID:", id)
return
}
delete(userMap, id)
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "Deleted User ID:", id)
}
지난번에 작성하던 myapp.go에서 추가된 func이다. deleteUserHander()
함수는 그 이름처럼, 유저 정보를 Map에서 삭제하는 기능을 하는 함수이다.
func TestUpdateUser(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer(NewHandler())
defer ts.Close()
req, _ := http.NewRequest("PUT", ts.URL+"/users",
strings.NewReader(`{"id":1, "first_name":"updated", "last_name":"updated", "email":"updated@naver.com"}`))
resp, err := http.DefaultClient.Do(req)
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
data, _ := ioutil.ReadAll(resp.Body)
assert.Contains(string(data), "No User ID:1")
resp, err = http.Post(ts.URL+"/users", "application/json",
strings.NewReader(`{"first_name":"tucker", "last_name":"kim", "email":"tucker@naver.com"}`))
assert.NoError(err)
assert.Equal(http.StatusCreated, resp.StatusCode)
user := new(User)
err = json.NewDecoder(resp.Body).Decode(user)
assert.NoError(err)
assert.NotEqual(0, user.ID)
updateStr := fmt.Sprintf(`{"id":%d, "first_name":"jason"}`, user.ID)
req, _ = http.NewRequest("PUT", ts.URL+"/users",
strings.NewReader(updateStr))
resp, err = http.DefaultClient.Do(req)
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
updateUser := new(User)
err = json.NewDecoder(resp.Body).Decode(updateUser)
assert.NoError(err)
assert.Equal(updateUser.ID, user.ID)
assert.Equal("jason", updateUser.FirstName)
assert.Equal(user.LastName, updateUser.LastName)
assert.Equal(user.Email, updateUser.Email)
}
삭제기능 테스트라서 테스트 코드가 매우 길지만, 실질적으로 추가된 기능은 딱히 없다. golang에서 제공하는 http package
에서는 Delete
요청이 없기 때문에 새로 만들어줘야 한다.
http.NewRequest()
로 요청을 만들어주면된다. PUT
method를 테스트 할때는 새로운 데이터 업데이트를 위해서 NewRequest()
함수의 세번째 인자로 Body를 직접 작성해서 넣어줘야 한다.
그리고 NewRequest()
로 method를 생성해준 뒤에는, 실행을 위해서 http패키지의 http.DefaultClient.Do()
함수로 실행해줄 수 있으며, NewRequest()
함수로 return된 requst를 인자로 넣으면 된다.
그리고 새로 업데이트 된 데이터를 확인해보면 문제가 있음을 확인할 수 있는데, 업데이트를 원하지 않았던 데이터들이 ""으로 바뀌었음을 확인할 수 있다.
이런 문제를 해결 하기 위해서 app.go
에서 updateUserHandler
함수에 아래와 같은 구문을 추가해서 공백인 상태로 오게되면 이전의 데이터를 상속하는 방식으로 수정했다.
func updateUserHandler(w http.ResponseWriter, r *http.Request) {
updateUser := new(User)
err := json.NewDecoder(r.Body).Decode(updateUser)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, err)
return
}
user, ok := userMap[updateUser.ID]
if !ok {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "No User ID:", updateUser.ID)
return
}
if updateUser.FirstName != "" {
user.FirstName = updateUser.FirstName
}
if updateUser.LastName != "" {
user.LastName = updateUser.LastName
}
if updateUser.Email != "" {
user.Email = updateUser.Email
}
userMap[updateUser.ID] = updateUser
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
data, _ := json.Marshal(updateUser)
fmt.Fprint(w, string(data))
}
이렇게 작성해도 문제점이 발생하는데, 이렇게 작성된 코드는 우리가 정보중 일부를 지우고 싶을때, 그 데이터가 비어있는 상태로 업데이트를 하고 싶은건지, 아니면 이전의 데이터를 상속받아야 하는지 알 수 없다.
그래서 현업에는 보통 업데이트에 사용하는 구조체를 별도로 만들고 bool 형 자료를 추가해 flag를 만들어서 이런 문제를 해결한다.
func usersHandler(w http.ResponseWriter, r *http.Request) {
if len(userMap) == 0 {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "No Users")
}
users := []*User{}
for _, u := range userMap {
users = append(users, u)
}
data, _ := json.Marshal(users)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, string(data))
}
func TestUsersWithUserData(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer(NewHandler())
defer ts.Close()
resp, err := http.Post(ts.URL+"/users", "application/json",
strings.NewReader(`{"first_name":"tucker", "last_name":"kim", "email":"tucker@naver.com"}`))
assert.NoError(err)
assert.Equal(http.StatusCreated, resp.StatusCode)
resp, err = http.Post(ts.URL+"/users", "application/json",
strings.NewReader(`{"first_name":"json", "last_name":"park", "email":"json@naver.com"}`))
assert.NoError(err)
assert.Equal(http.StatusCreated, resp.StatusCode)
resp, err = http.Get(ts.URL + "/users")
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
users := []*User{}
err = json.NewDecoder(resp.Body).Decode(&users)
assert.NoError(err)
assert.Equal(2, len(users))
}