2

I'm trying to write a POST call to Google Translate with Jersey 1.5. This is my code:

package main;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.core.util.MultivaluedMapImpl;

import javax.ws.rs.core.MultivaluedMap;

public class Main {

    private static String GOOGLE_TRANSLATE_URL = "https://www.googleapis.com/language/translate/v2";

    private static String translateString(String sourceString, String sourceLanguage, String targetLanguage) {
        String response;
        Client c = Client.create();

        WebResource wr = c.resource(GOOGLE_TRANSLATE_URL);
        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
        params.add("q", sourceString);
        params.add("source", sourceLanguage);
        params.add("target", targetLanguage);
        params.add("key", "xxxx");
        wr.header("X-HTTP-Method-Override", "GET");
        response = wr.post(String.class, params);

        return response;
    }

    public static void main(String[] args) {
        System.out.println(translateString("Hello", "en", "sv"));    
    }
}

When I run this, all I get back is this: com.sun.jersey.api.client.UniformInterfaceException: POST https://www.googleapis.com/language/translate/v2 returned a response status of 404.

I've managed to accomplish this with a simple cURL command like so:

curl --header "X-HTTP-Method-Override: GET" -d key=xxxx -d q=Hello -d source=en -d target=sv https://www.googleapis.com/language/translate/v2

Thanks in advance!

vrutberg
  • 1,981
  • 1
  • 20
  • 28
  • Why are you using a POST if you have an empty body? Try using GET. – Peter Knego Jan 21 '11 at 08:50
  • The reason I want to use POST is because otherwise I will be limited by the length of an URL when translating very long texts. The length of an URL seem to vary a lot between browser and server implementations. http://www.boutell.com/newfaq/misc/urllength.html – vrutberg Jan 21 '11 at 09:15

4 Answers4

3

I suspect that POST with zero Content-Length is not something a normal HTTP server will accept. The RFC does not define this case, but the main assumption of POST is that you're sending a message body.

Looking at the Google API, they mention the following

You can also use POST to invoke the API if you want to send more data in a single request. The q parameter in the POST body must be less than 5K characters. To use POST, you must use the X-HTTP-Method-Override header to tell the Translate API to treat the request as a GET (use X-HTTP-Method-Override: GET).

This means that instead of adding q, source and target parameters in the URL, you need to do so in the POST body. I'm not familiar with the Jersey API, from a brief look you just need to add params as an explicit second parameter to the .post call, remove the queryParams() call, and set the Content-Length properly.

RomanK
  • 1,258
  • 7
  • 18
  • Thanks for your reply! I've done exactly what you wrote, added my params variable as second parameter to the .post() call and removed the line where I used to set the Content-Length. This, however, renders a UniformInterfaceException saying "returned a response status of 404". – vrutberg Jan 24 '11 at 11:24
  • You still need to set the Content-Length to match the length of your posted data. I don't know offhand why you get a 404 back, but Content-Length needs to be set in any case. If it still does not work, please attach a traffic capture with Wireshark, maybe this will give us an idea on what's going wrong. – RomanK Jan 24 '11 at 11:49
  • Your problem might be the wr.queryParams(params) call w/ POST; it shouldn't have any request parameters. Otherwise, I'm with RomanK, Wireshark would remove any doubt about the validity of the request (though I suspect that Jersey takes care of the content length for you). – Danny Thomas Jan 24 '11 at 11:54
  • The solution is described in detail in http://stackoverflow.com/a/9432636/16673 - it is C#, but easy to adapt. – Suma Jul 04 '12 at 14:46
2

I think the best and correct way is this

private static final String gurl = "www.googleapis.com";
private static final String gpath = "/language/translate/v2/detect";


public String detectLangGooglePost(String text) throws SystemException {

    List<NameValuePair> qparams = new ArrayList<NameValuePair>();
    qparams.add(new BasicNameValuePair("key", key));

    URI uri;
    try {
        uri = URIUtils.createURI("https", gurl, -1, gpath, URLEncodedUtils.format(qparams, "UTF-8"), null);
    } catch (URISyntaxException e) {
        throw new SystemException("Possibly invalid URI parameters", e);
    }

    HttpResponse response = getPostResponse(uri, text);
    StringBuilder builder = getBuilder(response);
    String language = getLanguage(builder);

    return language;
}

private HttpResponse getPostResponse(URI uri, String text) throws SystemException {

    List<NameValuePair> qparams = new ArrayList<NameValuePair>();
    qparams.add(new BasicNameValuePair("q", text));

    HttpResponse response;
    HttpClient httpclient = new DefaultHttpClient();
    HttpPost httpPost = new HttpPost(uri);
    httpPost.addHeader("X-HTTP-Method-Override", "GET");
    try {
        httpPost.setEntity(new UrlEncodedFormEntity(qparams));
        response = httpclient.execute(httpPost);
    } catch (Exception e) {
        throw new SystemException("Problem when executing Google get request", e);
    }

    int sc = response.getStatusLine().getStatusCode();
    if (sc != HttpStatus.SC_OK)
        throw new SystemException("google status code : " + sc);
    return response;
}

private StringBuilder getBuilder(HttpResponse response) throws SystemException {
    HttpEntity entity = response.getEntity();
    if (entity == null)
        throw new SystemException("response entity null");

    StringBuilder builder = new StringBuilder();
    BufferedReader in = null;
    String str;
    try {
        in = new BufferedReader(new InputStreamReader(entity.getContent()));
        while ((str = in.readLine()) != null)
            builder.append(str);
    } catch (IOException e) {
        throw new SystemException("Reading input stream of http google response entity problem", e);
    } finally {
        IOUtils.closeQuietly(in);
    }
    if (builder.length() == 0)
        throw new SystemException("content stream of response entity empty has zero length");
    return builder;
}

private String getLanguage(StringBuilder builder) throws SystemException {
    JSONObject data = null;
    JSONArray detections = null;
    String language = null;

    JSONObject object = (JSONObject) JSONValue.parse(builder.toString());
    if (object == null)
        throw new SystemException("JSON parsing builder object returned null");

    if (object.containsKey("data") == false)
        throw new SystemException("JSONObject doesn't contain data key");
    data = (JSONObject) object.get("data");

    detections = (JSONArray) data.get("detections");
    if (detections == null)
        throw new SystemException("JSON detections is null");

    JSONObject body = (JSONObject) ((JSONArray) detections.get(0)).get(0);
    if (body == null)
        throw new SystemException("detections body is null");

    if (body.containsKey("language") == false)
        throw new SystemException("language key is null");
    language = (String) body.get("language");

    if (language == null || language.equals(unknown))
        throw new SystemException("Google lang detection - resulting language : " + language);
    return language;
}
lisak
  • 21,611
  • 40
  • 152
  • 243
-1

I was able to send very long text like this!

Client:

MultivaluedMap<String,String> formData = new MultivaluedMapImpl();
formData.add("text", text);

WebResource resource = Client.create().resource(getBaseURI()).path("text2rdf");
return resource.type("application/x-www-form-urlencoded").post(String.class, formData);

Server:

@POST
@Produces("text/whatever")
public String textToRdf (
        @FormParam("text") String text) {...
Daniel Gerber
  • 3,226
  • 3
  • 25
  • 32
-1

I switched to Apache HttpClient 4.x and solved it like this instead:

public class Main {

    private static String GOOGLE_TRANSLATE_URL = "https://www.googleapis.com/language/translate/v2";
    private static String GOOGLE_API_KEY = "xxxx";

    private static String translateString(String sourceString, String sourceLanguage, String targetLanguage) {

        String response = null;

        // prepare call
        HttpClient client = new DefaultHttpClient();
        HttpPost post = new HttpPost(GOOGLE_TRANSLATE_URL+"?q="+sourceString+"&source="+sourceLanguage+"&target="+targetLanguage+"&key="+GOOGLE_API_KEY);
        post.setHeader("X-HTTP-Method-Override", "GET");

        try {

            // make the call
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            response = client.execute(post, responseHandler);

        } catch (IOException e) {
            // todo: proper error handling
        }

        return response;
    }

    public static void main(String[] args) {
        System.out.println(translateString("hello", "en", "sv"));
    }

}

Don't really know why this works better than Jersey, but it works. Thanks for trying to help!

vrutberg
  • 1,981
  • 1
  • 20
  • 28
  • 1
    I believe what you did here is practically a POST request with parameters not in the request body but in URL. If the sourceString is too long, it will fail .... 413 status code ... Look at HttpPost constructor what happens with the URL – lisak Aug 17 '11 at 11:33