JSON conversion issue from JS to Google Cloud Endpoints server - google-cloud-endpoints

When executing the call I get a server exception:
var x = '{"name": "Test","address": { "street": "White House"}}';
gapi.client.realestate.create(x).execute(function(resp) {
console.log(resp);
});
java.lang.ClassCastException: com.google.appengine.repackaged.org.codehaus.jackson.node.TextNode cannot be cast to com.google.appengine.repackaged.org.codehaus.jackson.node.ObjectNode
at com.google.api.server.spi.tools.devserver.RpcApiServlet.doSingleBackendCall(RpcApiServlet.java:131)
at com.google.api.server.spi.tools.devserver.RpcApiServlet.service(RpcApiServlet.java:112)
If I use the same JSON expression in API Explorer, it works. The #ApiMethod is defined as:
#ApiMethod(name = "create", path = "properties", httpMethod = HttpMethod.POST)
public void create(RealEstateProperty property, User user) throws Exception {

The JS Client library methods expect JSON objects and not JSON strings, it does the encoding for the call itself. Also since this is a POST request you will have to define the POST body as a resource parameter for the method (which isn't really well documented unfortunately)
This should work:
var x = {"name": "Test","address": { "street": "White House"}};
gapi.client.realestate.create({"resource": x}).execute( ...

Related

How to retrieve JSON data from HttpContent

I'm buildin a console Web API to communicate with a localhost server, hosting computer games and highscores for them. Every time I run my code, I get this charming error:
fail:
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.NotSupportedException: Deserialization of types without a
parameterless constructor, a singular parameterized constructor, or a
parameterized constructor annotated with 'JsonConstructorAttribute' is
not supported. Type 'System.Net.Http.HttpContent'. Path: $ |
LineNumber: 0 | BytePositionInLine: 1.
This is the method I'm using to post to the database. Note that this method is not in the console application. It is in the ASP.NET Core MvC application opening a web browser and listening for HTTP requests (which can come from the console application).
[HttpPost]
public ActionResult CreateHighscore(HttpContent requestContent)
{
string jasonHs = requestContent.ReadAsStringAsync().Result;
HighscoreDto highscoreDto = JsonConvert.DeserializeObject<HighscoreDto>(jasonHs);
var highscore = new Highscore()
{
Player = highscoreDto.Player,
DayAchieved = highscoreDto.DayAchieved,
Score = highscoreDto.Score,
GameId = highscoreDto.GameId
};
context.Highscores.Add(highscore);
context.SaveChanges();
return NoContent();
}
I'm sending POST requests in a pure C# console application, with information gathered from user input, but the result is exactly the same when using Postman for post requests - the above NotSupportedException.
private static void AddHighscore(Highscore highscore)
{
var jasonHighscore = JsonConvert.SerializeObject(highscore);
Uri uri = new Uri($"{httpClient.BaseAddress}highscores");
HttpContent requestContent = new StringContent(jasonHighscore, Encoding.UTF8, "application/json");
var response = httpClient.PostAsync(uri, requestContent);
if (response.IsCompletedSuccessfully)
{
OutputManager.ShowMessageToUser("Highscore Created");
}
else
{
OutputManager.ShowMessageToUser("Something went wrong");
}
}
I'm new to all this HTTP requests stuff, so if you spot some glaring errors in my code, that would be appreciated. Though, the most important question is, what am I missing, and how can I read from the HttpContent object, to be able to create a Highscore object to send to the database?
It seems to be the string jasonHs... line that is the problem, since the app crashed in exactly the same way, when I commented out the rest of the ActionResult method.
Based on your code, we can find that you make a HTTP Post request with a json string data (serialized from a Highscore object) from your console client to Web API backend.
And in your action method, you create an instance of Highscore manually based on received data, so why not make your action accept a Highscore type parameter, like below. Then the model binding system would help bind data to action parameter(s) automatically.
[HttpPost]
public ActionResult CreateHighscore([FromBody]Highscore highscore)
{
//...

Cannot deserialize the current JSON array - Auth0 Management API Endpoint - despite using online POCO generators

I am working with Auth0 Management API endpoints, the issue with this one.
https://auth0.com/docs/api/management/v2?_ga=2.197148647.957265821.1601726170-1678439180.1588036522#!/Users/get_users
Here is my rest code.
var client = new RestClient(tempapiendpoint);
var request = new RestRequest(Method.GET);
request.AddHeader(header, bearerstring);
request.AddParameter(specificfieldname,specificfields);
request.AddParameter(includefieldname, includetrueorfalse);
IRestResponse response = await client.ExecuteAsync(request);
var myDeserializedClass = JsonConvert.DeserializeObject<Root>(response.Content);
I have the following response.
[
{
"email": "somevalue",
"name": "somevalue",
"nickname": "somevalue",
"user_id": "somevalue"
},
{
"email": "somevalue",
"name": "somevalue",
"nickname": "somevalue",
"user_id": "somevalue"
},
{
"email": "somevalue",
"name": "somevalue",
"nickname": "somevalue",
"user_id": "somevalue"
}
]
At this point, I use an online class generator, such as, https://json2csharp.com/
// Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
public class MyArray {
[JsonProperty("email")]
public string Email;
[JsonProperty("name")]
public string Name;
[JsonProperty("nickname")]
public string Nickname;
[JsonProperty("user_id")]
public string UserId;
}
public class Root {
[JsonProperty("MyArray")]
public List<MyArray> MyArray;
}
and everytime, I get the same error.
Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'RandomStuffGeneratorPrivate.POCO.Root' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
Path '', line 1, position 1.
A few more things I have tried.
I have tried another source for class generation, https://app.quicktype.io/. I get the exact same error.
I have checked the json for validity. it is in the correct format.
even if hand map it, this is a straight forward json.
Further, I noticed that the name of the user class and the collection name are both the same. So, I changed the name for the user class. (it never gave any errors while debugging but I changed it anyway). no change in error
When generating classes, I am taking the json string directly from the live response during debugging, from IRestResponse response.content, just in case the online API documentation is making a mistake.
i have looked at other stack questions, and in those cases, I noticed that there was some mistake related to not having a list. Here, I definitely have mapped (the online generators wont make such a mistake) the returning array to a list.
Looks to me that you are deserializing to the wrong class, try using : JsonConvert.DeserializeObject<MyArray[]>(myJsonResponse)
(looks like Thomas has already provided a solution which i also discovered on my own. Posting some additional details here.)
Ultimately, this seems to have worked for me.
var myDeserializedClass = JsonConvert.DeserializeObject<List<SingleUser>>(response.Content);
I think, the issue was, I was looking for a solution, that would me something like this.
myDeserializedClass
and, I could use it, like this,
var nameOfPerson = myDeserializedClass.MyArray[0].Name;
But, i think that is not how JSON works. Perhaps, if there were other fields in the JSON body, with a mix and match of collection and non-collection values. Since, this was a complete collection, the outer layer gets eliminated or something.
Further, Quicktype class generator clearly says this at the top.
// <auto-generated />
//
// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
//
// using RandomStuffGeneratorPrivate.POCO;
//
// var allUsers445 = AllUsers445.FromJson(jsonString);
and, the function is defined like this.
public static List<AllUsers445> FromJson(string json) => JsonConvert.DeserializeObject<List<AllUsers445>>(json, RandomStuffGeneratorPrivate.POCO.Converter.Settings);

Trying to query "traces" in Application Insights via the REST API

I am attempting to query the Application Insights "traces" data via the API by using the C# example given on the API Quickstart page (https://dev.applicationinsights.io/quickstart) and I think I am having an issue understanding the path to make the call work.
The following was taken from the Quickstart page...
public class QueryAppInsights
{
private const string URL = "https://api.applicationinsights.io/v1/apps/{0}/{1}/{2}?{3}";
public static string GetTelemetry(string appid, string apikey, string queryType, string queryPath, string parameterString)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("x-api-key", apikey);
var req = string.Format(URL, appid, queryType, queryPath, parameterString);
HttpResponseMessage response = client.GetAsync(req).Result;
if (response.IsSuccessStatusCode)
{
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
Console.WriteLine(response.StatusCode);
return response.Content.ReadAsStringAsync().Result;
}
else
{
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
Console.WriteLine(response.StatusCode);
return response.ReasonPhrase;
}
}
}
When I call it using the following parameters I get the following error.
{"error":{"message":"The requested path does not exist","code":"PathNotFoundError"}}
NotFound
public class TestSuite
{
public void CallTest()
{
QueryAppInsights.GetTelemetry("My Application ID", "My API Key", "query", "traces", "timespan=P7D&query=traces%7C%20where%20message%20contains%20%1111a11aa1-1111-11aa-1a1a-11aa11a1a11a%22");
}
}
When I call it replacing the "query" param with "events" I get over 500 rows returned with the following at the top
{"#odata.context":"https://api.applicationinsights.io/v1/apps/GUID PLACE HOLDER/events/$metadata#traces","#ai.messages":[{"code":"AddedLimitToQuery","message":"The query was limited to 500 rows"}
public class TestSuite
{
public void CallTest()
{
QueryAppInsights.GetTelemetry("My Application ID", "My API Key", "events", "traces", "timespan=P7D&query=traces%7C%20where%20message%20contains%20%1111a11aa1-1111-11aa-1a1a-11aa11a1a11a%22");
}
}
When I call it replacing the "events" param with "metrics" I get the following error:
{"error":{"message":"The requested item was not found","code":"ItemNotFoundError","innererror":{"code":"MetricNotFoundError","message":"Metric traces does not exist"}}}
NotFound
public class TestSuite
{
public void CallTest()
{
QueryAppInsights.GetTelemetry("My Application ID", "My API Key", "metrics", "traces", "timespan=P7D&query=traces%7C%20where%20message%20contains%20%1111a11aa1-1111-11aa-1a1a-11aa11a1a11a%22");
}
}
So I don't know if the way I am passing the query is incorrect or if I am trying something that is not possible. The query was taken from the API Explorer page (https://dev.applicationinsights.io/apiexplorer/query) in the "Query" > "GET /query" section and it does work as expected returning the correct row:
traces
| where message contains "1111a11aa1-1111-11aa-1a1a-11aa11a1a11a" (I've replaced the real GUID's with made up ones)
Just in case anyone ever comes across this I wanted to share how I did it successfully. Basically, I was using the wrong URL constant provided by the example on the quickstart (https://dev.applicationinsights.io/quickstart) page. I had to modify it in order to query Traces:
The given example on the quickstart:
private const string URL = "https://api.applicationinsights.io/v1/apps/{0}/{1}/{2}?{3}";
My implementation:
private const string URL = "https://api.applicationinsights.io/v1/apps/{0}/{1}?{2}{3}";
essentially moving the query string params to match what the GET/query API Explorer (https://dev.applicationinsights.io/apiexplorer/query) does when sending a query.

use json format in an URL

I am building a rest API with asp.net my problem is that when I try to add a student to my database like that :
http://localhost:50001/api/Students?&FirstName=cc&LastName=cc&Email=student10#gmail.com&DropOut=false&Live=false&ClassId=1&ImageId=1
I get "the value variable is null",
this is my code to add a student:
// Get All Students
[Route("api/Students")]
public IEnumerable<Student> Get()
{
return _StudentService.Queryable().ToList();
}
// Insert Student
[Route("api/Students/")]
public IEnumerable<Student> Post(Student value)
{
cc.Students.Add(value);
cc.SaveChanges();
return Get();
}
I have used "Fiddler web Debugger" to test my URLs an it works only in this way:
now If I have an angularJS client that tries to add a new student to the database,how can I send data as a json format in an URL
this is how I add a new student from my client angularJS:
$http({method: 'POST', url: 'http://localhost:50001/api/Students?&FirstName=cc&LastName=cc&Email=student10#gmail.com&DropOut=false&Live=false&ClassId=1&ImageId=1})
.success(function (data) {
console.log("success");
}).error(function (data, status, headers, config) {
console.log("data error ...");
});
thanks a lot for help
If you are saying you want a true Rest API you should continue to use the POST verb as it is more semantically right for creating a new student.
Passing a new student on the URL is possible but not in the configuration you have provided.
Your API method expects a POST request and that the new student be located in the HTTP body.
Just configure your angular call to use jsonData and post it to your API.

A simple POST request to Web API not hitting the API at all

From my MVC application, I am trying to make a POST request to these sample end-points (actions) in an API controller named MembershipController:
[HttpPost]
public string GetFoo([FromBody]string foo)
{
return string.Concat("This is foo: ", foo);
}
[HttpPost]
public string GetBar([FromBody]int bar)
{
return string.Concat("This is bar: ", bar.ToString());
}
[HttpPost]
public IUser CreateNew([FromBody]NewUserAccountInfo newUserAccountInfo)
{
return new User();
}
Here's the client code:
var num = new WebAPIClient().PostAsXmlAsync<int, string>("api/membership/GetBar", 4).Result;
And here's the code for my WebAPIClient class:
public class WebAPIClient
{
private string _baseUri = null;
public WebAPIClient()
{
// TO DO: Make this configurable
_baseUri = "http://localhost:54488/";
}
public async Task<R> PostAsXmlAsync<T, R>(string uri, T value)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_baseUri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
var requestUri = new Uri(client.BaseAddress, uri);
var response = await client.PostAsXmlAsync<T>(requestUri, value);
response.EnsureSuccessStatusCode();
var taskOfR = await response.Content.ReadAsAsync<R>();
return taskOfR;
}
}
}
I have the following default route defined for the Web API:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
UPDATE
My code breaks into the debugger until the time the PostAsXmlAsync method on the System.Net.HttpClient code is called. However, no request shows up in Fiddler.
However, if I try to compose a POST request in Fiddler or try to fire a GET request via the browser to one of the API end-points, the POST request composed via Fiddler tells me that I am not sending any data and that I must. The browser sent GET request rightly tells me that the action does not support a GET request.
It just seems like the System.Net.HttpClient class is not sending the POST request properly.
One of the most usual problems is that you don't use the appropriate attribute.
Take into account that there are attributes for ASP.NET MVC and ASP.NET Web API with the same name, but which live in different namespaces:
For Web API you must use the one in System.Web.Http
For MVC, the one in System.Web.MVc
This is a very very usual error, and it affects to allkind of things that exist for both MVC and Web API. So you must be very careful when using something which can exists in bith worlds (for example filters, attributes, or dependency injection registration).
I experienced a similar problem (may not be same one though). In my case, I hadn't given name attribute to the input element. I only figured that out when fiddler showed no post data being sent to the server (just like your case)
<input id="test" name="xyz" type="text" />
Adding the name attribute in the input tag fixed my problem.
However, there is one more thing to note. WebAPI does not put form data into parameters directly. Either you have to create an object with those properties and put that object in the parameter of the post controller. Or you could put no parameters at all like this:
[Route("name/add")]
public async Task Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
return;
}
var provider = PostHelper.GetMultipartProvider();
var result = await Request.Content.ReadAsMultipartAsync(provider);
var clientId = result.FormData["xyz"];
...
Try changing the FromBody to FromUri.
If the parameter is a "simple" type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from a string.
For complex types, Web API tries to read the value from the message body, using a media-type formatter.
Remove FromBody at all and don't make any restrictions in passing parameters (it can be passed at this time either in uri, query string or form submissions (which is kinda a similar to query strings)
[HttpPost]
public string GetFoo(string foo){...}
It will be implicitly parsed and passed.

Resources