I am trying to move change some methods from httpwebrequest to httpclient. I have done most of the work but stuck with this one. Can someone help to achieve this.
string url = someurl;
HttpWebRequest request = CreateRequest(url);
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
request.ServicePoint.Expect100Continue = false;
string body = #"somestring here.";
byte[] postBytes = Encoding.UTF8.GetBytes(body);
request.ContentLength = postBytes.Length;
Stream stream = request.GetRequestStream();
stream.Write(postBytes, 0, postBytes.Length);
stream.Close();
response = (HttpWebResponse)request.GetResponse();
I need to convert this method using HttpClient.
This is what I have tried.
string url = someurl;
var client = new HttpClient();;
client.DefaultRequestHeaders
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));//ACCEPT header
//request.ContentType = "application/x-www-form-urlencoded";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post,url);
string body = #"somestring here...";
var content = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded");
request.Content = content;
var ss = client.PostAsync(url,content).Result;
string str2 = await ss.Content.ReadAsStringAsync();
and I am not getting this part to work.
string body = #"somestring here.";
byte[] postBytes = Encoding.UTF8.GetBytes(body);
request.ContentLength = postBytes.Length;
Stream stream = request.GetRequestStream();
stream.Write(postBytes, 0, postBytes.Length);
stream.Close();
This is the sample client class which I use most of the time. You can use either PostAsync or SendAsync
public class RestClient
{
public bool IsDisposed { get; private set; }
public HttpClient BaseClient { get; private set; }
private readonly string jsonMediaType = "application/json";
public RestClient(string hostName, string token, HttpClient client)
{
BaseClient = client;
BaseClient.BaseAddress = new Uri(hostName);
BaseClient.DefaultRequestHeaders.Add("Authorization", token);
}
public async Task<string> PostAsync(string resource, string postData)
{
StringContent strContent = new StringContent(postData, Encoding.UTF8, jsonMediaType);
HttpResponseMessage responseMessage = await BaseClient.PostAsync(resource, strContent).ConfigureAwait(false);
responseMessage.RaiseExceptionIfFailed();
return await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
}
public async Task<string> SendAsync(HttpMethod method, string resource, string token, string postData)
{
var resourceUri = new Uri(resource, UriKind.Relative);
var uri = new Uri(BaseClient.BaseAddress, resourceUri);
HttpRequestMessage request = new HttpRequestMessage(method, uri);
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
if (!string.IsNullOrEmpty(postData))
{
request.Content = new StringContent(postData, Encoding.UTF8, jsonMediaType);
}
HttpResponseMessage response = BaseClient.SendAsync(request).Result;
response.RaiseExceptionIfFailed();
return await response.Content.ReadAsStringAsync();
}
protected virtual void Dispose(bool isDisposing)
{
if (IsDisposed)
{
return;
}
if (isDisposing)
{
BaseClient.Dispose();
}
IsDisposed = true;
}
public virtual void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~RestClient()
{
Dispose(false);
}
}
Related
I am coding both a client and an API in C# .Net4.8. I am POSTing data from the client and I have an ActionFilterAttribute on the endpoint method. I want to read the POSTed data within the ActionFilterAttribute method. I found I was able to POST form data using FormUrlEncodedContent and it is received, but when I try POSTing JSON data using stringContent it is not received.
How can I change either my client side code or API code to POST JSON correctly?
POSTing form data like so works:
HttpClientHandler handler = new HttpClientHandler()
HttpClient httpClient = new HttpClient(handler);
FormUrlEncodedContent formString = new FormUrlEncodedContent(data);
response = httpClient.PostAsync(url, formString).Result; // run synchronously
And then on the API side, dataFromClient gets populated:
public class myFilter : ActionFilterAttribute
{
public string Feature { get; set; }
public myFilter(string feature)
{
this.Feature = feature;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string dataFromClient = (HttpContext.Current.Request.Params["dataFromClient"] == null) ? "" : HttpContext.Current.Request.Params["dataFromClient"];
// do other stuff with dataFromClient here
}
}
POSTing JSON data like so does not work:
HttpClientHandler handler = new HttpClientHandler()
HttpClient httpClient = new HttpClient(handler);
StringContent stringContent = new StringContent(jsonString, System.Text.Encoding.UTF8, "application/json");
response = httpClient.PostAsync(url, stringContent).Result; // run synchronously
With this method, dataFromClient in the API is empty.
Since you're posting application/json,you should read request body. I think, here's what you want;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.HttpContext.Request;
var initialBody = request.Body;
try
{
request.EnableRewind();
using (StreamReader reader = new StreamReader(request.Body))
{
string dataFromClient = reader.ReadToEnd();
// do other stuff with dataFromClient here
return dataFromClient;
}
}
finally
{
request.Body = initialBody;
}
filterContext.Request.Body.Position = 0
return string.Empty;
}
I need to post data with files but I face this problem - all data is null.
It works when I use postman:
My post function in ASP.NET Core Web API:
public async Task<ActionResult<Category>> PostCategory([FromForm]CategoryViewModel model)
{
Category category = new Category()
{
Brief = model.Brief,
Color = model.Color,
IsDraft = model.IsDraft,
Name = model.Name,
Priority = model.Priority,
Update = DateTime.Now
};
if (model.Files != null)
{
category.IconUrl = ApplicationManager.UploadFiles(model.Files, "content/category")[0];
}
_context.Categories.Add(category);
await _context.SaveChangesAsync();
return CreatedAtAction("GetCategory", new { id = category.Id }, category);
}
My post function in ASP.NET Core MVC:
private ApiClient _client;
_client = new ApiClient(new Uri("https:localhost:55436/api/"));
public async Task<IActionResult> Create([FromForm]CategoryViewModel category)
{
var uri = new Uri(_appSettings.WebApiBaseUrl + "Categories");
var response = await _client.PostAsync<Category, CategoryViewModel>(uri, category);
return RedirectToAction("index");
}
My ApiClient class:
public partial class ApiClient
{
private readonly HttpClient _httpClient;
private Uri BaseEndpoint { get; set; }
public ApiClient(Uri baseEndpoint)
{
if (baseEndpoint == null)
{
throw new ArgumentNullException("baseEndpoint");
}
BaseEndpoint = baseEndpoint;
_httpClient = new HttpClient();
}
/// <summary>
/// Common method for making GET calls
/// </summary>
public async Task<T> GetAsync<T>(Uri requestUrl)
{
addHeaders();
var response = await _httpClient.GetAsync(requestUrl, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
var data = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(data);
}
public async Task<HttpResponseMessage> PostStreamAsync(Uri requestUrl, object content)
{
using (var client = new HttpClient())
using (var request = new HttpRequestMessage(HttpMethod.Post, requestUrl))
using (var httpContent = CreateHttpContentForStream(content))
{
request.Content = httpContent;
using (var response = await client
.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)
.ConfigureAwait(false))
{
response.EnsureSuccessStatusCode();
return response;
}
}
}
public async Task<HttpResponseMessage> PostBasicAsync(Uri requestUrl, object content)
{
using (var client = new HttpClient())
using (var request = new HttpRequestMessage(HttpMethod.Post, requestUrl))
{
var json = JsonConvert.SerializeObject(content);
using (var stringContent = new StringContent(json, Encoding.UTF8, "application/json"))
{
request.Content = stringContent;
using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)
.ConfigureAwait(false))
{
response.EnsureSuccessStatusCode();
return response;
}
}
}
}
public static void SerializeJsonIntoStream(object value, Stream stream)
{
using (var sw = new StreamWriter(stream, new UTF8Encoding(false), 1024, true))
using (var jtw = new JsonTextWriter(sw) { Formatting = Formatting.None })
{
var js = new JsonSerializer();
js.Serialize(jtw, value);
jtw.Flush();
}
}
private static HttpContent CreateHttpContentForStream<T>(T content)
{
HttpContent httpContent = null;
if (content != null)
{
var ms = new MemoryStream();
SerializeJsonIntoStream(content, ms);
ms.Seek(0, SeekOrigin.Begin);
httpContent = new StreamContent(ms);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
return httpContent;
}
/// <summary>
/// Common method for making POST calls
/// </summary>
public async Task<T> PostAsync<T>(Uri requestUrl, T content)
{
addHeaders();
var response = await _httpClient.PostAsync(requestUrl.ToString(), CreateHttpContent<T>(content));
response.EnsureSuccessStatusCode();
var data = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(data);
}
public async Task<T1> PostAsync<T1, T2>(Uri requestUrl, T2 content)
{
addHeaders();
var response = await _httpClient.PostAsync(requestUrl.ToString(), CreateHttpContent<T2>(content));
response.EnsureSuccessStatusCode();
var data = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T1>(data);
}
public Uri CreateRequestUri(string relativePath, string queryString = "")
{
var endpoint = new Uri(BaseEndpoint, relativePath);
var uriBuilder = new UriBuilder(endpoint);
uriBuilder.Query = queryString;
return uriBuilder.Uri;
}
public HttpContent CreateHttpContent<T>(T content)
{
var json = JsonConvert.SerializeObject(content, MicrosoftDateFormatSettings);
var value = new StringContent(json, Encoding.UTF8, "application/json");
return value;
}
public static JsonSerializerSettings MicrosoftDateFormatSettings
{
get
{
return new JsonSerializerSettings
{
DateFormatHandling = DateFormatHandling.MicrosoftDateFormat
};
}
}
public void addHeaders()
{
_httpClient.DefaultRequestHeaders.Remove("userIP");
_httpClient.DefaultRequestHeaders.Add("userIP", "192.168.1.1");
}
}
If you want to post the multipart/form-data using HttpClient, you should write a separate post method using MultipartFormDataContent as HttpContent type as shown:
PostCategoryAsync
public async Task<Category> PostCategoryAsync(Uri requestUrl, CategoryViewModel content)
{
addHeaders();
var response = new HttpResponseMessage();
var fileContent = new StreamContent(content.Files.OpenReadStream())
{
Headers =
{
ContentLength = content.Files.Length,
ContentType = new MediaTypeHeaderValue(content.Files.ContentType)
}
};
var formDataContent = new MultipartFormDataContent();
formDataContent.Add(fileContent, "Files", content.Files.FileName); // file
//other form inputs
formDataContent.Add(new StringContent(content.Name), "Name");
formDataContent.Add(new StringContent(content.Brief), "Brief");
formDataContent.Add(new StringContent(content.IsDraft.ToString()), "IsDraft");
formDataContent.Add(new StringContent(content.Color), "Color");
formDataContent.Add(new StringContent(content.Priority), "Priority");
response = await _httpClient.PostAsync(requestUrl.ToString(), formDataContent);
var data = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode();
return JsonConvert.DeserializeObject<Category>(data);
}
MVC controller
var response = await _client.PostCategoryAsync(uri, category);
Web API
So I am working on writing an extension class for my project using HttpClient since I am moving over from HttpWebRequest.
When doing the POST request, how do I send a normal string as a parameter? No json or anything just a simple string.
And this is what it looks like so far.
static class HttpClientExtension
{
static HttpClient client = new HttpClient();
public static string GetHttpResponse(string URL)
{
string fail = "Fail";
client.BaseAddress = new Uri(URL);
HttpResponseMessage Response = client.GetAsync(URL).GetAwaiter().GetResult();
if (Response.IsSuccessStatusCode)
return Response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
else
return fail;
}
public static string PostRequest(string URI, string PostParams)
{
client.PostAsync(URI, new StringContent(PostParams));
HttpResponseMessage response = client.GetAsync(URI).GetAwaiter().GetResult();
string content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
return content;
}
}
If you look at this like
client.PostAsync(URI, new StringContent(PostParams));
You can see that I just tried creating new StringContent and passing a string into it and the response returned 404 page not found.
How do I properly use Post.Async(); do I send a string or byte array? Because with HttpWebRequest you would do it like this
public static void SetPost(this HttpWebRequest request, string postdata)
{
request.Method = "POST";
byte[] bytes = Encoding.UTF8.GetBytes(postdata);
using (Stream requestStream = request.GetRequestStream())
requestStream.Write(bytes, 0, bytes.Length);
}
In the PostRequest the following is done..
client.PostAsync(URI, new StringContent(PostParams));
HttpResponseMessage response = client.GetAsync(URI).GetAwaiter().GetResult();
Which does not capture the response of the POST.
Refactor to
public static string PostRequest(string URI, string PostParams) {
var response = client.PostAsync(URI, new StringContent(PostParams)).GetAwaiter().GetResult();
var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
return content;
}
HttpClient is primarily meant to be used async so consider refactoring to
public static async Task<string> PostRequestAsync(string URI, string PostParams) {
var response = await client.PostAsync(URI, new StringContent(PostParams));
var content = await response.Content.ReadAsStringAsync();
return content;
}
You need prepare object and then you will serialize the object using Newtonsoft.Json. After that you will prepare byte content from the buffer. We are using api url api/auth/login and it is not full api url as we used dependency injection and configure base address in startup, see the second code.
public async void Login(string username, string password)
{
LoginDTO login = new LoginDTO();
login.Email = username;
login.Password = password;
var myContent = JsonConvert.SerializeObject(login);
var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await httpClient.PostAsync("api/auth/login", byteContent);
var contents = await response.Content.ReadAsStringAsync();
}
services.AddHttpClient<IAuthService, AuthService>(client =>
{
client.BaseAddress = new Uri("https://localhost:44354/");
});
.NET 5 Solution
In .NET 5, There is new class JsonContent and you can implement this easily
LoginDTO login = new LoginDTO();
login.Email = username;
login.Password = password;
JsonContent content = JsonContent.Create(login);
var url = "http://...";
HttpResponseMessage response = await httpClient.PostAsync(url, content);
I have worked the following (using the package Ngonzalez.ImageProcessorCore).
Query (ASP.NET Core 2 Controller):
async Task<byte[]> CreateImage(IFormFile file)
{
using (var memoryStream = new MemoryStream())
{
await file.CopyToAsync(memoryStream);
var image = new Image(memoryStream);
var height = image.Height < 150 ? image.Height : 150;
image.Resize((int)(image.Width * height / image.Height), height).Save(memoryStream);
return memoryStream.ToArray();
}
}
[HttpPost, ValidateAntiForgeryToken]
public async Task<IActionResult> ImageAdd(ImageAddVm vm)
{
byte[] image = null;
if (vm.File != null && vm.File.Length > 0)
image = await CreateImage(vm.File);
if (image != null)
{
var json = JsonConvert.SerializeObject(new { vm.ObjectId, image });
var content = new StringContent(json, Encoding.UTF8, "application/json");
var client= new HttpClient();
await client.PostAsync($"{ApiUrl}/SaveImage", content);
}
return RedirectToAction("ReturnAction");
}
Api (ASP.NET Core 2 Controller):
public class ObjectImage
{
public int ObjectId { get; set; }
public byte[] Image { get; set; }
}
[HttpPost("SaveImage")]
public void SaveImage([FromBody]object content)
{
var obj = JsonConvert.DeserializeObject<ObjectImage>(content.ToString());
_db.Images.Find(obj.ObjectId).Image = obj.Image;
_db.SaveChanges();
}
I have the following code:
What do I put as the second argument for GetPage?
The second argument should be the previous cookie request of the same url.
for example I put google.com as the first argument to get the cookie when I make a get Request, but for the second how do I insert it?
static void Main()
{
GetPage("http://google.com/",cookieContainer??);
}
public class CookieAwareWebClient : WebClient
{
public CookieAwareWebClient(CookieContainer container)
{
CookieContainer = container;
}
public CookieAwareWebClient()
: this(new CookieContainer())
{ }
public CookieContainer CookieContainer { get; private set; }
protected override WebRequest GetWebRequest(Uri address)
{
var request = (HttpWebRequest)base.GetWebRequest(address);
request.CookieContainer = CookieContainer;
return request;
}
}
public HtmlAgilityPack.HtmlDocument GetPage(string url, CookieContainer CookieContainer)
{
Uri absoluteUri = new Uri("http://google.com/");
var cookies = CookieContainer.GetCookies(absoluteUri);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.CookieContainer = new CookieContainer();
foreach (Cookie cookie in cookies)
{
request.CookieContainer.Add(cookie);
}
request.Method = "GET";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
var stream = response.GetResponseStream();
using (var reader = new StreamReader(stream))
{
string html = reader.ReadToEnd();
var doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(html);
return doc;
}
}
Well really I guess it would be https://login.live.com/, But regardless my goal is to make a windows application that will be able to log into bing.com. Right now I have a button on my windows form that's purpose is to log into bing.com. I found some code on how to log into websites and tried to implement it but to no avail. Right now this is what I have in the button click event:
var loginAddress = "https://login.live.com/";
var loginData = new NameValueCollection
{
{ "username", "myUserName" },
{ "password", "myPassword" }
};
var client = new CookieAwareWebClient();
client.Login(loginAddress, loginData);
Process.Start("IExplore.exe", "https://login.live.com/");
Here is my CookieAwareWebClient class code:
public class CookieAwareWebClient : WebClient
{
public void Login(string loginPageAddress, NameValueCollection loginData)
{
CookieContainer container;
var request = (HttpWebRequest)WebRequest.Create(loginPageAddress);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
var buffer = Encoding.ASCII.GetBytes(loginData.ToString());
request.ContentLength = buffer.Length;
var requestStream = request.GetRequestStream();
requestStream.Write(buffer, 0, buffer.Length);
requestStream.Close();
container = request.CookieContainer = new CookieContainer();
var response = request.GetResponse();
response.Close();
CookieContainer = container;
}
public CookieAwareWebClient(CookieContainer container)
{
CookieContainer = container;
}
public CookieAwareWebClient()
: this(new CookieContainer())
{ }
public CookieContainer CookieContainer { get; private set; }
protected override WebRequest GetWebRequest(Uri address)
{
var request = (HttpWebRequest)base.GetWebRequest(address);
request.CookieContainer = CookieContainer;
return request;
}
}