csv response для swagger 2.0 & codegen (golang)
Пишу хэндлер, который в ответ присылает csv файл. На проекте используем swagger 2.0 в формате json и по нему codegen. Так вот есть несколько проблем:
- Когда запускаю генерацию кода по спецификации, то codegen генерирует CSVProducer так:
CsvProducer: runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
return errors.NotImplemented("csv producer has not yet been implemented")
}),
При этом JsonProducer и JsonConsumer генерируются правильно, с функциями из пакета runtime. Для CSV формата в том же пакете есть свои consumer и producer, но генерирует он почему-то их так, как я представил код выше. Естественно если я руками изменю, то в будущем при очередной генерации все слетает к дефолтному коду в виде возвращения того, что consumer не имплементирован.
- Если я все же руками поменяю все как надо и попробую проверить, то у меня при запросе, который должен вернуть csv файл вернется это (тестирую в insomnia):
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: X-API-KEY,X-USER-ID,X-SPLIT-NAME,Content-Type
Access-Control-Allow-Methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
Access-Control-Allow-Origin: *
Content-Type: text/csv
Date: Tue, 14 Mar 2023 10:11:11 GMT
Content-Length: 0
Как видно, Content-Length: 0. При этом в коде возникает ошибка:
2023/03/14 10:11:11 http: superfluous response.WriteHeader call from pkg/api/middleware.LoggingMiddleware.func1.1.1 (middleware.go:122)
2023-03-14T10:11:11.445777085Z goroutine 79 [running]:
2023-03-14T10:11:11.445809960Z runtime/debug.Stack()
...
Вот код функции pkg/api/middleware.LoggingMiddleware:
func LoggingMiddleware(ctx *open_middleware.Context) open_middleware.Builder {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if recov := recover(); recov != nil {
w.WriteHeader(http.StatusInternalServerError)
debug.PrintStack()
log.
Error().
Interface("err", recov).
Msg(r.URL.Path)
}
}()
start := time.Now()
wrapped := wrapResponseWriter(w)
// print body
body := &bytes.Buffer{}
content, _ := ioutil.ReadAll(r.Body)
defer r.Body.Close()
l := len(content)
if l > bufferLen {
l = bufferLen
}
body.Write(content[:l])
r.Body = ioutil.NopCloser(bytes.NewReader(content))
h.ServeHTTP(wrapped, r)
if wrapped.Status() != http.StatusOK && wrapped.Status() != http.StatusNoContent {
cLogger := log.Info().
Int("status", wrapped.Status()).
Str("method", r.Method).
Str("path", routePath(r, ctx)).
Dur("duration", time.Since(start)).
Str("query", r.URL.RawQuery).
RawJSON("body", body.Bytes()).
Str("user", r.URL.User.String()).
Str("api-key", r.Header.Get("X-API-KEY")).
Str("user-id", r.Header.Get("X-USER-ID"))
cLogger.Str("resp", wrapped.body.String())
cLogger.Msg(r.URL.Path)
}
})
}
}
Вот код хэндлера в swagger 2.0 json:
"/file": {
"get": {
"tags": [
"files"
],
"operationId": "GetCSVFile",
"parameters": [
{
"name": "fileName",
"in": "query",
"required": true,
"type": "string"
}
],
"produces": [
"text/csv"
],
"responses": {
"200": {
"description": "Successful response",
"schema": {
"$ref": "#/definitions/GetCSVFileResponse"
},
"headers": {
"Content-Disposition": {
"type": "string",
"description": "Attachment; filename=example.csv"
}
}
},
"default": {
"$ref": "#/responses/defaultResponse"
}
}
}
},
...
"GetCSVFileResponse": {
"type": "string",
"format": "binary",
"description": "CSV file response",
"x-go-mimetype": "text/csv",
"x-go-fileName": "example.csv",
"x-nullable": false
},
Пробовал по разному оформлять файл swagger для правильной генерации, но ни один из вариантов не привел к правильной работе.