text/plain results in an undefined response type with Golang Swagger
pen-gocaspi opened this issue · 1 comments
Description
Hello, when im generating Golang code for a simple endpoint that returns plain text the generated client runs in an error:
undefined response type
. I added the code snippets below.
Swagger-codegen version
2.0
Swagger declaration file content or url
---
swagger: "2.0"
info:
description: "something"
version: "v2.6"
title: "something"
contact:
email: "something@something.de"
host: "something.de"
tags:
- name: "administration"
description: "Meta services"
schemes:
- "https"
- "http"
consumes:
- "application/json"
produces:
- "application/json"
security:
- basicAuth: []
paths:
/about/version:
get:
tags:
- "administration"
summary: "current software version"
description: "Returns the current something version"
produces:
- "text/plain"
parameters: []
responses:
"200":
description: "successful operation"
schema:
type: "string"
example: "1.9.44-04345"
"400":
description: "bad request"
schema:
$ref: "#/definitions/ErrorMessage"
"500":
description: "internal server error"
schema:
$ref: "#/definitions/ErrorMessage"
securityDefinitions:
basicAuth:
type: "basic"
definitions:
ErrorMessage:
type: "object"
properties:
errorMessage:
type: "string"
Command line used for generation
Steps to reproduce
Generate Go Client with Swagger generator
Related issues/PRs
Suggest a fix/enhancement
When it now comes to decoding the response of the endpoint the generate Golang code looks like:
func (a *AdministrationApiService) AboutVersionGet(ctx context.Context) (string, *http.Response, error) {
var (
localVarHttpMethod = strings.ToUpper("Get")
localVarPostBody interface{}
localVarFileName string
localVarFileBytes []byte
localVarReturnValue string
)
// create path and map variables
localVarPath := a.client.cfg.BasePath + "/about/version"
localVarHeaderParams := make(map[string]string)
localVarQueryParams := url.Values{}
localVarFormParams := url.Values{}
// to determine the Content-Type header
localVarHttpContentTypes := []string{"application/json"}
// set Content-Type header
localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes)
if localVarHttpContentType != "" {
localVarHeaderParams["Content-Type"] = localVarHttpContentType
}
// to determine the Accept header
localVarHttpHeaderAccepts := []string{"text/plain"}
// set Accept header
localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts)
if localVarHttpHeaderAccept != "" {
localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
}
r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
if err != nil {
return localVarReturnValue, nil, err
}
localVarHttpResponse, err := a.client.callAPI(r)
if err != nil || localVarHttpResponse == nil {
return localVarReturnValue, localVarHttpResponse, err
}
localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body)
localVarHttpResponse.Body.Close()
if err != nil {
return localVarReturnValue, localVarHttpResponse, err
}
if localVarHttpResponse.StatusCode < 300 {
// If we succeed, return the data, otherwise pass on to decode error.
//added by issue author: normal cast form byte to string would be enough if Content-Type is plain-text
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type"))
return localVarReturnValue, localVarHttpResponse, err
}
if localVarHttpResponse.StatusCode >= 300 {
newErr := GenericSwaggerError{
body: localVarBody,
error: localVarHttpResponse.Status,
}
if localVarHttpResponse.StatusCode == 200 { //added by issue author: is this even reachable?
var v string
err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"))
if err != nil {
newErr.error = err.Error()
return localVarReturnValue, localVarHttpResponse, newErr
}
newErr.model = v
return localVarReturnValue, localVarHttpResponse, newErr
}
if localVarHttpResponse.StatusCode == 400 {
var v ErrorMessage
err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"))
if err != nil {
newErr.error = err.Error()
return localVarReturnValue, localVarHttpResponse, newErr
}
newErr.model = v
return localVarReturnValue, localVarHttpResponse, newErr
}
if localVarHttpResponse.StatusCode == 500 {
var v ErrorMessage
err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"))
if err != nil {
newErr.error = err.Error()
return localVarReturnValue, localVarHttpResponse, newErr
}
newErr.model = v
return localVarReturnValue, localVarHttpResponse, newErr
}
return localVarReturnValue, localVarHttpResponse, newErr
}
return localVarReturnValue, localVarHttpResponse, nil
}
The problem here is that the decode() function only is able to decode xml or JSON data.
What we need is cast from byte to string if the contenttype is text/plain
I had the same issue.
I think we should add the choice of strings.Contains(contentType, "text/plain")
to the decode function in client.go that is generated by template file client.mustache.
It should be modified to look like this:
func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err error) {
if strings.Contains(contentType, "application/xml") {
if err = xml.Unmarshal(b, v); err != nil {
return err
}
return nil
} else if strings.Contains(contentType, "application/json") {
if err = json.Unmarshal(b, v); err != nil {
return err
}
return nil
} else if strings.Contains(contentType, "text/plain") {
// I'm not sure that's going to cause panic
*(v.(*string)) = string(b)
return nil
}
return errors.New("undefined response type")
}
In order to return the message directly when the contentType is text/plain.