How to mock a Stream response when calling HttpClient GetStreamAsync - c#

I'm trying to test a class that uses HttpClient and i have to fake the Stream response.
This is the code i'm trying to test.
try
{
_httpClient.Timeout = TimeSpan.FromMinutes(10);
_httpClient.BaseAddress = new Uri(BaseAddress);
_httpClient.DefaultRequestHeaders.Accept.Clear();
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", _credentials);
using (Stream s = _httpClient.GetStreamAsync(API_string).Result) <---- this is the trouble line.
using (StreamReader sr = new StreamReader(s))
using (JsonReader reader = new JsonTextReader(sr))
{
JsonSerializer serializer = new JsonSerializer();
return serializer.Deserialize<T>(reader); //breaks loop
}
}
From the test side, i can insert my custom HttpClientHandler and send back any response i want.
This is my Fake HttpClientHandler builder (I'm using Moq).
public HttpClientHandler MockHttpClientHandler()
{
var requestUri = new Uri("Uri.Expected.To.Be.Called");
var expectedResponse = "Response text"; <-- This is where i need to write the Object to be returned.
var mockResponse = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(expectedResponse) };
var mockHandler = new Mock<HttpClientHandler>();
mockHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.Is<HttpRequestMessage>(message => message.RequestUri == requestUri),
// ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.Returns(Task.FromResult(mockResponse));
return mockHandler.Object;
}
My test breaks when it tries to run the return line, because whatever i'm sending back can't be deserialized. I just don't know what my Stream s should look like, and i can't see its contents by debugging either.
I'm fairly lost here. Maybe my approach is wrong?
Thanks for any help you can give me.

var stream = new MemoryStream();
var httpResponse = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StreamContent(stream)
};
var handlerMock = new Mock<HttpMessageHandler>();
handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(httpResponse);

Related

How to Pass value along with file upload through webclient C# [duplicate]

How can I send a file and form data with the HttpClient?
I have two ways to send a file or form data. But I want to send both like an HTML form. How can I do that? Thanks.
This is my code:
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
var client = new HttpClient();
var requestContent = new MultipartFormDataContent();
filename = openFileDialog1.FileName;
array = File.ReadAllBytes(filename);
var imageContent = new ByteArrayContent(array);
imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse("audio/*");
requestContent.Add(imageContent, "audio", "audio.wav");
var values = new Dictionary<string, string>
{
{ "token", "b53b99534a137a71513548091271c44c" },
};
var content = new FormUrlEncodedContent(values);
requestContent.Add(content);
var response = await client.PostAsync("localhost", requestContent);
var responseString = await response.Content.ReadAsStringAsync();
txtbox.Text = responseString.ToString();
}
Here's code I'm using to post form information and a csv file
using (var httpClient = new HttpClient())
{
var surveyBytes = ConvertToByteArray(surveyResponse);
httpClient.DefaultRequestHeaders.Add("X-API-TOKEN", _apiToken);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var byteArrayContent = new ByteArrayContent(surveyBytes);
byteArrayContent.Headers.ContentType = MediaTypeHeaderValue.Parse("text/csv");
var response = await httpClient.PostAsync(_importUrl, new MultipartFormDataContent
{
{new StringContent(surveyId), "\"surveyId\""},
{byteArrayContent, "\"file\"", "\"feedback.csv\""}
});
return response;
}
This is for .net 4.5.
Note the \" in the MultipartFormDataContent. There is a bug in MultipartFormDataContent.
In 4.5.1 MultipartFormDataContent wraps the data with the correct quotes.
Update: This link to the bug no longer works since the have retired Microsoft Connect.
Here's code I'm using a method to send file and data from console to API
static async Task uploaddocAsync()
{
MultipartFormDataContent form = new MultipartFormDataContent();
Dictionary<string, string> parameters = new Dictionary<string, string>();
//parameters.Add("username", user.Username);
//parameters.Add("FullName", FullName);
HttpContent DictionaryItems = new FormUrlEncodedContent(parameters);
form.Add(DictionaryItems, "model");
try
{
var stream = new FileStream(#"D:\10th.jpeg", FileMode.Open);
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(#"http:\\xyz.in");
HttpContent content = new StringContent("");
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = "uploadedFile1",
FileName = "uploadedFile1"
};
content = new StreamContent(stream);
form.Add(content, "uploadedFile1");
client.DefaultRequestHeaders.Add("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.dsfdsfdsfdsfsdfkhjhjkhjk.vD056hXETFMXYxOaLZRwV7Ny1vj-tZySAWq6oybBr2w");
var response = client.PostAsync(#"\api\UploadDocuments\", form).Result;
var k = response.Content.ReadAsStringAsync().Result;
}
catch (Exception ex)
{
}
}

HttpClient throws error with the Rest services

I am trying to call multiple rest services from the Web API I am creating and I am getting the below error while one of the Sharepoint rest service is called
This instance has already started one or more requests. Properties can only be modified before sending the first request.
Below is the code for calling the rest services using the HttpClient
try
{
var credential = new NetworkCredential(userName_SP, password_SP, domain_SP);
var myCache = new CredentialCache();
myCache.Add(new Uri(core_URL), "NTLM", credential);
var handler = new HttpClientHandler();
handler.AllowAutoRedirect = true;
handler.Credentials = myCache;
using (var client_sharePoint = new HttpClient(handler))
{
var response = client_sharePoint.GetAsync(core_URL).Result;
client_sharePoint.BaseAddress = uri;
client_sharePoint.DefaultRequestHeaders.Accept.Clear();
client_sharePoint.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var responsedata = await response.Content.ReadAsStringAsync();
var returnObj = JsonConvert.DeserializeObject<SharepointDTO.RootObject>(
responsedata);
return returnObj;
}
...
I have never encountered this error before. Can anyone please suggest me if I need set the timeout
Try this:
var credential = new NetworkCredential(userName_SP, password_SP, domain_SP);
var myCache = new CredentialCache();
myCache.Add(new Uri(core_URL), "NTLM", credential);
var handler = new HttpClientHandler();
handler.AllowAutoRedirect = true;
handler.Credentials = myCache;
using (var client_sharePoint = new HttpClient(handler))
{
client_sharePoint.BaseAddress = uri;
client_sharePoint.DefaultRequestHeaders.Accept.Clear();
client_sharePoint.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client_sharePoint.GetAsync(core_URL);
var responsedata = await response.Content.ReadAsStringAsync();
var returnObj = JsonConvert.DeserializeObject<SharepointDTO.RootObject>(
responsedata);
return returnObj;
}
Headers and BaseAddress must be set before you make the request with GetAsync.
I also took the liberty to change from .Result to await since calling .Result is poor practice and I can see this is in an async method.
You should also read this: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

Sending multipart/related content to SOAP service .NET Core

I'm trying to set the headers of my HttpRequestMessage for sending a multipart/related HTTP message to a SOAP service.
The headers need to look to something like this :
multipart/related;boundary=MIMEBoundaryurn_uuid_7B970E2B89C4446286DDFDBFF7F19581; type="application/xop+xml"; start="0.urn:uuid:4628599A1A934415B3EFB15A1888004B#ws.jboss.org>"; start-info="application/soap+xml"; action="urn:ihe:iti:2007:ProvideAndRegisterDocumentSet-b"
But I can't figure out how to set them up with my HttpRequestMessage.
Here's my code :
using (var client = new HttpClient(handler))
{
var request = new HttpRequestMessage()
{
RequestUri = new Uri(Host),
Method = HttpMethod.Post
};
var boundary = Guid.NewGuid().ToString();
var bytes = new ByteArrayContent(doc);
bytes.Headers.ContentType = new MediaTypeHeaderValue("text/xml");
var multi = new MultipartContent("related", boundary) {
new StringContent(xml.ToString(), Encoding.UTF8, "text/xml"),
bytes
};
request.Content = multi;
Log.Information(request.Headers.ToString());
Log.Information(request.Content.Headers.ToString());
Log.Information(request.Content.ToString());
var response = client.SendAsync(request).Result;
if (!response.IsSuccessStatusCode)
throw new Exception($"Request resulted in status :{response.StatusCode}");
var stream = response.Content.ReadAsStreamAsync().Result;
using (var sr = new StreamReader(stream))
{
var soapResponse = XDocument.Load(sr);
Log.Information("Soap response : " + soapResponse);
}
}

C# - Body content in POST request

I need to make some api calls in C#. I'm using Web API Client from Microsoft to do that. I success to make some POST requests, but I don't know how to add the field "Body" into my requests. Any idea ?
Here's my code:
static HttpClient client = new HttpClient();
public override void AwakeFromNib()
{
base.AwakeFromNib();
notif_button.Activated += (sender, e) => {
};
tips_button.Activated += (sender, e) =>
{
Tip t1 = new Tip(title_tips.StringValue, pic_tips.StringValue, content_tips.StringValue, "TEST");
client.BaseAddress = new Uri("my_url");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
CreateProductAsync(t1).Wait();
};
}
static async Task<Uri> CreateProductAsync(Tip tips)
{
HttpResponseMessage response = await client.PostAsJsonAsync("api/add_tips", tips);
response.EnsureSuccessStatusCode();
return response.Headers.Location;
}
Step 1. Choose a type that derives from HttpContent. If you want to write a lot of content with runtime code, you could use a StreamContent and open some sort of StreamWriter on it. For something short, use StringContent. You can also derive your own class for custom content.
Step 2. Pass the content in a call to HttpClient.PostAsync.
Here's an example that uses StringContent to pass some JSON:
string json = JsonConvert.SerializeObject(someObject);
var httpContent = new StringContent(json, Encoding.UTF8, "application/json");
var httpResponse = await httpClient.PostAsync("http://www.foo.bar", httpContent);
See also How do I set up HttpContent?.
Thanks to this and this, I finally found the solution to send post requests with headers AND body content. Here's the code:
var cl = new HttpClient();
cl.BaseAddress = new Uri("< YOUR URL >");
int _TimeoutSec = 90;
cl.Timeout = new TimeSpan(0, 0, _TimeoutSec);
string _ContentType = "application/x-www-form-urlencoded";
cl.DefaultRequestHeaders.Add(key, value);
cl.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(_ContentType));
cl.DefaultRequestHeaders.Add("key", "value");
cl.DefaultRequestHeaders.Add("key", "value");
var _UserAgent = "d-fens HttpClient";
cl.DefaultRequestHeaders.Add("User-Agent", _UserAgent);
var nvc = new List<KeyValuePair<string, string>>();
nvc.Add(new KeyValuePair<string, string>("key of content", "value"));
var req = new HttpRequestMessage(HttpMethod.Post, "http://www.t-lab.fr:3000/add_tips") { Content = new FormUrlEncodedContent(nvc) };
var res = cl.SendAsync(req);
a little more understandable
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
client.DefaultRequestHeaders.Add("Accept", "*/*");
var Parameters = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("Id", "1"),
};
var Request = new HttpRequestMessage(HttpMethod.Post, "Post_Url")
{
Content = new FormUrlEncodedContent(Parameters)
};
var Result = client.SendAsync(Request).Result.Content.ReadAsStringAsync();
}

Trustpilot OAuth Restful API: Unable to PostAsync

I am trying to use the Trustpilot API, to post invitations to review products.
I have successfully gone through the authentication step as you can see in the code below, however I am unable to successfully post data to the Trustpilot Invitations API. The PostAsnyc method appears to be stuck with an WaitingForActivation status. I wonder if there is anything you can suggest to help.
Here is my code for this (the API credentials here aren't genuine!):
using (HttpClient httpClient = new HttpClient())
{
string trustPilotAccessTokenUrl = "https://api.trustpilot.com/v1/oauth/oauth-business-users-for-applications/accesstoken";
httpClient.BaseAddress = new Uri(trustPilotAccessTokenUrl);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
var authString = "MyApiKey:MyApiSecret";
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Base64Encode(authString));
var stringPayload = "grant_type=password&username=MyUserEmail&password=MyPassword";
var httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/x-www-form-urlencoded");
HttpResponseMessage httpResponseMessage = httpClient.PostAsync(trustPilotAccessTokenUrl, httpContent).Result;
var accessTokenResponseString = httpResponseMessage.Content.ReadAsStringAsync().Result;
var accessTokenResponseObject = JsonConvert.DeserializeObject<AccessTokenResponse>(accessTokenResponseString);
// Create invitation object
var invitation = new ReviewInvitation
{
ReferenceID = "inv001",
RecipientName = "Jon Doe",
RecipientEmail = "Jon.Doe#comp.com",
Locale = "en-US"
};
var jsonInvitation = JsonConvert.SerializeObject(invitation);
var client = new HttpClient();
client.DefaultRequestHeaders.Add("token", accessTokenResponseObject.AccessToken);
var invitationsUri = new Uri("https://invitations-api.trustpilot.com/v1/private/business-units/{MyBusinessID}/invitations");
// This here as a status of WaitingForActivation!
var a = client.PostAsync(invitationsUri, new StringContent(jsonInvitation)).ContinueWith((postTask) => postTask.Result.EnsureSuccessStatusCode());
}
This is how I solved the issue:
// Serialize our concrete class into a JSON String
var jsonInvitation = JsonConvert.SerializeObject(invitationObject);
// Wrap our JSON inside a StringContent which then can be used by the HttpClient class
var stringContent = new StringContent(jsonInvitation);
// Get the access token
var token = GetAccessToken().AccessToken;
// Create a Uri
var postUri = new Uri("https://invitations-api.trustpilot.com/v1/private/business-units/{BusinessUnitID}/invitations");
// Set up the request
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, postUri);
request.Content = stringContent;
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
request.Content.Headers.Add("token", token);
// Set up the HttpClient
var httpClient = new HttpClient();
//httpClient.DefaultRequestHeaders.Accept.Clear();
//httpClient.BaseAddress = postUri;
//httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
//httpClient.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("en-US"));
var task = httpClient.SendAsync(request);
task.Wait();
This question here on SO was helpful:
How do you set the Content-Type header for an HttpClient request?

Categories

Resources