Content-Type과 HTTP 요청/응답 처리
HTTP 요청과 응답에서 Content-Type 헤더는 전송되는 데이터의 형식을 정의하며, 클라이언트와 서버 간 데이터 처리 방식을 결정합니다. 각 Content-Type은 특정 데이터 형식에 맞춰 처리되며, 다양한 데이터 전송 방식에 맞춰 사용됩니다.
1. Content-Type별 패킷 바디 구조
(1) text/plain
- 설명: 단순 텍스트 데이터를 전송할 때 사용됩니다.
- 바디 예시:
Hello, this is plain text!
(2) application/x-www-form-urlencoded
- 설명: URL 인코딩된 키-값 쌍을 전송할 때 사용됩니다. 주로 HTML 폼 데이터 전송에 사용됩니다.
- 바디 예시:
key1=value1&key2=value2
(3) application/json
- 설명: JSON 형식의 데이터를 전송할 때 사용됩니다.
- 바디 예시:
json { "key1": "value1", "key2": "value2" }
(4) multipart/form-data
- 설명: 여러 부분으로 나뉘어진 데이터를 전송할 때 사용됩니다. 주로 파일 업로드에 사용됩니다.
- 바디 예시:
--boundary123
Content-Disposition: form-data; name="key1"
value1
--boundary123
Content-Disposition: form-data; name="file"; filename="example.txt"
Content-Type: text/plain
File content here.
--boundary123--
2. Go에서 http/net 및 Gin으로 요청/응답 처리
(1) text/plain
요청(Request) 생성
- http/net:
package main
import (
"bytes"
"net/http"
"log"
)
func main() {
body := []byte("Hello, this is plain text!")
req, err := http.NewRequest("POST", "<http://example.com>", bytes.NewBuffer(body))
if err != nil {
log.Fatal(err)
}
req.Header.Set("Content-Type", "text/plain")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
}
- Gin:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.POST("/text", func(c *gin.Context) {
body, _ := c.GetRawData()
c.String(200, "Received: %s", string(body))
})
r.Run(":8080")
}
(2) application/x-www-form-urlencoded
요청(Request) 생성
- http/net:
package main
import (
"bytes"
"io"
"log"
"mime/multipart"
"net/http"
"os"
)
func main() {
var requestBody bytes.Buffer
writer := multipart.NewWriter(&requestBody)
err := writer.WriteField("key1", "value1")
if err != nil {
log.Fatal(err)
}
file, err := os.Open("example.txt") // 실제 파일 열기
if err != nil {
log.Fatal(err)
}
defer file.Close()
fileWriter, err := writer.CreateFormFile("file", "example.txt")
if err != nil {
log.Fatal(err)
}
_, err = io.Copy(fileWriter, file)
if err != nil {
log.Fatal(err)
}
writer.Close()
req, err := http.NewRequest("POST", "http://example.com", &requestBody)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Content-Type", writer.FormDataContentType())
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
}
- Gin:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.POST("/form", func(c *gin.Context) {
key1 := c.PostForm("key1")
key2 := c.PostForm("key2")
c.JSON(200, gin.H{
"key1": key1,
"key2": key2,
})
})
r.Run(":8080")
}
(3) application/json
요청(Request) 생성
- http/net:
package main
import (
"bytes"
"encoding/json"
"net/http"
"log"
)
func main() {
data := map[string]string{"key1": "value1", "key2": "value2"}
jsonData, err := json.Marshal(data)
if err != nil {
log.Fatal(err)
}
req, err := http.NewRequest("POST", "<http://example.com>", bytes.NewBuffer(jsonData))
if err != nil {
log.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
}
- Gin:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.POST("/json", func(c *gin.Context) {
var jsonData map[string]string
if err := c.ShouldBindJSON(&jsonData); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{
"key1": jsonData["key1"],
"key2": jsonData["key2"],
})
})
r.Run(":8080")
}
(4) multipart/form-data
요청(Request) 생성
- http/net:
package main
import (
"bytes"
"mime/multipart"
"net/http"
"log"
)
func main() {
var requestBody bytes.Buffer
writer := multipart.NewWriter(&requestBody)
_ = writer.WriteField("key1", "value1")
fileWriter, err := writer.CreateFormFile("file", "example.txt")
if err != nil {
log.Fatal(err)
}
fileWriter.Write([]byte("File content here"))
writer.Close()
req, err := http.NewRequest("POST", "<http://example.com>", &requestBody)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Content-Type", writer.FormDataContentType())
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
}
- Gin:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.POST("/upload", func(c *gin.Context) {
key1 := c.DefaultPostForm("key1", "default_value")
file, _ := c.FormFile("file")
c.String(http.StatusOK, "Received: %s with file %s", key1, file.Filename)
})
r.Run(":8080")
}