How to compress HttpClient post and receive data in web api - c#

I have the following web api client which sends data to server using json and gzip:
public void Remote_Push(BlockList Blocks)
{
// Pushes pending records to the remote server
using (var Client = new HttpClient())
{
Client.BaseAddress = new Uri(Context.ServerUrl);
Client.DefaultRequestHeaders.Accept.Clear();
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var Content = JsonCompress(Blocks);
var T1 = Client.PostAsync("SyncPush/", Content); T1.Wait();
T1.Result.EnsureSuccess();
}
}
private static ByteArrayContent JsonCompress(object Data)
{
// Compress given data using gzip
var Bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(Data));
using (var Stream = new MemoryStream())
{
using (var Zipper = new GZipStream(Stream, CompressionMode.Compress, true)) Zipper.Write(Bytes, 0, Bytes.Length);
var Content = new ByteArrayContent(Stream.ToArray());
Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
Content.Headers.ContentEncoding.Add("gzip");
return Content;
}
}
On the server, I have created following action in web api controller:
[HttpPost]
public void SyncPush([FromBody]BlockList Blocks)
{
var Context = SyncCreateContext();
var Sync = new Processor(Context);
Sync.ServerPush(Blocks);
}
Previously, I have used PostAsJsonAsync on the client and it worked fine.
Now, I have switched to ByteArrayContent and gzip and no longer works, the Blocks is always null on the server.
What am I missing here, what is wrong or could be the problem?

Here is a sample console application to do what you are trying to do.
/*using System;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json;
using WebApi.Models;*/
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Press any key to POST");
Console.ReadLine();
RemotePush(new BlockList());
Console.ReadLine();
}
public static async void RemotePush(BlockList blocks)
{
try
{
using (var client = new HttpClient())
{
try
{
Console.WriteLine("Please wait.");
client.BaseAddress = new Uri("http://localhost:52521/Home/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var content = JsonCompress(blocks);
var response = await client.PostAsync("SyncPush/", content);
using (var stream = await response.Content.ReadAsStreamAsync())
{
using (var streamReader = new StreamReader(stream))
{
Console.WriteLine(streamReader.ReadToEnd());
}
}
Console.WriteLine("Done.");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private static MultipartFormDataContent JsonCompress(object data)
{
var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data));
var stream = new MemoryStream();
using (var zipper = new GZipStream(stream, CompressionMode.Compress, true))
{
zipper.Write(bytes, 0, bytes.Length);
}
MultipartFormDataContent multipartContent = new MultipartFormDataContent();
multipartContent.Add(new StreamContent(stream), "gzipContent");
return multipartContent;
}
}
My controller is like this.
[System.Web.Mvc.HttpPost]
public JsonResult SyncPush(BlockList content)
{
try
{
if (content != null)
{
return Json("success", JsonRequestBehavior.AllowGet);
}
return Json("failed due to null", JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return Json("failed " + ex.Message, JsonRequestBehavior.AllowGet);
}
}

just FYI, since .NET Core is not covered and this question is still relevant working .NET Core code. I used brotli, since that is a widely accepted standard today.
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Net.Http.Headers;
using System.IO.Compression;
public static class CompressedJsonHelper
{
private static readonly Lazy<JsonSerializerOptions>
Options = new(() =>
{
var opt = new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
//opt.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
return opt;
});
public static HttpContent ToJson(this object data, bool noCompress = false)
{
if (noCompress)
{
ByteArrayContent byteContent = new (ToBytes(data));
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return byteContent;
}
MemoryStream memoryStream = new ();
BrotliStream compress = new (memoryStream, CompressionLevel.Optimal, true);
StreamContent streamContent = new (memoryStream);
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
streamContent.Headers.ContentEncoding.Add("brotli");
JsonSerializer.Serialize(compress, data, Options.Value);
compress.Flush();
memoryStream.Position = 0;
return streamContent;
}
private static byte[] ToBytes(this object data) => JsonSerializer.SerializeToUtf8Bytes(data, Options.Value);
}
and the httpClient code:
HttpRequestMessage request = new(HttpMethod.Post, $"{yourbaseurl}/{path}")
{
Content = json.ToJson()
};
await _httpClient.SendAsync(request, ...) etc

Related

How to implement WebClient.UploadFileAsync with HttpClient? [duplicate]

Does anyone know how to use the HttpClient in .Net 4.5 with multipart/form-data upload?
I couldn't find any examples on the internet.
my result looks like this:
public static async Task<string> Upload(byte[] image)
{
using (var client = new HttpClient())
{
using (var content =
new MultipartFormDataContent("Upload----" + DateTime.Now.ToString(CultureInfo.InvariantCulture)))
{
content.Add(new StreamContent(new MemoryStream(image)), "bilddatei", "upload.jpg");
using (
var message =
await client.PostAsync("http://www.directupload.net/index.php?mode=upload", content))
{
var input = await message.Content.ReadAsStringAsync();
return !string.IsNullOrWhiteSpace(input) ? Regex.Match(input, #"http://\w*\.directupload\.net/images/\d*/\w*\.[a-z]{3}").Value : null;
}
}
}
}
It works more or less like this (example using an image/jpg file):
async public Task<HttpResponseMessage> UploadImage(string url, byte[] ImageData)
{
var requestContent = new MultipartFormDataContent();
// here you can specify boundary if you need---^
var imageContent = new ByteArrayContent(ImageData);
imageContent.Headers.ContentType =
MediaTypeHeaderValue.Parse("image/jpeg");
requestContent.Add(imageContent, "image", "image.jpg");
return await client.PostAsync(url, requestContent);
}
(You can requestContent.Add() whatever you want, take a look at the HttpContent descendant to see available types to pass in)
When completed, you'll find the response content inside HttpResponseMessage.Content that you can consume with HttpContent.ReadAs*Async.
This is an example of how to post string and file stream with HTTPClient using MultipartFormDataContent. The Content-Disposition and Content-Type need to be specified for each HTTPContent:
Here's my example. Hope it helps:
private static void Upload()
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("User-Agent", "CBS Brightcove API Service");
using (var content = new MultipartFormDataContent())
{
var path = #"C:\B2BAssetRoot\files\596086\596086.1.mp4";
string assetName = Path.GetFileName(path);
var request = new HTTPBrightCoveRequest()
{
Method = "create_video",
Parameters = new Params()
{
CreateMultipleRenditions = "true",
EncodeTo = EncodeTo.Mp4.ToString().ToUpper(),
Token = "x8sLalfXacgn-4CzhTBm7uaCxVAPjvKqTf1oXpwLVYYoCkejZUsYtg..",
Video = new Video()
{
Name = assetName,
ReferenceId = Guid.NewGuid().ToString(),
ShortDescription = assetName
}
}
};
//Content-Disposition: form-data; name="json"
var stringContent = new StringContent(JsonConvert.SerializeObject(request));
stringContent.Headers.Add("Content-Disposition", "form-data; name=\"json\"");
content.Add(stringContent, "json");
FileStream fs = File.OpenRead(path);
var streamContent = new StreamContent(fs);
streamContent.Headers.Add("Content-Type", "application/octet-stream");
//Content-Disposition: form-data; name="file"; filename="C:\B2BAssetRoot\files\596090\596090.1.mp4";
streamContent.Headers.Add("Content-Disposition", "form-data; name=\"file\"; filename=\"" + Path.GetFileName(path) + "\"");
content.Add(streamContent, "file", Path.GetFileName(path));
//content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
Task<HttpResponseMessage> message = client.PostAsync("http://api.brightcove.com/services/post", content);
var input = message.Result.Content.ReadAsStringAsync();
Console.WriteLine(input.Result);
Console.Read();
}
}
}
Try this its working for me.
private static async Task<object> Upload(string actionUrl)
{
Image newImage = Image.FromFile(#"Absolute Path of image");
ImageConverter _imageConverter = new ImageConverter();
byte[] paramFileStream= (byte[])_imageConverter.ConvertTo(newImage, typeof(byte[]));
var formContent = new MultipartFormDataContent
{
// Send form text values here
{new StringContent("value1"),"key1"},
{new StringContent("value2"),"key2" },
// Send Image Here
{new StreamContent(new MemoryStream(paramFileStream)),"imagekey","filename.jpg"}
};
var myHttpClient = new HttpClient();
var response = await myHttpClient.PostAsync(actionUrl.ToString(), formContent);
string stringContent = await response.Content.ReadAsStringAsync();
return response;
}
Here is another example on how to use HttpClient to upload a multipart/form-data.
It uploads a file to a REST API and includes the file itself (e.g. a JPG) and additional API parameters. The file is directly uploaded from local disk via FileStream.
See here for the full example including additional API specific logic.
public static async Task UploadFileAsync(string token, string path, string channels)
{
// we need to send a request with multipart/form-data
var multiForm = new MultipartFormDataContent();
// add API method parameters
multiForm.Add(new StringContent(token), "token");
multiForm.Add(new StringContent(channels), "channels");
// add file and directly upload it
FileStream fs = File.OpenRead(path);
multiForm.Add(new StreamContent(fs), "file", Path.GetFileName(path));
// send request to API
var url = "https://slack.com/api/files.upload";
var response = await client.PostAsync(url, multiForm);
}
Here's a complete sample that worked for me. The boundary value in the request is added automatically by .NET.
var url = "http://localhost/api/v1/yourendpointhere";
var filePath = #"C:\path\to\image.jpg";
HttpClient httpClient = new HttpClient();
MultipartFormDataContent form = new MultipartFormDataContent();
FileStream fs = File.OpenRead(filePath);
var streamContent = new StreamContent(fs);
var imageContent = new ByteArrayContent(streamContent.ReadAsByteArrayAsync().Result);
imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
form.Add(imageContent, "image", Path.GetFileName(filePath));
var response = httpClient.PostAsync(url, form).Result;
Example with preloader Dotnet 3.0 Core
ProgressMessageHandler processMessageHander = new ProgressMessageHandler();
processMessageHander.HttpSendProgress += (s, e) =>
{
if (e.ProgressPercentage > 0)
{
ProgressPercentage = e.ProgressPercentage;
TotalBytes = e.TotalBytes;
progressAction?.Invoke(progressFile);
}
};
using (var client = HttpClientFactory.Create(processMessageHander))
{
var uri = new Uri(transfer.BackEndUrl);
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", AccessToken);
using (MultipartFormDataContent multiForm = new MultipartFormDataContent())
{
multiForm.Add(new StringContent(FileId), "FileId");
multiForm.Add(new StringContent(FileName), "FileName");
string hash = "";
using (MD5 md5Hash = MD5.Create())
{
var sb = new StringBuilder();
foreach (var data in md5Hash.ComputeHash(File.ReadAllBytes(FullName)))
{
sb.Append(data.ToString("x2"));
}
hash = result.ToString();
}
multiForm.Add(new StringContent(hash), "Hash");
using (FileStream fs = File.OpenRead(FullName))
{
multiForm.Add(new StreamContent(fs), "file", Path.GetFileName(FullName));
var response = await client.PostAsync(uri, multiForm);
progressFile.Message = response.ToString();
if (response.IsSuccessStatusCode) {
progressAction?.Invoke(progressFile);
} else {
progressErrorAction?.Invoke(progressFile);
}
response.EnsureSuccessStatusCode();
}
}
}
I'm adding a code snippet which shows on how to post a file to an API which has been exposed over DELETE http verb. This is not a common case to upload a file with DELETE http verb but it is allowed. I've assumed Windows NTLM authentication for authorizing the call.
The problem that one might face is that all the overloads of HttpClient.DeleteAsync method have no parameters for HttpContent the way we get it in PostAsync method
var requestUri = new Uri("http://UrlOfTheApi");
using (var streamToPost = new MemoryStream("C:\temp.txt"))
using (var fileStreamContent = new StreamContent(streamToPost))
using (var httpClientHandler = new HttpClientHandler() { UseDefaultCredentials = true })
using (var httpClient = new HttpClient(httpClientHandler, true))
using (var requestMessage = new HttpRequestMessage(HttpMethod.Delete, requestUri))
using (var formDataContent = new MultipartFormDataContent())
{
formDataContent.Add(fileStreamContent, "myFile", "temp.txt");
requestMessage.Content = formDataContent;
var response = httpClient.SendAsync(requestMessage).GetAwaiter().GetResult();
if (response.IsSuccessStatusCode)
{
// File upload was successfull
}
else
{
var erroResult = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
throw new Exception("Error on the server : " + erroResult);
}
}
You need below namespaces at the top of your C# file:
using System;
using System.Net;
using System.IO;
using System.Net.Http;
P.S. You are seeing a number of using blocks(IDisposable pattern) in the above code snippet which doesn't look very clean. Unfortunately, the syntax of using construct doesn't support initializing multiple variables in single statement.
X509Certificate clientKey1 = null;
clientKey1 = new X509Certificate(AppSetting["certificatePath"],
AppSetting["pswd"]);
string url = "https://EndPointAddress";
FileStream fs = File.OpenRead(FilePath);
var streamContent = new StreamContent(fs);
var FileContent = new ByteArrayContent(streamContent.ReadAsByteArrayAsync().Result);
FileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("ContentType");
var handler = new WebRequestHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ClientCertificates.Add(clientKey1);
handler.ServerCertificateValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) =>
{
return true;
};
using (var client = new HttpClient(handler))
{
// Post it
HttpResponseMessage httpResponseMessage = client.PostAsync(url, FileContent).Result;
if (!httpResponseMessage.IsSuccessStatusCode)
{
string ss = httpResponseMessage.StatusCode.ToString();
}
}
public async Task<object> PassImageWithText(IFormFile files)
{
byte[] data;
string result = "";
ByteArrayContent bytes;
MultipartFormDataContent multiForm = new MultipartFormDataContent();
try
{
using (var client = new HttpClient())
{
using (var br = new BinaryReader(files.OpenReadStream()))
{
data = br.ReadBytes((int)files.OpenReadStream().Length);
}
bytes = new ByteArrayContent(data);
multiForm.Add(bytes, "files", files.FileName);
multiForm.Add(new StringContent("value1"), "key1");
multiForm.Add(new StringContent("value2"), "key2");
var res = await client.PostAsync(_MEDIA_ADD_IMG_URL, multiForm);
}
}
catch (Exception e)
{
throw new Exception(e.ToString());
}
return result;
}

HttpClient PutAsync not updating Web API

I have a simple .Net Core Web API with some simple Get, post, put endpoints. I also created a simple Console Client to test the API. The get and post methods work fine although when I test the Put, it does not update the Web API with the new data. I have tested the Web API Put method with swagger and it works fine, so it seems like my Console App Client logic is incorrect. Can anyone see what I am doing wrong?
edit: weather is an in-memory collection - a List so I do not need to SaveChanges()
Console Client:
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading.Tasks;
namespace Lab1RestConsole
{
class Program
{
private static readonly HttpClient client = new HttpClient();
private static readonly String baseURI = "https://localhost:44347/api/Weather/";
static async Task RunAsync()
{
try
{
//1 GET /api/Weather/
var serializer = new DataContractJsonSerializer(typeof(List<WeatherInformation>));
var streamTask = client.GetStreamAsync(baseURI);
var entries = serializer.ReadObject(await streamTask) as List<WeatherInformation>;
foreach (var e in entries)
{
Console.WriteLine(e.ToString());
}
Console.WriteLine("");
//2 GET /api/Weather/
serializer = new DataContractJsonSerializer(typeof(List<WeatherInformation>));
streamTask = client.GetStreamAsync(baseURI + "warning/false");
entries = serializer.ReadObject(await streamTask) as List<WeatherInformation>;
foreach (var e in entries)
{
Console.WriteLine(e.ToString());
}
//3 GET /api/Weather/city/Dublin
serializer = new DataContractJsonSerializer(typeof(WeatherInformation));
streamTask = client.GetStreamAsync(baseURI + "city/Dublin");
WeatherInformation weatherinfo = serializer.ReadObject(await streamTask) as WeatherInformation;
Console.WriteLine("\n" + weatherinfo + "\n");
// serialise to JSON
weatherinfo = new WeatherInformation() { City = "Antwerp", Temp = 3, Warning = true, WeatherType = 0, WindSpeed = 22 }; ;
MemoryStream ms = new MemoryStream();
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(WeatherInformation));
ser.WriteObject(ms, weatherinfo);
// read and create string content for POST
ms.Position = 0;
StreamReader sr = new StreamReader(ms);
StringContent content = new StringContent(sr.ReadToEnd(), Encoding.UTF8, "application/json");
//4 POST - add new city
HttpResponseMessage httpResponse = await client.PostAsync(baseURI, content);
httpResponse.EnsureSuccessStatusCode();
//5 update PUT to api/Weather/Dublin - now temp is 10
weatherinfo = new WeatherInformation() { City = "Antwerp", Temp = 10, Warning = true, WeatherType = 0, WindSpeed = 22 }; ;
var putTask = client.PutAsync(baseURI + "Dublin", content);
}
catch (HttpRequestException e)
{
Console.WriteLine(e.Message);
}
}
static void Main()
{
RunAsync().Wait();
client.Dispose();
Console.ReadLine();
}
}
}
The Put action in Web API
// PUT: api/Weather/5
[HttpPut("{city}")]
public IActionResult PutUpdateCityWeather(string city, WeatherInformation weatherInfo)
{
if (ModelState.IsValid)
{
var record = weather.FirstOrDefault(e => e.City.ToUpper() == city.ToUpper());
if (record != null)
{
record.Temp = weatherInfo.Temp;
record.Warning = weatherInfo.Warning;
record.WeatherType = weatherInfo.WeatherType;
record.WindSpeed = weatherInfo.WindSpeed;
return Ok();
}
else
{
return BadRequest();
}
}
else
{
return BadRequest(ModelState);
}
}

Http client Post with body parameter and file in c#

I was trying to attach a csv file as a body parameter in my test script. But still as per the below code controller expect file and just curious how should I pass that.
I run test script in below order
Method-1
public void AttachedRatesFile(string fileName)
{
_form = string.IsNullOrWhiteSpace(fileName)
? _form = new StringContent(string.Empty)
: _form = new StreamContent(File.OpenRead($"{ResourceFolder}{fileName}"));
_form.Headers.ContentType = new MediaTypeHeaderValue("application/csv");
_form.Headers.ContentDisposition = new ContentDispositionHeaderValue(fileName);
}
Method-2
public void PostRequestExecutes(string resource)
{
var content = new MultipartFormDataContent{_form};
WhenThePostRequestExecutesWithContent(resource, content);
}
Method-3
public async void WhenThePostRequestExecutesWithContent(string resource, HttpContent content)
{
ResponseMessage = await HttpClient.PostAsync(resource, content);
}
I see null in below file parameter
Controller:
public async Task<IActionResult> SeedData(IFormFile file)
{
var result = await _seedDataService.SeedData(file);
return Ok(new { IsUploadSuccesful = result});
}
I would add that to the body as a stream
var memoryContentStream = new MemoryStream();
using (var streamWriter = new StreamWriter(memoryContentStream, Encoding.UTF8, 1000,
true))
{
using (var jsonTextWriter = new JsonTextWriter(streamWriter))
{
var jsonSerializer = new JsonSerializer();
jsonSerializer.Serialize(jsonTextWriter, OBJECT);
jsonTextWriter.Flush();
}
}
if (memoryContentStream.CanSeek)
{
memoryContentStream.Seek(0, SeekOrigin.Begin);
}
Then
using (var streamContent = new StreamContent(memoryContentStream))
{
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
request.Content = streamContent;
using (var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
{
var stream = await response.Content.ReadAsStreamAsync();
response.EnsureIsSuccessStatusCode();
}
}
The above would first write the content as a memory stream and then when creating the POST request you can send the stream as a streamContent

ObjectDisposedException: The CancellationTokenSource has been disposed

I'm starting to develop with Xamarin.Forms using Xamarin Studio on my MacBook Pro. I built an application whose purpose is to query a PrestaShop Website, retrieve the Products and show them.
When deploying the application to Android, I had some problems with versions below Marshmallow, but I solved them, so I won't describe them here.
When deploying the application to iOS (Simulator), I'm still having a critical problem. The application runs, but when I click on the button to retrieve the data, it crashes giving me a System.ObjectDisposedException, whose message is "The CancellationTokenSource has been disposed". I'll paste here the relevant source code:
async void button1_Click(object sender, System.EventArgs e)
{
try
{
HttpClientHandler hnd = new HttpClientHandler();
hnd.Credentials = new NetworkCredential("[apikey]", "");
string res;
using (var client = new HttpClient(hnd))
{
var responseText = await client.GetStringAsync("[endpoint]/products");
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(responseText.Replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", string.Empty));
writer.Flush();
stream.Position = 0;
XDocument doc = XDocument.Load(stream);
res = JsonConvert.SerializeXNode(doc, Formatting.Indented, true);
}
var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(res);
//Result.Text = data["products"].GetType().ToString() + Result.Text;
Func<string, Task> creator_method = async (string id) =>
{
try
{
var responseProd = await client.GetStringAsync($"[endpoint]/products/{id}"); // AT THIS ROW THE EXCEPTION IS RAISED!!!
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(responseProd.Replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", string.Empty));
writer.Flush();
stream.Position = 0;
XDocument doc = XDocument.Load(stream);
res = JsonConvert.SerializeXNode(doc, Formatting.Indented, true);
}
var product_rawData = JsonConvert.DeserializeObject<Dictionary<string, object>>(res);
var productData = (JObject)product_rawData["product"];
try
{
views.Children.Add(new ProductXaml(productData["name"]["language"]["#cdata-section"].ToString(), float.Parse(productData["price"]["#cdata-section"].ToString().Replace('.', ',')), productData["id_default_image"]["#xlink:href"].ToString()));
}
catch (NullReferenceException nre)
{
views.Children.Add(new ProductXaml(productData["name"]["language"]["#cdata-section"].ToString(), float.Parse(productData["price"]["#cdata-section"].ToString().Replace('.', ',')), ""));
}
}
catch (Exception gex) { throw; }
};
foreach (var product in ((JObject)data["products"])["product"])
try
{
Device.BeginInvokeOnMainThread(async () => { await creator_method.Invoke(product["#id"].ToString()); });
}
catch (TaskCanceledException tce) { continue; }
catch (WebException we) { continue;}
}
}
catch (HttpRequestException ex)
{
Result.Text = ex.Message;
}
}
How could I solve the problem?
"The CancellationTokenSource has been disposed"
It seems that your using (var client = new HttpClient())...'s CancellationToken has already been consumed by the first client.GetStringAsync() call.
How To Fix This
I would suggest putting your second call in its own using block, to avoid this.
Here's what your code should now look like, with a second using statement:
async void button1_Click(object sender, System.EventArgs e)
{
try
{
HttpClientHandler hnd = new HttpClientHandler();
hnd.Credentials = new NetworkCredential("[apikey]", "");
string res;
using (var client = new HttpClient(hnd))
{
var responseText = await client.GetStringAsync("[endpoint]/products");
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(responseText.Replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", string.Empty));
writer.Flush();
stream.Position = 0;
XDocument doc = XDocument.Load(stream);
res = JsonConvert.SerializeXNode(doc, Formatting.Indented, true);
}
var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(res);
//Result.Text = data["products"].GetType().ToString() + Result.Text;
Func<string, Task> creator_method = async (string id) =>
{
try
{
using (var newClient = new HttpClient(hnd)) // Create a new client to avoid your other one's token being consumed
{
var responseProd = await newClient.GetStringAsync($"[endpoint]/products/{id}");
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(responseProd.Replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", string.Empty));
writer.Flush();
stream.Position = 0;
XDocument doc = XDocument.Load(stream);
res = JsonConvert.SerializeXNode(doc, Formatting.Indented, true);
}
var product_rawData = JsonConvert.DeserializeObject<Dictionary<string, object>>(res);
var productData = (JObject)product_rawData["product"];
try
{
views.Children.Add(new ProductXaml(productData["name"]["language"]["#cdata-section"].ToString(), float.Parse(productData["price"]["#cdata-section"].ToString().Replace('.', ',')), productData["id_default_image"]["#xlink:href"].ToString()));
}
catch (NullReferenceException nre)
{
views.Children.Add(new ProductXaml(productData["name"]["language"]["#cdata-section"].ToString(), float.Parse(productData["price"]["#cdata-section"].ToString().Replace('.', ',')), ""));
}
}
}
catch (Exception gex) { throw; }
};
foreach (var product in ((JObject)data["products"])["product"])
try
{
Device.BeginInvokeOnMainThread(async () => { await creator_method.Invoke(product["#id"].ToString()); });
}
catch (TaskCanceledException tce) { continue; }
catch (WebException we) { continue;}
}
}
catch (HttpRequestException ex)
{
Result.Text = ex.Message;
}
}
Hope this helps! :)
Create a new instance of HttpClient like this
public static HttpClient GetHttpClient()
{
HttpClient httpClient = new HttpClient()
{
BaseAddress = new Uri(BaseUrl)
};
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
return httpClient;
}
I was having the same issue, I was using a single httpclient for all my api requests.
private static HttpClient httpClient;
public static HttpClient GetHttpClient()
{
if (httpClient == null)
{
httpClient = new HttpClient()
{
BaseAddress = new Uri(BaseUrl)
};
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
}
return httpClient;
}
It seems that your using (var client = new HttpClient())...'s
CancellationToken has already been consumed by the first
client.GetStringAsync() call.
Geoff 's answer I removed my static httpclient and now creates a new httpclient for all requests, it's fixed my issue.
public static HttpClient GetHttpClient()
{
HttpClient httpClient = new HttpClient()
{
BaseAddress = new Uri(BaseUrl)
};
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
return httpClient;
}

parse data sent by webapi. is IEnumerable a good choice?

Hello I'm creating WebAPI and a windows8 metro app that communicates with this API.
I'll use simple example
API
public IEnumerable<string> Get() {
return new string[] { "value1", "value2" };
}
Application
using (HttpClient client = new HttpClient())
{
using (var response = await client.GetAsync(this.apiUri.GetUri("content", "Get")))
{
var result = await response.Content.ReadAsStringAsync();
}
}
Now the variable result is the response from API. But this is a string in form
"[\"value1\",\"value2\"]"
How can I parse/convert this ?
WebAPIs return JSON by default. You can request it to send XML, or use a JSON parser.
It's easy for you to deserialize back using Json.Net, in your case:
var json = "[\"value1\",\"value2\"]";
var values = JsonConvert.DeserializeObject<string[]>(json);
I use my class (MVCClient.cs) in silverlight to communicate with MVC WebAPI. In your case you'd just want to call :
DoGetMessage<string>(yourUrl, new DataDelegateArray<string>(yourCallbackFunction));
The lines that you are missing in your code would be :
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
T data = (T)serializer.ReadObject(stream);
Where T is string[].
MVCClient.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Text;
public class MVCClient
{
public delegate void DataDelegateArray<T>(T[] objects);
public delegate void DataDelegate<U>(U obj);
#region Communication Functions
public void DoPostMessage<T>(string queryURL, T item, ProcessPOSTResult<T> callback)
{
DoPostMessage<T, T>(queryURL, item, callback);
}
public void DoPostMessage<TPost, TReply>(string queryURL, TPost item, ProcessPOSTResult<TReply> callback)
{
#if SILVERLIGHT
Uri uri = new Uri(queryURL, UriKind.Absolute);
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "POST";
request.ContentType = "application/json";
request.BeginGetRequestStream(result =>
{
var req = (HttpWebRequest)result.AsyncState;
var stream = req.EndGetRequestStream(result);
if (stream != null)
{
var data = new
{
name = item
};
System.IO.MemoryStream ms = new System.IO.MemoryStream();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(TPost));
serializer.WriteObject(stream, item);
stream.Close();
}
req.BeginGetResponse((requestResult) =>
{
var req2 = (HttpWebRequest)requestResult.AsyncState;
var response = req2.EndGetResponse(requestResult);
var responseStream = response.GetResponseStream();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(TReply));
TReply responseItem = (TReply)serializer.ReadObject(responseStream);
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() =>
{
callback(responseItem);
});
}, req);
}, request);
#endif
}
public void DoGetMessage<T>(string queryURL, DataDelegateArray<T> callback)
{
DoGetMessage<T[]>(queryURL, result => callback(result));
}
public void DoGetMessage<T>(string queryURL, DataDelegate<T> callback)
{
#if SILVERLIGHT
Uri uri = new Uri(queryURL, UriKind.Absolute);
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "GET";
request.Accept = "text/json";
request.BeginGetResponse((result) =>
{
var response = request.EndGetResponse(result);
var stream = response.GetResponseStream();
if (stream != null)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
T data = (T)serializer.ReadObject(stream);
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() =>
{
callback(data);
});
}
else
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() =>
{
callback(default(T));
});
}, null);
#endif
}
#endregion
}

Categories

Resources