How to store only " in C# string - c#

I want to get JSON from string and I need to extract " instead of \" in my code.
Here is my code that I want to use it in:
internal static string ReturnRedditJsonPage(string subredditname)
{
return
$"https://reddit.com/r/{subredditname}.json";
}
internal static Reddit ParseReddit(string subredditname)
{
WebResponse response = HttpWebRequest.CreateHttp(ReturnRedditJsonPage(subredditname)).GetResponse();
string responseContent = new StreamReader(response.GetResponseStream()).ReadToEnd().Replace("\\",#"\").Replace("\"",((char)0x0022).ToString()).Trim();
return JsonConvert.DeserializeObject<Reddit>(responseContent);
}
internal static Uri[] GetMemesLinks(string subredditname)
{
Reddit jsonData = ParseReddit(subredditname);
List<Uri> result = new List<Uri>();
foreach(Child child in jsonData.Data.Children)
{
result.Add(child.Data.Url);
}
return result.ToArray();
}
It gives me back JSON that I cannot parse because of \" in string instead of ". How can I fix it?

You can use JSON.NET coupled with a little LINQ magic to extract all URI(s) out of the sub-reddit API.
Here's a demo, tweak to your requirements:
internal static string ReturnRedditJsonURI(string SubRedditName)
{
return $"https://reddit.com/r/{SubRedditName}.json";
}
// Does a HTTP GET request to the external Reddit API to get contents and de-serialize it
internal static async Task<JObject> ParseReddit(string SubRedditName)
{
string exampleURI = ReturnRedditJsonURI(SubRedditName);
JObject response = new JObject();
using (HttpClient client = new HttpClient())
{
// Make the HTTP request now
HttpResponseMessage msg = await client.GetAsync(exampleURI);
// If HTTP 200 then go ahead and de-serialize
if (msg.IsSuccessStatusCode)
{
string responseBody = await msg.Content.ReadAsStringAsync();
response = JsonConvert.DeserializeObject<JObject>(responseBody);
}
}
return response;
}
// Driver method to extract the URI(s) out of the reddit response
internal static async Task<List<Uri>> GetRedditURI(string SubRedditName)
{
string subRedditName = "Metallica";
JObject redditData = await ParseReddit(SubRedditName);
List<Uri> redditURIList = new List<Uri>();
try
{
// TODO: instead of JObject use concrete POCO, but for now this seems to be it.
redditURIList = redditData["data"]?["children"]?
.Select(x => x["data"])
.SelectMany(x => x)
.Cast<JProperty>()
.Where(x => x.Name == "url")
.Select(x => x.Value.ToString())
.Select(x => new Uri(x, UriKind.Absolute)).ToList() ?? new List<Uri>();
return redditURIList;
}
catch (Exception ex)
{
return redditURIList;
}
}

Related

C# How to polly the GetStreamAsync api call

I have the following code to call the bank api, and convert the response into the object.
public static async Task<ExchangeModel> LoadExchange(string currency, string date)
{
string url = "here is the bank's api url";
var streamTask = await _clientPolicy.LinearHttpRetry.ExecuteAsync(
() => _client.GetStreamAsync(url));
var deserialized = await JsonSerializer.DeserializeAsync<Root>(await streamTask);
var rate = deserialized.ExchangeModel.FirstOrDefault(r => r.Currency == currency);
return rate;
}
I wanted to add polly in order to retry api call if it failed. To do so, I created a policy client, but I don't know how to check if GetStreamAsync is succeed as it is a System.IO.Stream and it has no succeed status or something.
public AsyncRetryPolicy<System.IO.Stream> LinearHttpRetry { get; }
public ClientPolicy()
{
LinearHttpRetry = Policy.HandleResult<System.IO.Stream>(
res => !res.IsSuccessStatusCode).
WaitAndRetryAsync(5, retryAttempts => TimeSpan.FromSeconds(3));
}
In this example I kept it !res.IsSuccessStatusCode but there is no such parameter in my case.
Rather than calling directly the GetStreamAsync you can do the following "trick":
var response = await _client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
//TODO: Check response status code then
using var stream = await response.Content.ReadAsStreamAsync();
First you need to alter your policy definition like this:
public IAsyncPolicy<HttpResponseMessage> LinearHttpRetry { get; }
public ClientPolicy()
{
LinearHttpRetry = Policy
.HandleResult<HttpResponseMessage>(res => !res.IsSuccessStatusCode)
.WaitAndRetryAsync(5, _ => TimeSpan.FromSeconds(3));
}
Then you need to adjust the usage as well
public static async Task<ExchangeModel> LoadExchange(string currency, string date)
{
string url = "here is the bank's api url";
var response = await _clientPolicy.LinearHttpRetry.ExecuteAsync(
async () => await _client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead));
using var stream = await response.Content.ReadAsStreamAsync();
var deserialized = await JsonSerializer.DeserializeAsync<Root>(stream);
var rate = deserialized.ExchangeModel.FirstOrDefault(r => r.Currency == currency);
return rate;
}

How to deserialize dictionary to an object in Xamarin Forms

When I request to server, I receive a response as a dictionary:
{
data=({
gender=female;
id=3490074761922520002;
name=joy;
picture={
data={
url="";
};
};
},
{
gender=female;
id=8242568878166175182;
name="name";
picture={
data={
url="";
};
};
})
paging={
next="";
}
summary={
"total_count"=66;
};
}
How can I deserialize this response dictionary to an object?
[UPDATE ANSWER]
After 2 days searching for answer, I finally found the way to solve this issue.
Here is my code:
var err = new NSError();
var theJSONData = NSJsonSerialization.Serialize(response.Data, NSJsonWritingOptions.PrettyPrinted, out err);
var theJSONText = new NSString(theJSONData, NSStringEncoding.UTF8);
Console.WriteLine(theJSONText);
response.data as NSDictionary -> JSON string
Other way to desserealize is:
public async Task<List<Produto>> GetProdutosAsync()
{
try
{
string url = "http://www.something.com/api/produtos/";
var response = await client.GetStringAsync(url);
var produtos = JsonConvert.DeserializeObject<List<Produto>>(response);
return produtos;
}
catch (Exception ex)
{
throw ex;
}
}
Change produto for your type.

HttpClient post record, status OK but record is not created and empty response

I have a plugin using HttpClient which is supposed to create a record in external ERP database. I am sending a request, everything on my side is ok, I receive a status OK but response.Content.ReadAsStringAsync() is empty. In ERP database record is not created. Code below
public class OnPostCreate : BaseClass
{
private static Uri baseAdress = new Uri("http://adress/ws/Methodname.json?raw=");
private static HttpClient client = new HttpClient();
public static string trace = "";
public override void ExecutePlugin()
{
Account account = ((Entity)context.InputParameters["Target"]).ToEntity<Account>();
Account getAcc = dataContext.AccountSet.Where(p => p.Id == account.Id)
.Select(p => new Account()
{
Id = p.Id,
jar_id = p.jar_id,
jar_field1= p.jar_field1,
jar_field2= p.jar_field2,
jar_field3= p.jar_field3,
jar_field4= p.jar_field4
})
.SingleOrDefault();
if (getAcc == null) return;
ModelClass model = CreateModel(getAcc);
string content = GetContent(model);
Account acc = new Account
{
Description = baseAdress + content,
Id = getAcc.Id
};
service.Update(acc);
RunAsync(content, tracingService).GetAwaiter().GetResult();
tracingService.Trace(trace);
}
private static async Task RunAsync(string content, ITracingService tracingService)
{
client.BaseAddress = baseAdress;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
try
{
await SendRequest(HttpMethod.Post, content);
}
catch (Exception e)
{
throw new InvalidPluginExecutionException(e.Message + e.InnerException + e.InnerException.InnerException);
}
}
private static async Task SendRequest(HttpMethod method, string query)
{
HttpRequestMessage request = new HttpRequestMessage(method, query);
HttpResponseMessage response = await client.SendAsync(request);
string responsetext = await response.Content.ReadAsStringAsync();
trace += "Code:" + response.StatusCode.ToString();
trace += "/n Response: " + responsetext;
response.EnsureSuccessStatusCode();
}
private ModelClass CreateModel(Account account) => new ModelClass()
{
Id = account.jar_id,
field4= account.jar_field4,
field1 = Convert.ToInt32(account.jar_field1 ),
field2= Convert.ToInt32(account.jar_field2),
field3= account.jar_field3
};
private string GetContent(ClassModel model) => $"<RequestData>" +
$"<Id>{model.Id}</Id>" +
$"<field1>{model.field1}</field1>" +
$"<field2>{model.field2}</field2>" +
$"<field3>{model.field3}</field3>" +
$"<field4>{model.field4}</field4>" +
$"</RequestData>";
}
Please help! Any suggestions why on my side everything seems to be OK (but response is empty??) and on the other side there is no even a mark of request.
I pasted the request url to postman and at first attempt i got no response, second one: 503 error Object reference not set to an instance of an object at DataService.Modules.Json.JsonHandler.Process(HttpListenerContext context) at DataService.Common.HttpServer.BeginGetContextCompleted(IAsyncResult result)
Regards

How to call async method from sync in C#?

I am adding a new web API call to existing functionality. I want to make this API call async but looks like it is causing deadlock. I have to make a lot more changes if I want to make entire code channel async which is not possible.
Questions I have are:
Is it possible to call async method from regular method?
What am I missing here? OR What is the correct approach here?
Code:
// Exisitng Method
public Tuple<RestaurantDeliveryProvider, DeliveryHubResult, Task<DeliveryManagerQuoteResponse>> CreateDeliveryRequest(OrderContextDTO orderContextDto)
{
var provider = RestaurantBl.GetDeliveryProviderInformationByRestaurantId(orderContextDto.RestaurantId ?? 0);
var deliveryHubResult = RestaurantBl.GetDeliveryHubResult(orderContextDto.OrderId ?? 0);;
// New Call which always comes back with "Not Yet Computed" result
Task<DeliveryManagerQuoteResponse> deliveryManagerQuoteResponse = _deliveryManager.CreateQuoteRequestAsync(orderContextDto, orderInfo);
return Tuple.Create(provider, deliveryHubResult, deliveryManagerQuoteResponse);
}
Async Methods:
public async Task<DeliveryManagerQuoteResponse> CreateQuoteRequestAsync(OrderContextDTO orderContextDto, OrderInfoDTO orderInfo)
{
DeliveryManagerQuoteResponse deliveryManagerQuoteResponse = null;
try
{
var restaurantInfo = RestaurantApi.GetRestaurant(orderInfo.RestaurantId);
var quoteRequest = new DeliveryManagerQuoteRequest
{
DeliveryProvider = null,
Country = orderContextDto.DeliveryEstimateRequestDto.RequestedDeliveryAddress.Country,
Concept = "BK",
StoreName = "BK-TEST-US-4",
OrderId = orderInfo.OrderId.ToString(),
AllowCash = false,
PaymentType = OrderPaymentType.Prepaid_Credit,
Note = orderInfo.DeliveryInstructions,
};
deliveryManagerQuoteResponse = await Quote(quoteRequest);
}
catch (Exception ex)
{
Log.ErrorFormat("Get Delivery Manager Quote failed: Error: {0}, OrderId: {1}", ex.Message, orderContextDto.OrderId);
}
return deliveryManagerQuoteResponse;
}
public async Task<DeliveryManagerQuoteResponse> Quote(DeliveryManagerQuoteRequest quoteRequest)
{
DeliveryManagerQuoteResponse deliveryManagerQuoteResponse;
var client = HttpClientFactory.GetClient();
var content = HttpClientFactory.JsonContentFactory.CreateJsonContent(quoteRequest);
var response = await client.PostAsync("https://myUrl", content);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
deliveryManagerQuoteResponse = JsonConvert.DeserializeObject<DeliveryManagerQuoteResponse>(data);
}
else
{
throw new Exception((int)response.StatusCode + "-" + response.StatusCode);
}
return deliveryManagerQuoteResponse;
}
I tried following as well but same result:
public async Task<DeliveryManagerQuoteResponse> Quote(DeliveryManagerQuoteRequest quoteRequest)
{
DeliveryManagerQuoteResponse deliveryManagerQuoteResponse;
using (var client = new HttpClient())
{
var content = HttpClientFactory.JsonContentFactory.CreateJsonContent(quoteRequest);
var response = await client.PostAsync("https://myUrl", content);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
deliveryManagerQuoteResponse = JsonConvert.DeserializeObject<DeliveryManagerQuoteResponse>(data);
}
else
{
throw new Exception((int)response.StatusCode + "-" + response.StatusCode);
}
}
return deliveryManagerQuoteResponse;
}
Output (sorry for the blurry output, if you click on it, you will see clear result):
don't
don't
Basically, there is no good or workable way to call an async method from a sync method and wait for the answer. There's "sync over async", but that's an anti-pattern and should be aggressively avoided.
So either:
rewrite the caller to be async
implement a synchronous version of the API

C# Web API method returns 403 Forbidden

Solved!!! - See last edit.
In my MVC app I make calls out to a Web API service with HMAC Authentication Filterign. My Get (GetMultipleItemsRequest) works, but my Post does not. If I turn off HMAC authentication filtering all of them work. I'm not sure why the POSTS do not work, but the GETs do.
I make the GET call from my code like this (this one works):
var productsClient = new RestClient<Role>(System.Configuration.ConfigurationManager.AppSettings["WebApiUrl"],
"xxxxxxxxxxxxxxx", true);
var getManyResult = productsClient.GetMultipleItemsRequest("api/Role").Result;
I make the POST call from my code like this (this one only works when I turn off HMAC):
private RestClient<Profile> profileClient = new RestClient<Profile>(System.Configuration.ConfigurationManager.AppSettings["WebApiUrl"],
"xxxxxxxxxxxxxxx", true);
[HttpPost]
public ActionResult ProfileImport(IEnumerable<HttpPostedFileBase> files)
{
//...
var postResult = profileClient.PostRequest("api/Profile", newProfile).Result;
}
My RestClient builds like this:
public class RestClient<T> where T : class
{
//...
private void SetupClient(HttpClient client, string methodName, string apiUrl, T content = null)
{
const string secretTokenName = "SecretToken";
client.BaseAddress = new Uri(_baseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (_hmacSecret)
{
client.DefaultRequestHeaders.Date = DateTime.UtcNow;
var datePart = client.DefaultRequestHeaders.Date.Value.UtcDateTime.ToString(CultureInfo.InvariantCulture);
var fullUri = _baseAddress + apiUrl;
var contentMD5 = "";
if (content != null)
{
var json = new JavaScriptSerializer().Serialize(content);
contentMD5 = Hashing.GetHashMD5OfString(json); // <--- Javascript serialized version is hashed
}
var messageRepresentation =
methodName + "\n" +
contentMD5 + "\n" +
datePart + "\n" +
fullUri;
var sharedSecretValue = ConfigurationManager.AppSettings[_sharedSecretName];
var hmac = Hashing.GetHashHMACSHA256OfString(messageRepresentation, sharedSecretValue);
client.DefaultRequestHeaders.Add(secretTokenName, hmac);
}
else if (!string.IsNullOrWhiteSpace(_sharedSecretName))
{
var sharedSecretValue = ConfigurationManager.AppSettings[_sharedSecretName];
client.DefaultRequestHeaders.Add(secretTokenName, sharedSecretValue);
}
}
public async Task<T[]> GetMultipleItemsRequest(string apiUrl)
{
T[] result = null;
try
{
using (var client = new HttpClient())
{
SetupClient(client, "GET", apiUrl);
var response = await client.GetAsync(apiUrl).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
await response.Content.ReadAsStringAsync().ContinueWith((Task<string> x) =>
{
if (x.IsFaulted)
throw x.Exception;
result = JsonConvert.DeserializeObject<T[]>(x.Result);
});
}
}
catch (HttpRequestException exception)
{
if (exception.Message.Contains("401 (Unauthorized)"))
{
}
else if (exception.Message.Contains("403 (Forbidden)"))
{
}
}
catch (Exception)
{
}
return result;
}
public async Task<T> PostRequest(string apiUrl, T postObject)
{
T result = null;
try
{
using (var client = new HttpClient())
{
SetupClient(client, "POST", apiUrl, postObject);
var response = await client.PostAsync(apiUrl, postObject, new JsonMediaTypeFormatter()).ConfigureAwait(false); //<--- not javascript formatted
response.EnsureSuccessStatusCode();
await response.Content.ReadAsStringAsync().ContinueWith((Task<string> x) =>
{
if (x.IsFaulted)
throw x.Exception;
result = JsonConvert.DeserializeObject<T>(x.Result);
});
}
}
catch (HttpRequestException exception)
{
if (exception.Message.Contains("401 (Unauthorized)"))
{
}
else if (exception.Message.Contains("403 (Forbidden)"))
{
}
}
catch (Exception)
{
}
return result;
}
//...
}
My Web API Controller is defined like this:
[SecretAuthenticationFilter(SharedSecretName = "xxxxxxxxxxxxxxx", HmacSecret = true)]
public class ProfileController : ApiController
{
[HttpPost]
[ResponseType(typeof(Profile))]
public IHttpActionResult PostProfile(Profile Profile)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
GuidValue = Guid.NewGuid();
Resource res = new Resource();
res.ResourceId = GuidValue;
var data23 = Resourceservices.Insert(res);
Profile.ProfileId = data23.ResourceId;
_profileservices.Insert(Profile);
return CreatedAtRoute("DefaultApi", new { id = Profile.ProfileId }, Profile);
}
}
Here is some of what SecretAuthenticationFilter does:
//now try to read the content as string
string content = actionContext.Request.Content.ReadAsStringAsync().Result;
var contentMD5 = content == "" ? "" : Hashing.GetHashMD5OfString(content); //<-- Hashing the non-JavaScriptSerialized
var datePart = "";
var requestDate = DateTime.Now.AddDays(-2);
if (actionContext.Request.Headers.Date != null)
{
requestDate = actionContext.Request.Headers.Date.Value.UtcDateTime;
datePart = requestDate.ToString(CultureInfo.InvariantCulture);
}
var methodName = actionContext.Request.Method.Method;
var fullUri = actionContext.Request.RequestUri.ToString();
var messageRepresentation =
methodName + "\n" +
contentMD5 + "\n" +
datePart + "\n" +
fullUri;
var expectedValue = Hashing.GetHashHMACSHA256OfString(messageRepresentation, sharedSecretValue);
// Are the hmacs the same, and have we received it within +/- 5 mins (sending and
// receiving servers may not have exactly the same time)
if (messageSecretValue == expectedValue
&& requestDate > DateTime.UtcNow.AddMinutes(-5)
&& requestDate < DateTime.UtcNow.AddMinutes(5))
goodRequest = true;
Any idea why HMAC doesn't work for the POST?
EDIT:
When SecretAuthenticationFilter tries to compare the HMAC sent, with what it thinks the HMAC should be they don't match. The reason is the MD5Hash of the content doesn't match the MD5Hash of the received content. The RestClient hashes the content using a JavaScriptSerializer.Serialized version of the content, but then the PostRequest passes the object as JsonMediaTypeFormatted.
These two types don't get formatted the same. For instance, the JavaScriptSerializer give's us dates like this:
\"EnteredDate\":\"\/Date(1434642998639)\/\"
The passed content has dates like this:
\"EnteredDate\":\"2015-06-18T11:56:38.6390407-04:00\"
I guess I need the hash to use the same data that's passed, so the Filter on the other end can confirm it correctly. Thoughts?
EDIT:
Found the answer, I needed to change the SetupClient code from using this line:
var json = new JavaScriptSerializer().Serialize(content);
contentMD5 = Hashing.GetHashMD5OfString(json);
To using this:
var json = JsonConvert.SerializeObject(content);
contentMD5 = Hashing.GetHashMD5OfString(json);
Now the sent content (formatted via JSON) will match the hashed content.
I was not the person who wrote this code originally. :)
Found the answer, I needed to change the SetupClient code from using this line:
var json = new JavaScriptSerializer().Serialize(content);
contentMD5 = Hashing.GetHashMD5OfString(json);
To using this:
var json = JsonConvert.SerializeObject(content);
contentMD5 = Hashing.GetHashMD5OfString(json);
Now the content used for the hash will be formatted as JSON and will match the sent content (which is also formatted via JSON).

Categories

Resources