Trouble gathering JSON from DB and recieving it on Xamarin Forms - rest

I am following a document on how to recieve data from a database-site where I have my own API-key that is needed to get it out. However I have trouble reaching the JSONdata.
These are the documents I follow.
https://cbis-rest-api.citybreak.com/v1/swagger/ui/index
https://visit.github.io/api-doc/
This is what my code currently looks like:
static public class myData
{
static string apiKey = "myApiKeyNumber";
static string apiApp = "application/json";
static public async Task<JObject> testing()
{
var httpClientRequest = new HttpClient();
httpClientRequest.DefaultRequestHeaders.Add("ApiKey", apiKey);
httpClientRequest.DefaultRequestHeaders.Add("Accept", apiApp);
var result = await httpClientRequest.GetAsync("https://cbis-rest-api.citybreak.com/v1/api/raw/product/");
var resultString = await result.Content.ReadAsStringAsync();
System.Diagnostics.Debug.WriteLine(resultString);
var jsonResult = JObject.Parse(resultString);
System.Diagnostics.Debug.WriteLine(jsonResult);
}
}
And when I want to work with it:
async void loadData()
{
var getInfo = await phpApi.testing();
if (getInfo == null)
{
System.Diagnostics.Debug.WriteLine("no data");
}
else {
System.Diagnostics.Debug.WriteLine("data");
}
}
I think that the httpadress i entered in my JObject task must be wrong because when I run this code I get the crash: "Unexpected character encountered while parsing value: <. Path ", line 0, position 0.".
And when i run the "resultString" System.Diagnostics.Debug.WriteLine(resultString); in the log i get this:
<!DOCTYPE html PUBLIC "-/W3C/DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml/DTD/xhtm1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
<title>404 - File or directory not found.</title>
<style type="text/css">`
Which means that the http (https://cbis-rest-api.citybreak.com/v1/api/raw/product/) does not have the json.
When I look at the documents i think that i am following the correct instruction however so i am a bit confused.
Any help, guidance would be very appreciated. I am not used to working with databases so i appreciate the help a lot!
UPDATED:
static public class myData
{
static string apiKey = "myApiKeyNumber";
static string apiApp = "application/json";
static public async Task<JObject> testing()
{
var httpClientRequest = new HttpClient();
httpClientRequest.DefaultRequestHeaders.Add("ApiKey", apiKey);
httpClientRequest.DefaultRequestHeaders.Add("Accept", apiApp);
var result = await httpClientRequest.GetAsync("https://cbis-rest-api.citybreak.com/v1/api/raw/product/getpaged/1");
var resultString = await result.Content.ReadAsStringAsync();
System.Diagnostics.Debug.WriteLine(resultString);
var jsonResult = JObject.Parse(resultString);
return jsonResult;
}
}
With this code I get the error "Error reading JObject from JsonReader, Path ", line 0, position 0", and i get nothing in the log from the resultString which means that there is no JSON there? I try with many different ID's but with same result. I do not quite know where i find the id of the data either.

The documentation for the API clearly states that the /raw/product/ call needs an {Id} property for the Id of the product you wish to retreive.
To get a list of products, use /raw/product/getpaged/
That still requires a parameter, in this case {pagesize}, which means you still need to add a parameter on how many products per page you wish to retreive.
A correct call would be something like "/raw/product/getpaged/10"
You can verify in your browser that trying to "GET" just /raw/product/ will result in the 404 error that you are seeing in your code.
While adding a parameter for the ID result in an "unauthorized access" error, as I do not have your key and secret.

Related

Azure Function Request POST Body Encoding

I'm wondering whether encoding is something I have to explicitly handle in an Azure function (v3) triggered by an http POST.
For example... which approach is correct (s1, s2, or s3):
[Function("MyFancyFunction")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req,
FunctionContext executionContext)
{
// What charset/encoding will be used if not specified?
var s1 = req.ReadAsString();
// Surely it can't be save to assume UTF8?
var s2 = req.ReadAsString(Encoding.UTF8);
// Use the charset of the first content-type
var ct = MediaTypeHeaderValue.Parse(req.Headers.GetValues("content-type").First());
var s3 = req.ReadAsString(ct.Encoding);
...
}
Thanks!
I put together a simple test function and simple http POST console app.
The console app explicitly encodes the POST payload as Win-1252 (only chosen because it has characters from 0-255). The payload includes a 0x0080 character (utf-8 start sequence) which should cause problems if not decoded using the appropriate encoding.
Here's what the POST payload program looks like:
var client = new HttpClient();
var req = new HttpRequestMessage(HttpMethod.Post, " http://localhost:7071/api/Function1");
req.Content = new StringContent("d\u0080d");
req.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
req.Content.Headers.ContentEncoding.Add("win-1252");
client.Send(req);
The test function looks like:
[Function("Function1")]
public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
FunctionContext executionContext)
{
var s1 = req.ReadAsString(); // s1 == "d\u0080d"... yippie!
var response = req.CreateResponse(HttpStatusCode.OK);
return response;
}
The end result is that YES Azure functions will pay attention to a content-type's charset instruction and use the appropriate Encoding.
which approach is correct (s1, s2, or s3)
Below is the sample code which I used in my environment for encoding data and it worked for me rather than these 3 approaches I suggest to use the below given code.
public static async Task<string> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("SendMessage function requested");
string body = string.Empty;
using (var reader = new StreamReader(req.Body, Encoding.UTF8))
{
body = await reader.ReadToEndAsync();
log.LogInformation($"Message body : {body}");
}
log.LogInformation($"SendMessage processed.");
return body;
}
And here is the Output with the Encoded Data

Bad Request on simple POST Request

I have broke it down to a minimum and still don't know why this happens.
I have the following method in my controller:
#RequestMapping(method = RequestMethod.POST, value = "/myGreatCall")
public String getDynamicData(#RequestBody DataRequest dr) {
return dr.toString();
}
Using the following simple class:
public class DataRequest {
private String type;
//Getters and setters here
}
Now if I try to call this, I get an error 400 as the response.
let url = window.location.protocol+"//"+window.location.host+"/myGreatCall";
let request = new XMLHttpRequest();
request.open("POST", url, true);
request.onload = function () {
console.log(request.response); //Here I read the reponse and get the error 404
};
// This is the data I send as the body
let data = JSON.stringify(
{
type: "myType"
}
);
request.setRequestHeader("Content-Type", "application/json");
request.send(data);
Now from the error I suspect that for some reason it cant map my json object into the java object, but I have no idea why.
I tested the following:
do the request without the Method Parameter, that worked
different data types in the java class
handing over a hardcoded string '{\"type\":\"myType\"}' to the #send()
Any Ideas what I might be doing wrong?
It may be down to JSON serialization. Try this:
let data = JSON.stringify(
{
"type": "myType"
}
);
Ok seems to be something weird. I dont know what caused it, but after a PC restart it worked fine.

Callback function issues in FB.API for Unity

I am using Unity 5.5.2f1 pro and facebook's SDK v 7.9.4
I have a script which after login (managed in a previous scene) sends an API request to FB asking for friends, name and email and sends that info as a POST to a php website.
code:
[Serializable]
public struct FBData {
public string first_name;
public string email;
public string friends;
public string id;}
public class UserManagement : MonoBehaviour {
string urlSaveUserData="some php website";
public Text testTxt;
FBData parsedData;
// Use this for initialization
void Start () {
//Check if it's the first time the user is opening the app.
if (UserInfo.FIRST_TIME) {
//update text (only used for testing, should be removed in production.)
testTxt.text = "Your user id is: " + UserInfo.ID;
//Perform FB.API call to get User Data.
getUserData ();
//Save in SQL table. (won't get here if line in getUserData() is active)
StartCoroutine ("saveUserData");
} else {
//do something else.
}
note: Since this is meant for iOS I have to test it on a device so I'm using text in the screen to display info (think of it as a badly implemented print statement).
The problem: In my callback function for FB.API I write in the text Gameobject (aka testTxt) the parsed information from the response which is saved in the Custom UserInfo clss. It display's correctly but the code gets stuck there. It doesn't continue to the next function. HOWEVER, if I delete/comment that line and don't display anything in the text field. The codes does continue to the POST function BUT the information from the API call is not passed, i.e my custom class is empty (leading me to believe the callback function is not called at all).
public void getUserData(){
string query = "me?fields=first_name,email,friends";
FB.API (query, HttpMethod.GET, Apicallback, new Dictionary<string, string> ());
}
private void Apicallback(IGraphResult result){
//Parse Graph response into a specific class created for this result.
parsedData = JsonUtility.FromJson<FBData>(result.RawResult);
//Pass each field into UserInfo class.
UserInfo.EMAIL = parsedData.email;
UserInfo.FRIENDS = parsedData.friends;
UserInfo.NAME = parsedData.first_name;
UserInfo.FACEBOOKID = parsedData.id;
/*problem area, if I comment line below, then previous information is apparently not stored. If left as is then testTxt displays correct information but code gets stuck there. */
testTxt.text = "This is the info from USerInfoInside the APICallback: " + UserInfo.EMAIL + UserInfo.FRIENDS + UserInfo.FACEBOOKID;
}
The function below is to send info to php website, is there for illustrative purposes:
code:
public IEnumerator saveUserData() {
//get user info (this information is EMPTY if line in getUserData() is commented.
parsedData.id = UserInfo.FACEBOOKID;
parsedData.friends = UserInfo.FRIENDS;
parsedData.first_name = UserInfo.NAME;
parsedData.email = UserInfo.EMAIL;
//translate data into json
string JsonBodyData = JsonUtility.ToJson (parsedData);
//Custom web request (POST method doesnt seem to work very well, documentation example sends empty form)
var w = new UnityWebRequest(urlSaveUserData, "POST");
byte[] bodyRaw = new System.Text.UTF8Encoding().GetBytes(JsonBodyData);
w.uploadHandler = (UploadHandler) new UploadHandlerRaw(bodyRaw);
w.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer();
w.SetRequestHeader("Content-Type", "application/json");
yield return w.Send();
//work with received data...}
Im stuck here any help is appreciated. Thanks!
Be sure to use EscapeURL when using strings directly for JSON or HTTP POST and GET methods. The lack of this treatment tends to screw things over, particulary in iOS platforms.
From what I can see, this code
string query = "me?fields=first_name,email,friends";
should instead be escaped as
string query = WWW.EscapeURL("me?fields=first_name,email,friends");
so characters like "?" won't get encoded as an URL symbol.
I'm assuming you don't need to do that for your illustrative example, because UnityWebRequest already escapes your POST request strings internally, but I can't fully confirm that.

How to add content header to Flurl

I would like to know how to add a content header to a flurl-statement.
The onedrive implementation requires me to add a content-type header to the content, and tried every possible solution with no luck.
I'm forced to use the regular httpclient with the following code.
Public Async Function UploadFile(folder As String, filepath As String) As Task(Of Boolean) Implements ICloud.UploadFile
Dim data As Byte() = File.ReadAllBytes(filepath)
Dim uploadurl As String = "drive/items/" + folder + ":/" + Path.GetFileName(filepath) + ":/" + "content?access_token=" + Token.access_token
Using client As New HttpClient()
client.BaseAddress = New Uri(ApiUrl)
Dim request As HttpRequestMessage = New HttpRequestMessage(HttpMethod.Put, uploadurl)
request.Content = New ByteArrayContent(data)
request.Content.Headers.Add("Content-Type", "application/octet-stream")
request.Content.Headers.Add("Content-Length", data.Length)
Dim response = Await client.SendAsync(request)
Return response.IsSuccessStatusCode
End Using
End Function
I already tried the regular PutJsonAsync method of Flurl, but with no luck.
It's the only non-flurl piece remaining in my code.
Thanx in advance.
The real issue here is that there's currently no out-of-the-box support for sending streams or byte arrays in Flurl. I plan to add some soon, but with the implementation details you already have it's easy to add this yourself with an extension method. (Forgive the C#, hopefully you can translate to VB.)
public static Task<HttpResponseMessage> PutFileAsync(this FlurlClient client, string filepath)
{
var data = File.ReadAllBytes(filepath);
var content = new ByteArrayContent(data);
content.Headers.Add("Content-Type", "application/octet-stream");
content.Headers.Add("Content-Length", data.Length);
return client.SendAsync(HttpMethod.Put, content: content);
}
The above works if you already have a FlurlClient, but as the docs describe it's a good idea to have corresponding string and Url extensions, which can just delegate to the above method:
public static Task<HttpResponseMessage> PutFileAsync(this Url url, string filepath)
{
return new FlurlClient(url).PutFileAsync(filepath);
}
public static Task<HttpResponseMessage> PutFileAsync(this string url, string filepath)
{
return new FlurlClient(url).PutFileAsync(filepath);
}
Tuck those away in a static helper class and they should work seamlessly with Flurl:
await uploadurl.PutFileAsync(filepath)

Using Restangular, can I use a jsonResultsAdapterProvider when needing to override the id field?

I've got a mySql db with non-standard IDs and field names, so I was trying to use both jsonResultsAdapterProvider and setRestangularFields. Here's the code in my app.config file:
RestangularProvider.setBaseUrl(remoteServiceName);
RestangularProvider.setRestangularFields({id: 'personID'});
RestangularProvider.addResponseInterceptor(function(data, operation, what, url, response, deferred) {
if (data.error) {
return data.error;
}
var extractedData = data.result;
return jsonResultsAdapterProvider.$get().camelizeKeys(extractedData);
});
RestangularProvider.addRequestInterceptor(function(elem, operation, what, url) {
return jsonResultsAdapterProvider.$get().decamelizeKeys(elem);
});
It's all good until I try to do a put/save. When I look at the request payload within the browser dev tools, it's: {"undefined":12842} (but the url is correct, so I know the id is set) If I don't use the ResultsAdapter and change the id field to Person_ID, payload looks good, so I know I'm making the right calls to Get and Save the Restangular objects. But for what it's worth, here's the code:
$scope.tests = Restangular.all('members').getList().$object;
vm.testEdit = function () {
$scope.test = Restangular.one('members', 12842).get().then(function(test) {
var copy = Restangular.copy(test);
copy.title = 'xxxx';
copy.put(); // payload was: undefined: 12842
});
}
// I also tried customPUT...
// copy.customPUT(copy, '', {}, {'Content-Type':'application/x-www-form-urlencoded'});
I tried "fixing" the id other ways too, too. like this:
Restangular.extendModel('members', function(model) {
model.id = model.personID;
return model;
});
but that messed up the urls, causing missing ids. And I tried getIdFromElem, but it only got called for my objects created with Restangular.one(), not with Restangular.all()
Restangular.configuration.getIdFromElem = function(elem) {
console.log('custom getIdFromElem called');
if (elem.route === 'members') { // this was never true
return elem[personID];
}
};
It seems like Restangular needs to substitute 'personID' most of the time, but maybe it needs 'Person_ID' at some point during the Save? Any ideas on what I could try to get the Save working?
I finally figured it out! The problem was in my config code and in the way I was decamelizing. Because of inconsistencies in my db field names (most use underscores, but some are already camelCase), I was storing the server's original elem names in an array within the jsonResultsAdapterProvider. But since I was calling jsonResultsAdapterProvider.$get().camelizeKeys(extractedData); within the interceptors, I was reinstantiating the array each time I made a new request. So, the undefined in the PUT request was coming from my decamelizeKeys() method.
My updated config code fixed the problem:
RestangularProvider.setBaseUrl(remoteServiceName);
RestangularProvider.setRestangularFields({id: 'personID'});
var jsonAdapter = jsonResultsAdapterProvider.$get();
RestangularProvider.addResponseInterceptor(function(data, operation, what, url, response, deferred) {
if (data.error) {
return data.error;
}
var extractedData = data.result;
// return extractedData;
return jsonAdapter.camelizeKeys(extractedData);
});
RestangularProvider.addRequestInterceptor(function(elem, operation, what, url) {
return jsonAdapter.decamelizeKeys(elem);
});