클라이언트가 만약 잘못된 http method
로 요청을 보냈다면 어떻게 해야할까? 또는 잘못된 정보나 인증되지 않은 접근을 할 때 어떻게 해야할까?? HTTP Hedaer
와 본문에 필요한 정보를 붙여서 전달해주어야 한다. 가령 Header
안에 가능한 method
들이 무엇인지 적어주고, status code
도 전송해주면 된다.
golang에서는 http.ResponseWriter
를 통해서 Header
를 작성하고 데이터를 전달할 수 있다. 이때 Header().Set()
과 Header().Add()
가 있는데, Set
은 key-value
형식은 haader
정보를 초기화하여 아예 새로운 정보를 넣는 것이고, Add
는 기존의 header
정보를 놔두고 새로운 정보를 덧붙이는 기능이다.
POST http method
이외에 다른 http method
로 요청이 오게 될 때 405( Method Not Allowed )
메시지를 전송하도록 하자.
package main
import "net/http"
func goHandler(res http.ResponseWriter, req *http.Request) {
switch req.Method {
case "POST":
default:
res.Header().Set("Allow", http.MethodPost)
res.WriteHeader(405)
res.Write([]byte("Method Not Allowed"))
}
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/go", goHandler)
http.ListenAndServe(":8888", mux)
}
요청을 보내어 결과를 확인해보도록 하자.
curl localhost:8888/go -v
다음의 결과가 반환된다.
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8888 (#0)
> GET /go HTTP/1.1
> Host: localhost:8888
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 405 Method Not Allowed
< Allow: POST
< Date: Thu, 27 Oct 2022 01:35:49 GMT
< Content-Length: 18
< Content-Type: text/plain; charset=utf-8
<
* Connection #0 to host localhost left intact
응답 부분을 잘보면 HTTP/1.1 405 Method Not Allowed
라고 써있는 부분이 있다. 순서대로 protocol status-code status-info
순서이다. 우리는 405
를 전달하였고, 405
는 Method Not Allowed
이기 때문에 반환받은 응답에서 위와 같이 나온 것이다.
기본적으로 status code
를 Header()
에 Set
해주지 않으면 200
이 전달된다. 또한 굳이 status code
를 405
와 같이 숫자로 써줄 필요는 없다. http.StatusMethodNotAllowed
는 net/http
에서 제공하는 상태코드 값으로 const
로 모두 정의되어있다.
const (
...
StatusOK = 200 // RFC 7231, 6.3.1
StatusCreated = 201 // RFC 7231, 6.3.2
StatusAccepted = 202 // RFC 7231, 6.3.3
...
StatusMovedPermanently = 301 // RFC 7231, 6.4.2
StatusFound = 302 // RFC 7231, 6.4.3
StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7
StatusPermanentRedirect = 308 // RFC 7538, 3
...
StatusBadRequest = 400 // RFC 7231, 6.5.1
StatusUnauthorized = 401 // RFC 7235, 3.1
StatusPaymentRequired = 402 // RFC 7231, 6.5.2
StatusForbidden = 403 // RFC 7231, 6.5.3
StatusNotFound = 404 // RFC 7231, 6.5.4
StatusMethodNotAllowed = 405 // RFC 7231, 6.5.5
StatusNotAcceptable = 406 // RFC 7231, 6.5.6
StatusProxyAuthRequired = 407 // RFC 7235, 3.2
...
StatusInternalServerError = 500 // RFC 7231, 6.6.1
StatusNotImplemented = 501 // RFC 7231, 6.6.2
StatusBadGateway = 502 // RFC 7231, 6.6.3
StatusServiceUnavailable = 503 // RFC 7231, 6.6.4
)
다음과 같이 주요 사용되는 status code들이 가독성 좋게 이미 구현되어있으므로 이를 사용하면 된다. 참고로 http method인 GET, POST, PUT, DELETE
등도 마찬가지이다.
const (
MethodGet = "GET"
MethodHead = "HEAD"
MethodPost = "POST"
MethodPut = "PUT"
MethodPatch = "PATCH" // RFC 5789
MethodDelete = "DELETE"
MethodConnect = "CONNECT"
MethodOptions = "OPTIONS"
MethodTrace = "TRACE"
)
POST
는 http.MethodPost
로 쓸 수 있는 것이다.
추후에 나오겠지만, 이러한 Header
를 이용하여 cookie, session 등을 사용할 수 있다.
클라이언트가 없는 URL로 요청을 보냈을 때, NotFound
와 처리를 하고싶다면 어떻게해야할까?? 커스텀하게 만들 수도 있지만, 기본적으로 go
에서 제공하는 함수를 사용해보자.
package main
import "net/http"
func helloHandler(res http.ResponseWriter, req *http.Request) {
res.Write([]byte("hello world"))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", helloHandler)
http.ListenAndServe(":8888", mux)
}
다음의 코드를 보자, url "/"
로 들어온 요청은 helloHandler
로 빠지게 된다. 그런데 없는 url로 요청을 보내도 "/"
로 보내져 helloHandler
로 들어오는 것을 확인할 수 있다.
curl localhost:8888/2
curl localhost:8888/23
/2, /3
둘 다 지원하지않는 url임에도 불구하고, 모두 hello world
라는 결과값을 받을 수 있을 것이다.
이는 go의
servemux
가 두 가지 URL 패턴 타입을 지원하기 때문인데,fixed paths
와subtree paths
이다.fixed path
는/
로 끝나지 않은 패턴이고,subtree paths
는/
로 끝나는 패턴이다.
fixed path
는 고정된 url값에 딱 맞는 것만 매칭시키는 것이다. 가령 /hello
라고 url을 작성하면 요청이 /hello
로 딱와야지만 된다. 가령 /hellos, /hello/name
와 같은 요청을 처리하지 않는다.
반면 subtree paths
는 url값에 딱 맞는 것만이 아니라 /
아래의 모든 요청을 처리한다. /hello/
라고 써있다면 /hello/
든 /hello/32
, /hello/32/name
등 모든 요청을 처리한다.
우리는 /
로 끝나는 패턴을 사용하였기 때문에 subtree paths
를 사용하고 있는 것이며, 이는 wildcard로 /**
와 같다. 즉 /
로 아래로 fixed path
이외의 모든 요청을 다 받겠다는 것이다.
그렇다면 /
이외의 모든 요청이 들어올텐데 이를 어떻게 처리하면 될까??
http.Request
에 있는 URL
의 path
를 이용하여 원하는 path
가 아니면 NotFound
로 처리하면 된다.
package main
import "net/http"
func helloHandler(res http.ResponseWriter, req *http.Request) {
if req.URL.Path != "" {
res.WriteHeader(http.StatusNotFound)
res.Write([]byte("Not Found"))
return
}
res.Write([]byte("hello world"))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", helloHandler)
http.ListenAndServe(":8888", mux)
}
다음과 같이 req.URL.Path
로 url
을 알아낸 다음, WriteHeader
로 status code Not Found
를 넣어주고 반환하면 된다. 참고로 return
을 하지 않으면 종료되지 않기 때문에 "hello world"
도 같이 보내게 된다.
추가적으로 go에서 기본적으로 제공하고 있는 http.NotFound()
함수를 이용해도 된다.
// NotFound replies to the request with an HTTP 404 not found error.
func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", StatusNotFound) }
// NotFoundHandler returns a simple request handler
// that replies to each request with a ``404 page not found'' reply.
func NotFoundHandler() Handler { return HandlerFunc(NotFound) }
우리는 HandlerFunc
으로만 작업을 하고 있으므로, NotFound
를 사용하도록 하자. 코드 내부를 보면 자동으로 메시지와 StatusNotFound
를 전송하는 것으로 확인된다.
func helloHandler(res http.ResponseWriter, req *http.Request) {
if req.URL.Path != "" {
http.NotFound(res, req)
return
}
res.Write([]byte("hello world"))
}
helloHandler
를 위와 같이 바꾸고 요청을 보내보도록 하자.
curl localhost:8888/32 -v
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8888 (#0)
> GET /32 HTTP/1.1
> Host: localhost:8888
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Content-Type: text/plain; charset=utf-8
< X-Content-Type-Options: nosniff
< Date: Thu, 27 Oct 2022 02:55:42 GMT
< Content-Length: 19
<
404 page not found
* Connection #0 to host localhost left intact
404 page not found
가 잘 도착하였고, header
의 status code도 Not Found
로 잘 온 것을 확인할 수 있다.
이번에는 redirect
를 처리해보도록 하자. 특정 url
이 이제 더이상 쓰이지 않는 경우나 다른 url
로 변경된 경우에 많이 사용된다. 우리는 위에서 작성했던 /go
url이 /golang
으로 변경되었다고 가정하자. 어떻게 redirect
를 할 수 있을까?
이것도 net/http
에서 기본적으로 제공해주는 http.Redirect
함수를 사용하면 된다.
package main
import "net/http"
func goHandler(res http.ResponseWriter, req *http.Request) {
http.Redirect(res, req, "/golang", http.StatusMovedPermanently)
}
func golangHandler(res http.ResponseWriter, req *http.Request) {
res.Write([]byte("golang go"))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/go", goHandler)
mux.HandleFunc("/golang", golangHandler)
http.ListenAndServe(":8888", mux)
}
다음과 같이 http.Redirect(res, req, "/golang", http.StatusMovedPermanently)
을 사용해주면 된다. http.Redirect
의 매개변수는 차례대로 resposne, request, url, status code
이다. 지정한 url로 redirect가 수행되고, status code를 헤더에 넣어준다.
curl localhost:8888/go -v
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8888 (#0)
> GET /go HTTP/1.1
> Host: localhost:8888
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Content-Type: text/html; charset=utf-8
< Location: /golang
< Date: Thu, 27 Oct 2022 04:33:26 GMT
< Content-Length: 42
<
<a href="/golang">Moved Permanently</a>.
* Connection #0 to host localhost left intact
다음과 같은 결과가 나오게 된다. status code
도 원하는 대로 301
이 잘 써졌으며 <a href="/golang">Moved Permanently</a>.
결과로 리다이렉트 화면으로 넘어갈 수 있도록 할 수 있다.