英文:
How do I send multipart form data in an HTTP request (for Watson NLC training)?
问题
我正在编写一个应用程序,将使用Watson自然语言分类器("NLC")。当我对v1/classifiers URI路径发送HTTP POST请求,并使用以下请求消息体时,服务器会以状态码415("不支持的媒体类型")进行响应:
--04fef47728eb08148fe9c7b18dd42b75abd75ebf752fd3412a85aa3af075
Content-Disposition: form-data; name="training_data"; filename="data.csv"
Content-Type: text/csv
今天有多热?;temperature
外面热吗?;temperature
会不会热得难受?;temperature
会不会酷热难耐?;temperature
今天有多冷?;temperature
外面冷吗?;temperature
会不会冷得难受?;temperature
会不会寒冷刺骨?;temperature
今天预计最高温度是多少?;temperature
预计温度是多少?;temperature
高温会危险吗?;temperature
天气会危险寒冷吗?;temperature
何时会降温?;temperature
天气热吗?;temperature
天气冷吗?;temperature
现在有多冷?;temperature
今天会是寒冷的一天吗?;temperature
何时会停止寒冷?;temperature
预计最高温度是多少?;temperature
预计最低温度是多少?;temperature
天气暖和吗?;temperature
天气凉爽吗?;temperature
目前的摄氏温度是多少?;temperature
华氏温度是多少?;temperature
--04fef47728eb08148fe9c7b18dd42b75abd75ebf752fd3412a85aa3af075
Content-Disposition: form-data; name="training_metadata"; filename="metadata.json"
Content-Type: application/json
{"language": "en"}
415状态码表明内容类型存在问题,但看起来所有的MIME类型都是正确的。
我的Go代码如下:
func (w WatsonClassifier) createFormFile(writer *multipart.Writer, fieldname string, filename string, contentType string) (io.Writer, error) {
h := make(textproto.MIMEHeader)
h.Set("Content-Disposition",
fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
fieldname, filename))
h.Set("Content-Type", contentType)
return writer.CreatePart(h)
}
func (w WatsonClassifier) request(method string, apiUrl string, body io.Reader) (string, error) {
url := w.url + "/" + apiUrl
req, err := http.NewRequest(method, url, body)
if err != nil {
return "", err
}
req.SetBasicAuth(w.username, w.password)
client := http.Client{}
resp, err := client.Do(req)
if resp.StatusCode != 200 {
answer, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(answer))
return "", errors.New("Watson returned wrong status code : " + resp.Status)
}
answer, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(answer), nil
}
func (w WatsonClassifier) Train(data []ClassifierCategory) (Classifier, error) {
table := w.buildTable(data)
str := w.buildCsv(data)
buf := new(bytes.Buffer)
writer := multipart.NewWriter(buf)
data_part, err := w.createFormFile(writer, "training_data", "data.csv", "text/csv")
if err != nil {
return WatsonClassifier{}, err
}
data_part.Write([]byte(str))
metadata_part, err := w.createFormFile(writer, "training_metadata", "metadata.json", "application/json")
if err != nil {
return WatsonClassifier{}, err
}
metadata_json := "{\"language\": \"" + w.Language + "\"}"
metadata_part.Write([]byte(metadata_json))
fmt.Println(buf.String())
answer, err := w.request("POST", "v1/classifiers", buf)
if err != nil {
return WatsonClassifier{}, err
}
fmt.Println(answer)
return WatsonClassifier{}, nil
}
英文:
I'm writing application that'll use Watson Natural Language Classifier ("NLC").
When I send an HTTP POST request against the v1/classifiers URI path with the following request message body, the server responds with status code 415 (Unsupported Media Type):
--04fef47728eb08148fe9c7b18dd42b75abd75ebf752fd3412a85aa3af075
Content-Disposition: form-data; name="training_data"; filename="data.csv"
Content-Type: text/csv
How hot is it today?;temperature
Is it hot outside?;temperature
Will it be uncomfortably hot?;temperature
Will it be sweltering?;temperature
How cold is it today?;temperature
Is it cold outside?;temperature
Will it be uncomfortably cold?;temperature
Will it be frigid?;temperature
What is the expected high for today?;temperature
What is the expected temperature?;temperature
Will high temperatures be dangerous?;temperature
Is it dangerously cold?;temperature
When will the heat subside?;temperature
Is it hot?;temperature
Is it cold?;temperature
How cold is it now?;temperature
Will we have a cold day today?;temperature
When will the cold subside?;temperature
What highs are we expecting?;temperature
What lows are we expecting?;temperature
Is it warm?;temperature
Is it chilly?;temperature
What's the current temp in Celsius?;temperature
What is the temperature in Fahrenheit?;temperature
--04fef47728eb08148fe9c7b18dd42b75abd75ebf752fd3412a85aa3af075
Content-Disposition: form-data; name="training_metadata"; filename="metadata.json"
Content-Type: application/json
{"language": "en"}
The 415 status code suggests a problem with the content type, but it seems like all the MIME types are correct.
My code (written in Go):
func (w WatsonClassifier) createFormFile(writer *multipart.Writer, fieldname string, filename string, contentType string) (io.Writer, error) {
h := make(textproto.MIMEHeader)
h.Set("Content-Disposition",
fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
fieldname, filename))
h.Set("Content-Type", contentType)
return writer.CreatePart(h)
}
func (w WatsonClassifier) request(method string, apiUrl string, body io.Reader) (string, error) {
url := w.url + "/" + apiUrl
req, err := http.NewRequest(method, url, body)
if err != nil {
return "", err
}
req.SetBasicAuth(w.username, w.password)
client := http.Client{}
resp, err := client.Do(req)
if resp.StatusCode != 200 {
answer, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(answer))
return "", errors.New("Watson returned wrong status code : " + resp.Status)
}
answer, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(answer), nil
}
func (w WatsonClassifier) Train(data []ClassifierCategory) (Classifier, error) {
table := w.buildTable(data)
str := w.buildCsv(data)
buf := new(bytes.Buffer)
writer := multipart.NewWriter(buf)
data_part, err := w.createFormFile(writer, "training_data", "data.csv", "text/csv")
if err != nil {
return WatsonClassifier{}, err
}
data_part.Write([]byte(str))
metadata_part, err := w.createFormFile(writer, "training_metadata", "metadata.json", "application/json")
if err != nil {
return WatsonClassifier{}, err
}
metadata_json := "{\"language\": \"" + w.Language + "\"}"
metadata_part.Write([]byte(metadata_json))
fmt.Println(buf.String())
answer, err := w.request("POST", "v1/classifiers", buf)
if err != nil {
return WatsonClassifier{}, err
}
fmt.Println(answer)
return WatsonClassifier{}, nil
}
答案1
得分: 3
请注意,curl 发送的 "Content-Type" 头部是 multipart/form-data
。而你的 Go 程序只发送了一个 "Content-Disposition" 头部,值为 form-data
(注意,它缺少了前缀 multipart
,即组合顶级媒体类型),但它没有正确设置包含 HTTP 请求的 "Content-Type" 头部。
Go 的 multipart.Writer
类型的 CreateFormFile
方法也是如此,但这只是工作的一部分:
h.Set("Content-Disposition",
fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
escapeQuotes(fieldname), escapeQuotes(filename)))
要获取正确的 "Content-Type" 头部值,你需要使用 multipart.Writer.FormDataContentType
。为了使用该值,你需要将你的 multipart.Writer
传递给 WatsonClassifier.request
方法,这样你就可以在 http.Request
实例上设置内容类型:
req.Header.Set("Content-Type", writer.FormDataContentType())
或者,你可以在 WatsonClassifier.request
中添加另一个参数来表示内容类型,并在 WatsonClassifier.Train
的调用点将 FormDataContentType
的结果作为参数传递。
如果以上方法有效,请告诉我们。
英文:
Note that curl is sending a "Content-Type" header of multipart/form-data
. Your Go program is sending a "Content-Disposition" header with just form-data
(note the difference, it lacking the leading multipart
composite top-level media type), but it doesn't take care of sending the correct "Content-Type" header for the containing HTTP request.
Go's multipart.Writer
type's CreateFormFile
method does the same, but again, that's only part of the job:
h.Set("Content-Disposition",
fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
escapeQuotes(fieldname), escapeQuotes(filename)))
To get the proper "Content-Type" header value, you need to use multipart.Writer.FormDataContentType
. To put that value to use, you'll need to get your multipart.Writer
into your WatsonClassifier.request
method, so that you can set the content type on your http.Request
instance:
req.Header.Set("Content-Type", writer.FormDataContentType())
Alternately, add another parameter to WatsonClassifier.request
for the content type, and pass the result of FormDataContentType
as the argument from the call site in WatsonClassifier.Train
.
Let us know if that does the trick.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论