I've created a small class like this one.
public class Device
{
public Device()
{
}
[DeserializeAs(Name = "u_friendly_name")]
//[SerializeAs(Name = "u_friendly_name")]
public string Name { get; set; }
}
}
In order to map "Name" to "u_friendly_name" (as it should be in json) I've added a DeserializeAs declaration. My first question here is.. do I need both DeserializeAs and SerializeAs if I want to POST/GET json or would DeserializeAs suffice?
Doing a GET like this works perfectly. It automatically manages to get the json and cast it to a device as required. However, doing a POST wasn't as simple...
public Device GetDevice(string guid)
{
var request = new RestRequest("api/now/table/x_device", Method.GET);
request.RootElement = "result";
var devices = client.Execute<List<Device>>(request);
return devices.Data.FirstOrDefault(d => d.Guid == guid);
}
I'm trying to do a POST like this.
public void RegisterDevice()
{
Device device = new Device();
device.Name = Environment.MachineName;
var request = new RestRequest("api/now/table/x_device", Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddBody(device); // HERE
Console.WriteLine(request.Parameters[0]);
var response = client.Execute(request);
}
The problem is that the object isn't translated into json. Instead of having "u_friendly_name" in the json output I see "Name". I know this can be solved using Json.Net such as:
string json = JsonConvert.SerializeObject(device);
But that would also require me to add another dependency to my project and add the JsonProperty declaration to my class. I have been able to get POST working with Json.NET, but not GET and vise versa.
Anyone who can point me in the right direction of how to serialize my class using (preferably) only RestSharp?
Edit: After reading a lot of posts on stackoverflow, I get the impression that RestSharp just isn't suitable for deserialization. I found the following link that uses a custom json serializer and while POST works great, I'm not sure what I'm doing wrong with GET: http://bytefish.de/blog/restsharp_custom_json_serializer/
public Device GetDevice(string guid)
{
var request = new RestRequest("api/now/table/x_device", Method.GET);
request.RequestFormat = DataFormat.Json;
request.JsonSerializer = NewtonsoftJsonSerializer.Default;
request.RootElement = "result";
var devices = client.Execute<List<Device>>(request);
return devices.Data.FirstOrDefault(d => d.Guid == guid);
}
The only thing that works is setting both:
[JsonProperty(PropertyName = "u_friendly_name")]
[DeserializeAs(Name = "u_friendly_name")]
But that shouldn't be necessary I hope?
Edit2: I have manged to get POST to work by adding the "AddHandlers" to the RestSharp client as mentioned in the blog post above. I have also changed my class and added a "Devices" definition.
public class Devices
{
[JsonProperty(PropertyName = "result")]
public List<Device> devices { get; set; }
}
public class Device
{
public Device()
{
}
[DeserializeAs(Name = "u_friendly_name")]
//[SerializeAs(Name = "u_friendly_name")]
public string Name { get; set; }
}
}
Now my function looks like this and works as intended:
public Device GetDevice(string guid)
{
var request = new RestRequest("api/now/table/x_uia_sdm_device", Method.GET);
request.RequestFormat = DataFormat.Json;
request.JsonSerializer = NewtonsoftJsonSerializer.Default;
IRestResponse response = client.Execute(request);
var deviceList = JsonConvert.DeserializeObject<Devices>(response.Content);
return deviceList.devices.FirstOrDefault(d => d.Guid == guid);
}
Json:
{
"result": [
{
"u_guid": "12345678",
"u_friendly_name": "DeviceA",
"sys_mod_count": "1",
"sys_updated_on": "2017-03-09 11:58:50",
"sys_tags": "",
"u_config_override": "log_level_local = 5",
"u_running_version": "1.0.0",
"sys_id": "abcdefg",
"sys_created_on": "2017-03-07 12:25:06",
"u_reference_record": "",
"u_status": "Provisioned",
"sys_created_by": "foo#bar.no"
},
{
"u_guid": "34567890",
"u_friendly_name": "DeviceB",
"sys_mod_count": "0",
"sys_updated_on": "2017-03-08 13:23:06",
"sys_tags": "",
"u_config_override": "",
"u_running_version": "",
"sys_id": "abcdefg",
"u_application": "",
"u_reference_record": "",
"u_status": "",
"sys_created_by": "foo#bar.no"
}
]
}
Even though it now works my initial question still stands. If possible I would love to remove Json.Net and only use RestSharp.
Related
I am writing my first Azure FunctionApp, and I am looking to return a valid JSON Object from a deserialized class that was created from a Faker JSON object as shown below:
Class:
public class TestData
{
//based on faker API
public int userId { get; set; }
public int id { get; set; }
public string title { get; set; }
public bool completed { get; set; }
}
Function App Entry point and return code:
public static Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,ExecutionContext context, ILogger log)
{
var configBuilder = new ConfigurationBuilder()
.SetBasePath(context.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
var config = configBuilder.Build();
log.LogInformation("Config Loaded");
// for later use when i get past the current issue
var settings = new AppSettings();
config.Bind("AppSettings", settings);
//Setting this seems to be the route cause, but need to respond correctly?
req.HttpContext.Response.Headers.Add("Content-Type", "application/json");
var worker = new WorkerClass();
var results = worker.DoSomeWork();
return Task.FromResult(results);
//Also Tried:
//return Task.FromResult<IActionResult>(new OkObjectResult(results));
}
Test Worker Class for returned object data and JSON Serialization:
public class WorkerClass
{
public IActionResult DoSomeWork()
{
try
{
var testdata = new TestData
{
userId = 123,
id = 1,
title = "delectus aut autem",
completed = true
};
//formatted as Json correctly
var results = JsonConvert.SerializeObject(testdata, Formatting.Indented);
return new OkObjectResult(results);
}
catch (Exception dswEx)
{
return new BadRequestObjectResult($"\{ERROR: {dswEx.Message}\}");
}
}
}
I have tried several ways to try and reach a clean JSON output using the response object types, as it would be good to add logic based on ObjectResults further down the line, but I suspect I am missing the obvious.
If the return is set to: return Task.FromResult(results); the the response is an escaped JSON output:
"{\"userId\": 1,\"id\": 1,\"title\": \"delectus aut autem\",\"completed\": false}"
if the return is amended to be: return Task.FromResult<IActionResult>(new OkObjectResult(results)); then the response is escaped but encapsulated in a Object wrapper:
{
"value": "{\r\n \"userId\": 1,\"id\": 1,\"title\": \"delectus aut autem\",\"completed\": false}",
"formatters": [],
"contentTypes": [],
"declaredType": null,
"statusCode": 200
}
All I would like to achieve is to have the correct response header as application/json and a result set presented as follows:
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
Thanks in advance.
You want to return new JsonResult(data), where data is your managed structures (not serialized json data like you were doing for some odd reason).
You don't need to serialize an object to json, Net will do it for you.
return Ok(testdata);
In a security run on our code base we are getting a high priority issue i.e. "Deserialization of Untrusted Data" We are using Newtonsoft JSON package for deserialization. Below is the code snippet used and I followed this stack overflow answer(Fixing the deserializing of untrusted data using C#) to solve this issue. It is still not resolved. Any pointers will be helpful.
var idstate = HttpContext.Current.Request.Form[Constants.State];
var jsonSerializerSettings = new JsonSerializerSettings();
LoginRedirection redirectionObject = JsonConvert.DeserializeObject<LoginRedirectionModel>(idstate, jsonSerializerSettings)?.ToLoginRedirection();
Models used for deserialization are below:-
public class LoginRedirection
{
public string stateUrl { get; set; }
public string cartSession { get; set; }
}
public class LoginRedirectionModel
{
public string stateUrl { get; set; }
public string cartSession { get; set; }
public LoginRedirection ToLoginRedirection()
{
return new LoginRedirection { stateUrl = stateUrl, cartSession = cartSession };
}
}
Security exception "OWASP Top 10 2017: A8-Insecure Deserialization" is coming for the below line
LoginRedirection redirectionObject = JsonConvert.DeserializeObject<LoginRedirectionModel>(idstate, jsonSerializerSettings)?.ToLoginRedirection();
JSON:-
{ "stateUrl"="<URL HERE>", "cartSession":"<GUID HERE>"}
Another aspect to problem is:-
When we consume an API using HttpClient and then trying to deserialize the response from API, we are getting the same security warning. Below is the code for consuming and deserializing the API.
public T Post<T, M>(M data, string url, bool ocpSubscriptionHeaderRequired = true)
{
T response = default(T);
try
{
string postBody = JsonConvert.SerializeObject(data);
using (var client = new HttpClient() { Timeout = TimeSpan.FromSeconds(ApiRequestTimeOutInSeconds) })
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (ocpSubscriptionHeaderRequired)
{
client.DefaultRequestHeaders.Remove(Constants.ApiSubscriptionKey);
client.DefaultRequestHeaders.Add(Constants.ApiSubscriptionKey, GenericUtilities.GetConfigData(Constants.ApiSubscriptionKeyValue));
}
HttpResponseMessage result = Task.Run(() => client.PostAsync(url, new StringContent(postBody, Encoding.UTF8, "application/json"))).Result;
if (result.IsSuccessStatusCode)
{
string responseString = Task.Run(() => result.Content.ReadAsStringAsync()).Result;
response = JsonConvert.DeserializeObject<T>(responseString, new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.None
});
}
}
}
catch (Exception ex)
{
_logger.WriteException(ex);
}
return response;
}
You can use the JsonConvert.PopulateObject(sourceJsonString, obj) instead of deserializing it using JsonConvert.DesrializeObject<>();.
Sounds like a false-positive to me.
JsonConvert.DeserializeObject with the wrong JsonSerializerSettings options could be used to construct some other .net type, which could execute something unexpected within its constructor. Or you could write your own serialiser, with some other exploitable bugs in it.
But if you can't use JsonConvert.DeserializeObject for a simple object with two string fields, then every .net app that handles json would already be broken.
Here is my class:
public class PTList
{
private String name;
public PTList() { }
public PTList(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
and my RestSharp POST Request:
protected static IRestResponse httpPost(String Uri, Object Data)
{
var client = new RestClient(baseURL);
client.AddDefaultHeader("X-Authentication", AuthenticationManager.getAuthentication());
client.AddDefaultHeader("Content-type", "application/json");
var request = new RestRequest(Uri, Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddJsonBody(Data);
var response = client.Execute(request);
return response;
}
and when I use the httpPost method with the good URI and a PTList object, the front API anwser that "name" is null.
I think that my PTList object is not serialized as a valid JSON in the request for the API, but can't understand what's going wrong.
There are a couple of issues I can see.
The first is that the object you're sending has no public fields, I'd also simplify the definition a little too:
public class PTList
{
public PTList() { get; set; }
}
The second issue is that you're setting the Content-Type header which RestSharp will do by setting request.RequestFormat = DataFormat.Json
I'd also be tempted to use generics rather than an Object
Your httpPost method would then become:
protected static IRestResponse httpPost<TBody>(String Uri, TBody Data)
where TBody : class, new
{
var client = new RestClient(baseURL);
client.AddDefaultHeader("X-Authentication", AuthenticationManager.getAuthentication());
var request = new RestRequest(Uri, Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddJsonBody(Data);
var response = client.Execute(request);
return response;
}
You could try this instead of AddJsonBody:
request.AddParameter("application/json; charset=utf-8", JsonConvert.SerializeObject(Data), ParameterType.RequestBody);
It's one of the solutions here: How to add json to RestSharp POST request
Json serializer used by RestSharp by default does not serialize private fields. So you can change your class like this:
public class PTList
{
public PTList() { }
public PTList(String name) {
this.name = name;
}
public string name { get; set; }
}
And it will work fine.
If capabilities of default serializer will be not enough (as far as I know - you cannot even rename properties with it, to make Name serialize as name for example) - you can use better serializer, like JSON.NET, like described here for example.
I am trying to post the following JSON with RestSharp:
{"UserName":"UAT1206252627",
"SecurityQuestion":{
"Id":"Q03",
"Answer":"Business",
"Hint":"The answer is Business"
},
}
I think that I am close, but I seem to be struggling with the SecurityQuestion (the API is throwing an error saying a parameter is missing, but it doesn't say which one)
This is the code I have so far:
var request = new RestRequest("api/register", Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddParameter("UserName", "UAT1206252627");
SecurityQuestion securityQuestion = new SecurityQuestion("Q03");
request.AddParameter("SecurityQuestion", request.JsonSerializer.Serialize(securityQuestion));
IRestResponse response = client.Execute(request);
And my Security Question class looks like this:
public class SecurityQuestion
{
public string id {get; set;}
public string answer {get; set;}
public string hint {get; set;}
public SecurityQuestion(string id)
{
this.id = id;
answer = "Business";
hint = "The answer is Business";
}
}
Can anyone tell me what I am doing wrong? Is there any other way to post the Security Question object ?
Many thanks.
You need to specify the content-type in the header:
request.AddHeader("Content-type", "application/json");
Also AddParameter adds to POST or URL querystring based on Method
I think you need to add it to the body like this:
request.AddJsonBody(
new
{
UserName = "UAT1206252627",
SecurityQuestion = securityQuestion
}); // AddJsonBody serializes the object automatically
Thanks again for your help. To get this working I had to submit everything as a single parameter. This is the code I used in the end.
First I made a couple of classes called Request Object and Security Question:
public class SecurityQuestion
{
public string Id { get; set; }
public string Answer { get; set; }
public string Hint { get; set; }
}
public class RequestObject
{
public string UserName { get; set; }
public SecurityQuestion SecurityQuestion { get; set; }
}
Then I just added it as a single parameter, and serialized it to JSON before posting it, like so:
var yourobject = new RequestObject
{
UserName = "UAT1206252627",
SecurityQuestion = new SecurityQuestion
{
Id = "Q03",
Answer = "Business",
Hint = "The answer is Business"
},
};
var json = request.JsonSerializer.Serialize(yourobject);
request.AddParameter("application/json; charset=utf-8", json, ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
and it worked !
To post raw json body string, AddBody(), or AddJsonBody() methods will not work. Use the following instead
request.AddParameter(
"application/json",
"{ \"username\": \"johndoe\", \"password\": \"secretpassword\" }", // <- your JSON string
ParameterType.RequestBody);
It looks like the easiest way to do this is to let RestSharp handle all of the serialization. You just need to specify the RequestFormat like so. Here's what I came up with for what I'm working on. .
public List<YourReturnType> Get(RestRequest request)
{
var request = new RestRequest
{
Resource = "YourResource",
RequestFormat = DataFormat.Json,
Method = Method.POST
};
request.AddBody(new YourRequestType());
var response = Execute<List<YourReturnType>>(request);
return response.Data;
}
public T Execute<T>(RestRequest request) where T : new()
{
var client = new RestClient(_baseUrl);
var response = client.Execute<T>(request);
return response.Data;
}
RestSharp supported from object by AddObject method
request.AddObject(securityQuestion);
I'm unable to globally configure my ASP.NET MVC 4 application (using Web API controllers) so that actions that return HttpResponseMessage with a Json string that includes values of type Newtonsoft.Json.Linq.JValue on dynamic properties.
I can produce required output by serializing the JSON string manually in each action, but ideally I'd like to be able to globally configure the correct behaviour on Application_Start.
To demonstrate my problem, consider the following class:
public class Content : IAccountEntity
{
public string StringProperty { get; set; }
public dynamic DynamicProperty { get; set; }
}
Given the following global serialization configuration:
protected void Application_Start()
{
...
ConfigureJsonFormat();
}
private static void ConfigureJsonFormat()
{
var formatters = GlobalConfiguration.Configuration.Formatters;
formatters.Remove(formatters.XmlFormatter);
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
json.SerializerSettings.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.IsoDateFormat;
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
And the following MVC Action code
public HttpResponseMessage Get(HttpRequestMessage request)
{
... // Other initialisation code
content.StringProperty = "Hello world";
content.DynamicProperty.Email = new Newtonsoft.Json.Linq.JValue("test#test.com");
return Request.CreateResponse(HttpStatusCode.OK, content);
}
I get the following Json response. As you can see, it is missing the value for the dynamic sub property 'email'.
[
{
"stringProperty": "Hello world",
"dynamicProperty": {
email:[]
}
}
]
I can resolve the problem on a per action basis by taking a more manual approach to creating the JSON string response as follows:
public HttpResponseMessage Get(HttpRequestMessage request)
{
... // Other initialisation code
content.StringProperty = "Hello world";
content.DynamicProperty.Email = new Newtonsoft.Json.Linq.JValue("test#test.com");
var jsonSerializerSettings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() };
string jsonStr = JsonConvert.SerializeObject(content, Formatting.Indented, jsonSerializerSettings);
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(jsonStr, Encoding.UTF8, "application/json")
};
}
Which produces the desired output:
[
{
"stringProperty": "Hello world",
"dynamicProperty": {
email: "test#test.com"
}
}
]
I would prefer however to be able to replicate the above behaviour using the global configuration.
Is there a way I can modify my method ConfigureJsonFormat so that the values for dynamics are included on the Json response without having to code for this on an action by action basis?
I'm hoping that I can set something on json.SerializerSettings, but have not been able to find the correct setting as yet.