Swagger-codegen: [Android][Volley] Post requests with json body raise exception Invalid header

Created on 5 May 2017  路  8Comments  路  Source: swagger-api/swagger-codegen

Description


Generated by swagger code for android for volley library incorrect handles post requests with json body. When I use api method with json body, I receive an error response with status = 400 and message "Invalid header". The problem is in this code fragment in ApiInvoker class:

request = new PostRequest(url, headers, contentType, new StringEntity(serialize(body), "UTF-8"), stringRequest, errorListener);

By default in my case code

 new StringEntity(serialize(body), "UTF-8")

creates an entity with text/plain content type. That is wrong because server expects application/json content type.

Swagger-codegen version
Swagger declaration file content or url
Command line used for generation
Steps to reproduce
Related issues
Suggest a Fix

To fix it is sufficient to replace code

request = new PostRequest(url, headers, contentType, new StringEntity(serialize(body), "UTF-8"), stringRequest, errorListener);

by

StringEntity entity = new StringEntity(serialize(body),"UTF-8");
entity.setContentType("application/json");
request = new PostRequest(url, headers, contentType, entity, stringRequest, errorListener);
Android Bug help wanted

Most helpful comment

Having the same issue.
A simple POST that swagger.yaml declares as consumes "application/json" is sent to server with two content types.
From WireShark "copy as plain text" of the http packet:

POST /api/things HTTP/1.1
Accept: application/json
User-Agent: Swagger-Codegen/1.0.0/android
Content-Type: application/json
Content-Type: text/plain; charset=UTF-8
...

Debugging using Android studio, I see that for POST methods, HurlStack.addBodyIfExists()is called, and for POSTs with a body (which most POSTs have), it calls

connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());

request.getBodyContentType() takes the content type from the request's org.apache.http.HttpEntity. The instance of the HttpEntity is actually a StringEntity, which returns a text/plain content type by default.

the fix suggested by @rasuldev will possibly work with other servers, but does not work with swagger-generated "jaxrs" server. The reason is that there are 2 lines with Content-Type: application/json in the request.

POST /api/things HTTP/1.1
Accept: application/json
User-Agent: Swagger-Codegen/1.0.0/android
Content-Type: application/json
Content-Type: application/json
...

Sounds like the correct fix is to change io.swagger.client.request.PostRequest to include only one content-type. It currently has two: one in field contentType and another in field HttpEntity.

For now, I locally changed PostRequest to return the content-type of contentType when asked for the body's content-type (could have used the suggestion from @rasuldev but had to also change the contentType sent to the PostRequest c'tor to null)

replaced

public String getBodyContentType() {
    if(entity == null) {
      return null;
    }
    return entity.getContentType().getValue();
  }
...
 public Map<String, String> getHeaders() throws AuthFailureError {
    Map<String, String> headers = super.getHeaders();
    if (headers == null || headers.equals(Collections.emptyMap())) {
        headers = new HashMap<String, String>();
    }
    if (apiHeaders != null && !apiHeaders.equals(Collections.emptyMap())) {
        headers.putAll(apiHeaders);
    }
    if(contentType != null) {
        headers.put("Content-Type", contentType);
    }

    return headers;
  }

with:

public String getBodyContentType() {
    if (contentType != null) {
      return contentType;
    }
    if(entity == null) {
      return null;
    }
    return entity.getContentType().getValue();
  }
...
 public Map<String, String> getHeaders() throws AuthFailureError {
    Map<String, String> headers = super.getHeaders();
    if (headers == null || headers.equals(Collections.emptyMap())) {
        headers = new HashMap<String, String>();
    }
    if (apiHeaders != null && !apiHeaders.equals(Collections.emptyMap())) {
        headers.putAll(apiHeaders);
    }
    if((entity == null) && (contentType != null)) {
        headers.put("Content-Type", contentType);
    }

    return headers;
  }

and now it works :)

POST /api/things HTTP/1.1
Accept: application/json
User-Agent: Swagger-Codegen/1.0.0/android
Content-Type: application/json
...

and getting HTTP 200

All 8 comments

@rasuldev in your spec, have you set a proper consumes for the POST JSON body operation?

For Android, you can also use the Java API client: https://github.com/swagger-api/swagger-codegen/wiki/FAQ#how-can-i-generate-an-android-sdk

@wing328 yes, I set application/json for consumes.
default
Full json spec can be found here. Mentioned in first comment error raises in method api/account/register.

@rasuldev thanks for providing more details. Can you try the Java (okhttp-gson or retrofit2) API client to see if it has similar issue?

@rasuldev do you need help generating Java API client (okhttp-gson, retrofit2)?

Closing this as there's no update.

Having the same issue.
A simple POST that swagger.yaml declares as consumes "application/json" is sent to server with two content types.
From WireShark "copy as plain text" of the http packet:

POST /api/things HTTP/1.1
Accept: application/json
User-Agent: Swagger-Codegen/1.0.0/android
Content-Type: application/json
Content-Type: text/plain; charset=UTF-8
...

Debugging using Android studio, I see that for POST methods, HurlStack.addBodyIfExists()is called, and for POSTs with a body (which most POSTs have), it calls

connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());

request.getBodyContentType() takes the content type from the request's org.apache.http.HttpEntity. The instance of the HttpEntity is actually a StringEntity, which returns a text/plain content type by default.

the fix suggested by @rasuldev will possibly work with other servers, but does not work with swagger-generated "jaxrs" server. The reason is that there are 2 lines with Content-Type: application/json in the request.

POST /api/things HTTP/1.1
Accept: application/json
User-Agent: Swagger-Codegen/1.0.0/android
Content-Type: application/json
Content-Type: application/json
...

Sounds like the correct fix is to change io.swagger.client.request.PostRequest to include only one content-type. It currently has two: one in field contentType and another in field HttpEntity.

For now, I locally changed PostRequest to return the content-type of contentType when asked for the body's content-type (could have used the suggestion from @rasuldev but had to also change the contentType sent to the PostRequest c'tor to null)

replaced

public String getBodyContentType() {
    if(entity == null) {
      return null;
    }
    return entity.getContentType().getValue();
  }
...
 public Map<String, String> getHeaders() throws AuthFailureError {
    Map<String, String> headers = super.getHeaders();
    if (headers == null || headers.equals(Collections.emptyMap())) {
        headers = new HashMap<String, String>();
    }
    if (apiHeaders != null && !apiHeaders.equals(Collections.emptyMap())) {
        headers.putAll(apiHeaders);
    }
    if(contentType != null) {
        headers.put("Content-Type", contentType);
    }

    return headers;
  }

with:

public String getBodyContentType() {
    if (contentType != null) {
      return contentType;
    }
    if(entity == null) {
      return null;
    }
    return entity.getContentType().getValue();
  }
...
 public Map<String, String> getHeaders() throws AuthFailureError {
    Map<String, String> headers = super.getHeaders();
    if (headers == null || headers.equals(Collections.emptyMap())) {
        headers = new HashMap<String, String>();
    }
    if (apiHeaders != null && !apiHeaders.equals(Collections.emptyMap())) {
        headers.putAll(apiHeaders);
    }
    if((entity == null) && (contentType != null)) {
        headers.put("Content-Type", contentType);
    }

    return headers;
  }

and now it works :)

POST /api/things HTTP/1.1
Accept: application/json
User-Agent: Swagger-Codegen/1.0.0/android
Content-Type: application/json
...

and getting HTTP 200

@kfirwolfson description is precise. @wing328 can we re-open?

The problem is in this StringEntity constructor, where for request = new PostRequest(url, headers, contentType, new StringEntity(serialize(body), "UTF-8"), stringRequest, errorListener); ContentType.TEXT_PLAIN.getMimeType() is enforced. And then HurlStack allows two ContentType headers to be present.

I have hit this same problem. I'm using an IIS server and having two Content-Type headers throws a failure.

It would be great if this issue could be reopened and the the generated code updated so I don't have to modify it after every codegen.

Incidentally, my solution was to change:

request = new PostRequest(url, headers, contentType, new StringEntity(serialize(body), "UTF-8"), stringRequest, errorListener);

to

request = new PostRequest(url, headers, null, new StringEntity(serialize(body), APPLICATION_JSON), stringRequest, errorListener);

This resulted in just the one content type being added.

Was this page helpful?
0 / 5 - 0 ratings