1

I have used SO to help with several issues in the past. However, I cannot find a solution to something I have been struggling with for 2 days now.

I am a noob, please be kind :)

I have an app that I created using Xamarin Studio, targeted for Android. It is a basic GET request from a Rest Api. It was working perfectly until I realized I was not helping myself when it came time to create the same app in IOS and Windows. Once I changed my project to utilize a PCL I started getting errors, primarily around my RestClient class (originally got from http://www.codeproject.com/Tips/497123/How-to-make-REST-requests-with-Csharp)

From my droid app class:

    var apiUser = GetString(Resource.String.apiUser);
    var apiPass = GetString(Resource.String.apiPass);
    //Get token from API
    string token = authenticate(apiUser,apiPass);

    public static string authenticate(string apiUser, string apiPass)
    {
        Authentication Auth = new Authentication ();
        try
        {
            // set json by passing AuthenticationUrl as endpoint, returns json data
            var o = JObject.Parse(EntryRepository.getJson(PJTApiUrls.getAuthenticationUrl(apiUser,apiPass)));
            Auth.Token = (string)o["Token"];
            return Auth.Token;
        }
        catch (Exception e)
        {
            // Couldn't do stuff. Log the exception.
            // TODO possible timeout, try again, if fails again then return error message
            if (e.Message.Contains("400") || e.Message.Contains("401"))
            {
                string error = string.Format("Invalid credentials, please try again");
                return error;
            } else {
                string error = string.Format ("An error occurred: \r\n{0}", e.Message);
                return error;
            }
        }
    }

getAuthenticationUrl gets the api URL. Here is getJson (in PCL):

    public static string getJson(string endpoint)
    {
        string apiurl = endpoint;
        var client = new _RestClient();
        client.EndPoint = apiurl;
        client.ContentType = "application/json";
        client.Method = HttpVerb.GET;
        //client.Method = HttpVerb.POST;
        client.PostData = "";
        //client.PostData = "{postData: value}";
        //client.PostData = "{'someValueToPost': 'The Value being Posted'}";
        var json = client._MakeRequestAsync();
        // to append parameters, pass them into make request:
        //var json = client.MakeRequest("?param=0");

        return json.ToString();
    }

And for the _RestClient class (in PCL):

    public async Task<string> _MakeRequestAsync()
    {
        try {
            var request = _MakeRequestAsync ("");
            return await request;
        }
        catch (Exception e){
            return e.Message;
        }
    }

    public async Task<string> _MakeRequestAsync(string parameters)
    {
        var uri = new Uri(EndPoint + parameters);
        var request = WebRequest.Create(uri) as HttpWebRequest;

        using (var response = await request.GetResponseAsync () as HttpWebResponse) {
            var responseValue = string.Empty;

            if (response.StatusCode != HttpStatusCode.OK) {
                var message = String.Format ("Request failed. Received HTTP {0}", response.StatusCode);
                throw new Exception (message);
            }

            // grab the response
            using (var responseStream = await Task.Factory.FromAsync<Stream>(request.BeginGetRequestStream, request.EndGetRequestStream, null)) {
            //using (var responseStream = response.GetResponseStream ()) {
                if (responseStream != null)
                    using (var reader = new StreamReader (responseStream)) {
                        responseValue = reader.ReadToEnd ();
                    }
            }

            return responseValue;
        }
    }

responseValue is returning null return await request is saying "Status = Waiting for activation" I have also had the error: "Unexpected character encountered while parsing value: S. Path '', line 0, position 0."

But this works if the RestClient class is within Droid (Instead of the shared PCL) and contains the following:

    public string MakeRequest ()
    {
        return MakeRequest ("");
    }

    public string MakeRequest (string parameters)
    {
        var request = (HttpWebRequest)WebRequest.Create (EndPoint + parameters);

        request.Method = Method.ToString ();
        request.ContentLength = 0;
        request.ContentType = ContentType;

        if (!string.IsNullOrEmpty (PostData) && Method == HttpVerb.POST) {
            var bytes = Encoding.GetEncoding ("iso-8859-1").GetBytes (PostData);
            request.ContentLength = bytes.Length;

            using (var writeStream = request.GetRequestStream ()) {
                writeStream.Write (bytes, 0, bytes.Length);
            }
        }

        using (var response = (HttpWebResponse)request.GetResponse ()) {
            var responseValue = string.Empty;

            if (response.StatusCode != HttpStatusCode.OK) {
                var message = String.Format ("Request failed. Received HTTP {0}", response.StatusCode);
                throw new ApplicationException (message);
            }

            // grab the response
            using (var responseStream = response.GetResponseStream ()) {
                if (responseStream != null)
                    using (var reader = new StreamReader (responseStream)) {
                        responseValue = reader.ReadToEnd ();
                    }
            }

            return responseValue;
        }
    }

I cannot figure this out, any help/guidance is appreciated. Let me know if I can clarify anything.

***** UPDATE ***** Thanks to @milen-pavlov help thus far, here is where I am currently at:

in Android project:

        var apiUser = GetString(Resource.String.apiUser);
        var apiPass = GetString(Resource.String.apiPass);
        //Get token from API
        var token = await authenticate(apiUser,apiPass);

        lblOutput.Text = token;

calls (also in Android project):

    public static async Task<string> authenticate(string apiUser, string apiPass)
    {
        Authentication Auth = new Authentication ();
        try
        {
            // set json by passing AuthenticationUrl as endpoint, returns json data
            var o = JObject.Parse(await EntryRepository.getJson(PJTApiUrls.getAuthenticationUrl(apiUser,apiPass)));
            Auth.Token = (string)o["Token"];
            return Auth.Token;
        }
        catch (Exception e)
        {
            if (e.Message.Contains("400") || e.Message.Contains("401"))
            {
                string error = string.Format("Invalid credentials, please try again");
                return error;
            } else {
                string error = string.Format ("An error occurred: \r\n{0}", e.Message);
                return error;
            }
        }
    }

Calls json class in PCL project:

    public static async Task<string> getJson(string endpoint)
    {
        string apiurl = endpoint;
        var client = new _RestClient();
        client.EndPoint = apiurl;
        client.ContentType = "application/json";
        client.Method = HttpVerb.GET;
        client.PostData = "";
        var json = await client._MakeRequestAsync();

        return json;
    }

which then calls restclient class in PCL project:

    public async Task<string> _MakeRequestAsync()
    {
        var request = _MakeRequestAsync ("");
        return await request;
    }

    public async Task<string> _MakeRequestAsync(string parameters) 
    {
        var uri = new Uri(EndPoint + parameters);
        using (var client = new HttpClient())
        {
            var response = await client.GetAsync(uri);
            return await response.Content.ReadAsAsync<string>();
        };
    }

End result/error:

Response is null

Any guidance is appreciated!

JSourp
  • 35
  • 7
  • You're not `await`ing `_MakeRequestAsync ("");` use `var request = await _MakeRequestAsync ("");` – Milen Mar 12 '15 at 07:03
  • Can you test you service with `fiddler` or something similar to ensure you're actually getting data back? – Milen Mar 12 '15 at 13:48
  • with that addition, it does not build: `var request = await _MakeRequestAsync ("");` "Cannot await 'string'" I just don't understand why this works perfectly (without async/await) when all classes are kept within the Droid project, but once moved to the PCL project everything goes wrong. – JSourp Mar 12 '15 at 19:04
  • Maybe I am over thinking it. The initial api call is to GET (with contentType = application/json) an authentication token (when passed the api url with credentials appended to the url). Here is the raw result returned (via postman): {"PartnerId":"XYZuserApi","Token":"Wa8rhN-LFUm3_vWoPKQsCw"}. In the above sample, I am trying to obtain the token as a variable, using PCL. – JSourp Mar 12 '15 at 20:05
  • just compared with an example of mine, use: `return await result.Content.ReadAsStringAsync();` – Milen Mar 13 '15 at 06:56
  • That did it!!!! Thank you @Milen I appreciate your continued guidance. – JSourp Mar 13 '15 at 12:27

1 Answers1

0

Can you use HttpClient instead? Sample Get request will look similar to this:

        public async Task<string> _MakeRequestAsync(string parameters) 
        {
            var uri = new Uri(EndPoint + parameters);
            using (var client = new HttpClient())
            {
                var response = await client.GetAsync(uri);
                return await result.Content.ReadAsStringAsync();
            };
        }
Milen
  • 8,697
  • 7
  • 43
  • 57
  • Thank you Milen. Using your provided snippet, I got the following error: 'System.Net.Http.HttpContent' does not contain a definition for 'ReadAsAsync'. – JSourp Mar 11 '15 at 15:03
  • After adding the package 'Microsoft.AspNet.WebApi.Client' I was able to get past the above mentioned error (thanks to http://stackoverflow.com/questions/14520762/system-net-http-httpcontent-does-not-contain-a-definition-for-readasasync-an) However, now I get the following when debugging: "await response.Content.ReadAsAsync() response is null" – JSourp Mar 11 '15 at 15:22
  • so if you use it like `var res = await _MakeRequestAsync(myparams)` you're getting `null` ? – Milen Mar 11 '15 at 15:26
  • Have a look at these examples: http://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client – Milen Mar 11 '15 at 15:30
  • I modified the call like so: `var json = await client._MakeRequestAsync();` Now I am getting "cannot convert from Task to string" error in the following line in 'authenticate' (full code above): `var o = JObject.Parse(EntryRepository.getJson(PJTApiUrls.getAuthenticationUrl(apiUser,apiPass)));` – JSourp Mar 11 '15 at 16:22
  • you have to `await` like so: `var o = JObject.Parse(await EntryRepository.getJson(PJTApiUrls.getAuthenticationUrl(apiUser,apiPass)));` - async/await all the way... – Milen Mar 11 '15 at 16:52
  • I am a noob to async/await, so forgive me here. I am now getting `An error occurred: Error reading string. Unexpected token: StartObject. Path '', line 1, position 1.` when trying to get the token returned from `var token = await authenticate(apiUser,apiPass);` – JSourp Mar 11 '15 at 18:19
  • Are you getting an empty/null result or not string? http://stackoverflow.com/questions/12376474/parsing-with-json-net-unexpected-token-startobject – Milen Mar 12 '15 at 07:01
  • I did get the error `An error occurred: Error reading string. Unexpected token: StartObject. Path '', line 1, position 1`. However, I cannot duplicate it again. All I am getting is response is null. In `_MakeRequestAsync` at line `var response = await client.GetAsync(uri);` the uri is the correct api url, but on the next line `return await response.Content.ReadAsAsync();` I get `await response.Content.ReadAsAsync() response is null` – JSourp Mar 12 '15 at 13:11
  • Marked as answer, because @Milen fixed my error in OP comments. Swapping out ReadAsAsync with ReadAsStringAsync resolved my issue. Full replaced line: `return await response.Content.ReadAsStringAsync();` – JSourp Mar 13 '15 at 12:27