B4J Question Sending Json Requests

potman100

Active Member
Licensed User
Longtime User
Hi

I am having some issues getting a json call to work, I want to mimic the functionality
of a third party android app.

I used jadx to decompile the apk and get the source, but just can't seem to get it to work
even though I think I am sending the call as they do in the app, I dont do a lot of java.

The website allows you to sell media to them and they use a shopping basket system, looking a the
source when you start the app, it firsts checks if there is a basket open for you
using the following code :

B4X:
  public void performBasketKeyLogin(boolean login) {
        try {
            this.se = new StringEntity("{\"customerId\":,\"customerKey\":}", Hex.DEFAULT_CHARSET_NAME);
            JSONObject json_data = new JSONObject((String) new putItemBasket().execute(new String[]{new StringBuilder(String.valueOf(this.string_data.mainUrl())).append("offer/create/?apiKey=").append(this.string_data.appApiKey()).append("&site=").append(this.string_data.siteType()).toString()}).get());
            if (json_data.getString("status").equalsIgnoreCase("ok")) {
                setBasketDetails(json_data.getJSONObject("data").getString("basketId"), json_data.getJSONObject("data").getString("basketKey"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


Beaking the above code down, the json part seems to me to be sending

customerId
customerKey

with blank values, I take it this will make a request to create a new basket or pass back
any current basket open on their system ?

It calls putItemBasket() as part of the call

B4X:
 class putItemBasket extends AsyncTask<String, String, String> {
        putItemBasket() {
        }

        protected String doInBackground(String... uri) {
            HttpClient httpclient = new DefaultHttpClient(new BasicHttpParams());
            try {
                [b]MainActivity.this.se.setContentType(OAuth.FORM_ENCODED);[/b]
                HttpPut httpget = new HttpPut(uri[0]);
                httpget.setEntity(MainActivity.this.se);
                HttpResponse response = httpclient.execute(httpget);
                StatusLine statusLine = response.getStatusLine();
                System.out.println("STATUS LINE >>> " + statusLine);
                if (statusLine.getStatusCode() == ParseException.USERNAME_MISSING) {
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    response.getEntity().writeTo(out);
                    out.close();
                    return out.toString();
                }
                response.getEntity().getContent().close();
                throw new IOException(statusLine.getReasonPhrase());
            } catch (ClientProtocolException e) {
                return null;
            } catch (IOException e2) {
                return null;
            }
        }

I take it doInBackground just creates a new thread ? but not to sure, the rest just looks
like an http call, but I am not sure about MainActivity.this.se.setContentType(OAuth.FORM_ENCODED);.

I want to send this request from B4A, and have tried various http request calls but keep getting

{"status":"ERROR","code":"GEN01","message":"The method you specified could not be found"}

but I have followed the source and think it can only be the json params that I am not sending
correctly ?

I would appriciiate any help with this, I do not really do a lot of Java programming, so I might
to missing something very basic !

Regards

Potman
 

potman100

Active Member
Licensed User
Longtime User
Hi Erel

You comment about put made me have a good look at the put posts, and that seemed to be the issue

Once I added PutString & PutBytes to the Flicker example it allow me to run the request.

As always thanks for your help.
 
Upvote 0

potman100

Active Member
Licensed User
Longtime User
Hi Again

Just a quick question, the method above work fine, but when I try to use another method I get the following error :

B4X:
</HEAD>
<BODY>
<H1>Method Not Allowed</H1>
The HTTP verb used to access this page is not allowed.<P>
<HR>
<ADDRESS>
Web Server at wbb.icanseehosting.co.uk
</ADDRESS>
</BODY>
</HTML>

<!--
- Unfortunately, Microsoft has added a clever new
- "feature" to Internet Explorer. If the text of
- an error's message is "too small", specifically
- less than 512 bytes, Internet Explorer returns
- its own error message. You can turn that off,
- but it's pretty tricky to find switch called
- "smart error messages". That means, of course,
- that short error messages are censored by default.
- IIS always returns error messages that are long
- enough to make Internet Explorer happy. The
- workaround is pretty simple: pad the error
- message with a big comment like this to push it
- over the five hundred and twelve bytes minimum.
- Of course, that's exactly what you're reading
- right now.
-->

The Call is :

B4X:
  public void postISBN(String isbn) {
        try {
            this.se = new StringEntity("{\"basketKey\":\"" + getBasketKey() + "\",\"isbn\":\"" + isbn + "\"}", Hex.DEFAULT_CHARSET_NAME);
            String result_data = (String) new putItemBasket().execute(new String[]{new StringBuilder(String.valueOf(this.string_data.mainUrl())).append("offer/").append(getBasketId()).append("/item/?apiKey=").append(this.string_data.appApiKey()).append("&site=").append(this.string_data.siteType()).toString()}).get();

I am unsure about Hex.DEFAULT_CHARSET_NAME, does this convert the string to HEX?, or just set a HEADER ?

It then calls putItemBasket()

B4X:
class putItemBasket extends AsyncTask<String, String, String> {
        putItemBasket() {
        }

        protected String doInBackground(String... uri) {
            HttpClient httpclient = new DefaultHttpClient(new BasicHttpParams());
            try {
                MainActivity.this.se.setContentType(OAuth.FORM_ENCODED);
                HttpPut httpget = new HttpPut(uri[0]);
                httpget.setEntity(MainActivity.this.se);
                HttpResponse response = httpclient.execute(httpget);
                StatusLine statusLine = response.getStatusLine();
                System.out.println("STATUS LINE >>> " + statusLine);
                if (statusLine.getStatusCode() == ParseException.USERNAME_MISSING) {
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    response.getEntity().writeTo(out);
                    out.close();
                    return out.toString();
                }
                response.getEntity().getContent().close();
                throw new IOException(statusLine.getReasonPhrase());
            } catch (ClientProtocolException e) {
                return null;
            } catch (IOException e2) {
                return null;
            }
        }

I am not sure what to do with OAuth.FORM_ENCODED.

Any idea's would be appreciated.

Regards

Potman
 
Upvote 0

potman100

Active Member
Licensed User
Longtime User
Hmm, tried your suggestion and now it does not find the url, it returns an error 404 - Status not found.

I am passing thr params as follows :

B4X:
Array As String("basketKey", "563f5fb43989a067370350", "isbn", "724352356225")

Any Idea's ?

Regards

Potman
 
Upvote 0

potman100

Active Member
Licensed User
Longtime User
Hi

I noticed that Download2 added a ? so I moved the api key and site param into the param list, have tried both ways by changing the download2 sub

B4X:
ArrayAsString("apiKey", "uvukavVKTRnsWY3vXWNobT4ub26M9er", "site", "wbb", "basketKey", "563f5fb43989a067370350", "isbn", "724352356225")

The code for the link is :

B4X:
          this.se = new StringEntity("{\"basketKey\":\"" + getBasketKey() + "\",\"isbn\":\"" + isbn + "\"}", OAuth.ENCODING);
            String result_data = (String) new putItemBasket().execute(new String[]{new StringBuilder(String.valueOf(this.string_data.mainUrl())).append("offer/").append(getBasketId()).append("/item/?apiKey=").append(this.string_data.appApiKey()).append("&site=").append(this.string_data.siteType()).toString()}).get();

It all seems the same as the PerformBasketKeyLogin above apart from

B4X:
class putItemBasket extends AsyncTask<String, String, String> {
 putItemBasket() {}

 protected String doInBackground(String... uri) {
 HttpClient httpclient = new DefaultHttpClient(new BasicHttpParams());
 try {
 MainActivity.this.se.setContentType(OAuth.FORM_ENCODED);
 HttpPut httpget = new HttpPut(uri[0]);

It seems to be OAuth.FORM_ENCODED that I am having issues with, after following the class and looking at the code


B4X:
    public static <T extends Entry<String, String>> void formEncode(Collection<T> parameters, OutputStream into) throws IOException {
        if (parameters != null) {
            boolean first = true;
            for (T entry : parameters) {
                if (first) {
                    first = false;
                } else {
                    into.write(38);
                }
                into.write(percentEncode(safeToString(entry.getKey())).getBytes());
                into.write(61);
                into.write(percentEncode(safeToString(entry.getValue())).getBytes());
            }
        }
    }

    public static <T extends Entry<String, String>> String formEncode(Collection<T> parameters) throws IOException {
        ByteArrayOutputStream b = new ByteArrayOutputStream();
        formEncode(parameters, b);
        return new String(b.toByteArray());
    }

It looks like it is encoding the url ?

Any Idea's

Regards

Potman
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
I noticed that Download2 added a ? so I moved the api key and site param into the param list, have tried both ways by changing the download2 sub
That's true.

The java code creates a json string. I recommend you to use a tool such as wireshark to monitor the network traffic and then you will be able to see how the message should be built.
 
Upvote 0

potman100

Active Member
Licensed User
Longtime User
Thanks for that, I have tried that before but its an ssl request and from what I can remember
I was unable to get the data.

I keep trying.
 
Upvote 0

potman100

Active Member
Licensed User
Longtime User
Hi Again

I managed to decompile to apk to smali, and edit the smali file just to add some text and then
recompiled, signed and installed aok, and all work well and the text I added is shown in the log.

B4X:
ublic void postISBN(String isbn) {
        try {
            this.se = new StringEntity("{\"basketKey\":\"" + getBasketKey() + "\",\"isbn\":\"" + isbn + "\"}", OAuth.ENCODING);
            String result_data = (String) new putItemBasket().execute(new String[]{new StringBuilder(String.valueOf(this.string_data.mainUrl())).append("offer/").append(getBasketId()).append("/item/?apiKey=").append(this.string_data.appApiKey()).append("&site=").append(this.string_data.siteType()).toString()}).get();
            System.out.println("RESULT_DATA " + result_data);

I just changed

B4X:
System.out.println("RESULT_DATA " + result_data);

to

B4X:
System.out.println("123456 RESULT_DATA " + result_data);

When looking at the code above, we have this.se which is a string, have you any idea how I would change the following smali code
to instead of passing result_data, to System.out.println, pass this.se ?

B4X:
move-result-object v12

    check-cast v12, Ljava/lang/String;

    .line 401
    .local v12, result_data:Ljava/lang/String;
    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;

    new-instance v1, Ljava/lang/StringBuilder;

    const-string v2, "123456 RESULT_DATA "

    invoke-direct {v1, v2}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V

This way I think I should be able to get the encode params ?

Regards

Potman
 
Upvote 0
Top