Skip to content

Support binary data in body #669

Closed
SeppPenner/swagger-codegen
#22

Description

@mgvirtzm

Hello,

Our Swagger 2.0 specs include several services that pass and receive binary data in the body (type="string, format="byte"), represented by content-type application/octet-stream (with respective consumes and produces clauses).

When generating Java for these services, code generator treats input and output as String, which is not suitable for binary data.

It would be advisable to generate byte[] parameter, and also avoid serialization treatment.

I would consider contributing this code (for Java only), if you find this appropriate.

Thanks,
Michael.

Activity

  1. fehguy commented on Apr 21, 2015

    @fehguy
    Contributor

    Would like this implantation especially with tests. Thank you!

  2. mgvirtzm commented on Apr 28, 2015

    @mgvirtzm
    Author

    The following workaround worked for me, and currently I'm not planning to modify the codegen sources.

    Preconditions:

    1. binary body is defined in Swagger JSON as follows (might occur in multiple paths; 'binaryData' name is mandatory):

                  {
                      "name": "BinaryData",
                      "in": "body",
                      "required": true,
                      "schema": {
                          "type": "string",
                          "format": "byte"  
                      }
                  }
      
    2. The following overriding class is placed at appropriate location in the source tree:

    package com.intuit.idps;
    import com.sun.jersey.api.client.Client;
    import com.sun.jersey.api.client.ClientResponse;
    import com.sun.jersey.api.client.WebResource.Builder;
    
    import io.swagger.client.ApiException;
    import io.swagger.client.ApiInvoker;
    
    import java.util.Map;
    import java.io.DataInputStream;
    import javax.ws.rs.core.Response.Status.Family;
    import java.io.IOException;
    
    
    public class BinaryApiInvoker extends ApiInvoker {
    
      static {
        INSTANCE = new BinaryApiInvoker();
        setUserAgent("Java-Swagger");
      }
    
      public static ApiInvoker getInstance() {
        return INSTANCE;
      }
    
      public byte[] invokeBinaryAPI(String host, String path, String method, Map<String, String> queryParams, byte[] body, Map<String, String> headerParams, Map<String, String> formParams, String contentType) throws ApiException {
        Client client = getClient(host);
    
        StringBuilder b = new StringBuilder();
    
        for(String key : queryParams.keySet()) {
          String value = queryParams.get(key);
          if (value != null){
            if(b.toString().length() == 0 && !path.contains("?"))
              b.append("?");
            else
              b.append("&");
            b.append(escapeString(key)).append("=").append(escapeString(value));
          }
        }
        String querystring = b.toString();
    
        Builder builder = client.resource(host + path + querystring).accept("application/octet-stream");
        for(String key : headerParams.keySet()) {
          builder = builder.header(key, headerParams.get(key));
        }
    
        for(String key : defaultHeaderMap.keySet()) {
          if(!headerParams.containsKey(key)) {
            builder = builder.header(key, defaultHeaderMap.get(key));
          }
        }
        ClientResponse response = null;
    
        if("GET".equals(method)) {
          response = (ClientResponse) builder.get(ClientResponse.class);
        }
        else if ("POST".equals(method)) {
          if(body == null)
            response = builder.post(ClientResponse.class, null);
          else
            response = builder.type(contentType).post(ClientResponse.class, body);
        }
        else if ("PUT".equals(method)) {
          if(body == null)
            response = builder.put(ClientResponse.class);
          else {
              response = builder.type(contentType).put(ClientResponse.class, body);
          }
        }
        else if ("DELETE".equals(method)) {
          if(body == null)
            response = builder.delete(ClientResponse.class);
          else
            response = builder.type(contentType).delete(ClientResponse.class, body);
        }
        else {
          throw new ApiException(405, "unknown method " + method);
        }
        if(response.getClientResponseStatus() == ClientResponse.Status.NO_CONTENT) {
          return null;
        }
        else if(response.getClientResponseStatus().getFamily() == Family.SUCCESSFUL) {
          if(response.hasEntity()) {
        DataInputStream stream = new DataInputStream(response.getEntityInputStream());
        byte[] data = new byte[response.getLength()];
        try {
          stream.readFully(data);
        } catch (IOException ex) {
          throw new ApiException(500, "Error obtaining binary response data");
        }
            return data;
          }
          else {
            return new byte[0];
          }
        }
        else {
          String message = "error";
          if(response.hasEntity()) {
            try{
              message = String.valueOf(response.getEntity(String.class));
            }
            catch (RuntimeException e) {
              // e.printStackTrace();
            }
          }
          throw new ApiException(
                    response.getClientResponseStatus().getStatusCode(),
                    message);
        }
      }
    
    
    
    }

    Each time after generating the code, run the following commands:

    sed -i -- '3 i\
    import com.intuit.idps.BinaryApiInvoker;' src/main/java/io/swagger/client/api/DefaultApi.java
    sed -i -- '/ApiInvoker apiInvoker/ c\
      BinaryApiInvoker apiInvoker = (BinaryApiInvoker)BinaryApiInvoker.getInstance();' src/main/java/io/swagger/client/api/DefaultApi.java
    sed -i -- '/String binaryData/,/ApiException ex/{ s/public String/public byte[]/; s/String binaryData/byte[] binaryData/; s/String response/byte[] response/; s/invokeAPI/invokeBinaryAPI/; s/postBody,/binaryData,/; s/(String).*/response;/;}' src/main/java/io/swagger/client/api/DefaultApi.java
    sed -i -- 's/\&amp;/\&/g' src/main/java/io/swagger/client/api/DefaultApi.java  # codegen escapes & in path, this may be an issue?
    sed -i -- 's/private /protected /g' src/main/java/io/swagger/client/ApiInvoker.java

    I'm not closing the issue (anyway, preferred to implement it without workaround).

  3. added this to the v2.1.0 milestone on May 4, 2015
  4. wing328 commented on Jan 23, 2016

    @wing328
    Contributor

    Java API client now supports binary format via #1898

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions