Issue in calling web API by using HttpClient Vs. WebClient C# - c#

I am trying to make a web API call by using HttpClient but getting Not authorized error. I am passing key in the header but still, it gives me this error. I can see my key in fiddler trace.
If I use WebClient then I am getting a successful response. request is same in both methods.
Using HttpClient:
#region HttpClient
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("apiKey", "MyKey");
var content = JsonConvert.SerializeObject(request);
var response = await client.PostAsJsonAsync("https://MyUrl", content);
if (response.IsSuccessStatusCode)
{
deliveryManagerQuoteResponse = await response.Content.ReadAsAsync<DeliveryManagerQuoteResponse>();
}
else
{
var reasonPhrase = response.ReasonPhrase;
if (reasonPhrase.ToUpper() == "NOT AUTHORIZED")
{
throw new KeyNotFoundException("Not authorized");
}
}
}
#endregion
Using WebClient:
#region WebClient
// Create string to hold JSON response
string jsonResponse = string.Empty;
using (var client = new WebClient())
{
try
{
client.UseDefaultCredentials = true;
client.Headers.Add("Content-Type:application/json");
client.Headers.Add("Accept:application/json");
client.Headers.Add("apiKey", "MyKey");
var uri = new Uri("https://MyUrl");
var content = JsonConvert.SerializeObject(request);
var response = client.UploadString(uri, "POST", content);
jsonResponse = response;
}
catch (WebException ex)
{
// Http Error
if (ex.Status == WebExceptionStatus.ProtocolError)
{
var webResponse = (HttpWebResponse)ex.Response;
var statusCode = (int)webResponse.StatusCode;
var msg = webResponse.StatusDescription;
throw new HttpException(statusCode, msg);
}
else
{
throw new HttpException(500, ex.Message);
}
}
}
#endregion

First things first, you are using HttpClient wrong.
Secondly, are you using fiddler to see what both requests look like? You should be able to see that the headers will look different. Right now you are using the Authorization Headers which will actually do something different than you want. All you need to do is simply add a regular 'ol header:
client.DefaultRequestHeaders.Add("apiKey", "MyKey");

Related

HttpClient.PostAsJsonAsync crushed without exception

I am trying to call an api(POST method) with HttpClient.PostAsJsonAsync. However, it stopped at httpClient.PostAsJsonAsync without any exception.
The source code as below:
public static async Task<oResult> PostApi(string JSON_sObject, string sEnd_Url) {
oResult oResult = new oResult();
var Data = JsonConvert.DeserializeObject(JSON_sObject);
var Url = "http://localhost:44340/" + sEnd_Url;
HttpClient httpClient = new HttpClient();
try {
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await httpClient.PostAsJsonAsync(new Uri(Url), Data); // it stopped here
if (response.IsSuccessStatusCode)
{
var sResponse_content = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<oResult>(sResponse_content);
}
else
{
return oResult;
}
}
catch (Exception ex)
{
LogFile(ex);
return oResult;
}
}
Please advice me if any issue from the source code.
Thank you
you should not trying serialize deserialize twice
remove from your code
var Data = JsonConvert.DeserializeObject(JSON_sObject);
and replace
HttpResponseMessage response = await httpClient.PostAsJsonAsync(new Uri(Url), Data);
with this
var content = new StringContent(JSON_sObject, Encoding.UTF8, "application/json");
var response = await client.PostAsync(sEnd_Url, content);
also fix base httpclient address
var baseUri= #"http://localhost:44340";
using HttpClient client = new HttpClient { BaseAddress = new Uri(baseUri) };
try {

Refactor HttpWebRequest to HttpClient?

How would I convert this to HttpClient? What I'm looking to do is submit a Tweet to the Twitter api and get the response as Json. The HttpWebRequest is working fine but I just want to port it to HttpClient. I made an attempt at it in the second code example, but it's not actually sending or receiving the response.
HttpWebRequest request = null;
WebResponse response = null;
string responseCode = String.Empty;
try
{
string postBody = "status=" + EncodingUtils.UrlEncode(status);
request = (HttpWebRequest)HttpWebRequest.Create(resource_url);
request.ServicePoint.Expect100Continue = true;
request.UseDefaultCredentials = true;
request.PreAuthenticate = true;
request.Credentials = CredentialCache.DefaultCredentials;
request.ServicePoint.ConnectionLimit = 1;
request.Headers.Add("Authorization", authHeader);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
using (Stream stream = request.GetRequestStream())
{
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(postBody);
}
}
using (response = request.GetResponse())
{
response.ContentType = "application/json";
responseCode = ((HttpWebResponse)response).StatusCode.ToString();
}
}
catch (WebException ex)
{
if (ex.Status != WebExceptionStatus.NameResolutionFailure)
{
request.Abort();
request = null;
}
throw ex;
}
return responseCode;
This is what I've tried to get it work:
private async Task<string> MakeWebRequest1(string status, string resource_url, string authHeader)
{
HttpClientHandler clientHandler = new HttpClientHandler();
clientHandler.Credentials = CredentialCache.DefaultCredentials;
clientHandler.PreAuthenticate = true;
clientHandler.AllowAutoRedirect = true;
string responseCode = "";
string postBody = "status=" + EncodingUtils.UrlEncode(status);
var request = new HttpRequestMessage()
{
RequestUri = new Uri(resource_url),
Method = HttpMethod.Post,
};
request.Headers.Add("Authorization", authHeader);
// request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
request.Content = new StringContent(postBody, Encoding.UTF8,"application/x-www-form-urlencoded");//CONTENT-TYPE header
using (HttpClient client = new HttpClient(clientHandler))
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
// Stream stuff = await client.GetStreamAsync(resource_url);
using (HttpResponseMessage response = await client.SendAsync(request))
{
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
if(response.StatusCode == HttpStatusCode.OK)
{
responseCode = "OK";
}
}
}
clientHandler.Dispose();
return responseCode;
}
enter code here
I've tried to add another parameter to the request and it's always coming back as 401 unauthorized. I'm trying to create a Twitter thread. If I remove the in_reply_to_status_id then it's fine.
data = new Dictionary<string, string> {
["status"] = "#username + status,
["in_reply_to_status_id"] = "1167588690929115136"
};
The Twitter API describes it here https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/post-statuses-update
Reference You're using HttpClient wrong to understand why a static client is being used.
static Lazy<HttpClient> client = new Lazy<HttpClient>(() => {
HttpClientHandler clientHandler = new HttpClientHandler {
Credentials = CredentialCache.DefaultCredentials,
PreAuthenticate = true,
AllowAutoRedirect = true
};
return new HttpClient(clientHandler);
});
private async Task<string> PostStatusRequestAsync(string status, string resource_url, string authHeader) {
using (var request = new HttpRequestMessage(HttpMethod.Post, resource_url)) {
request.Headers.TryAddWithoutValidation("Authorization", authHeader);
request.Headers.Accept.Clear();
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var data = new Dictionary<string, string> {
["status"] = status
};
request.Content = new FormUrlEncodedContent(data);
using (HttpResponseMessage response = await client.Value.SendAsync(request)) {
return response.StatusCode.ToString();
}
}
}
Note the use of the FormUrlEncodedContent for the request body, which will encode and concatenate the data as well as take care of the mime type header
...but it's not actually sending or receiving the response.
Ensure that the above is not invoked as a synchronous blocking call, like .Result, which could cause a deadlock.
For example, an async event handler can be used to make the async call
public async void onButtonClick(object sender, EventArgs args) {
//Non-blocking call
var tweetRequestCode = await PostStatusRequestAsync(TweetText, AuthUtils.GetResourceUrl(), AuthUtils.GetWebRequestHeader()));
//back on UI thread
//...
}
Reference Async/Await - Best Practices in Asynchronous Programming

Cannot manage redirect

I'm trying to manage the redirect on this site: https://uk.soccerway.com, so I wrote this code:
public static string GetHtml(string url)
{
HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(url);
try
{
webReq.AllowAutoRedirect = false;
HttpWebResponse response = webReq.GetResponse() as HttpWebResponse;
WebClient client = new WebClient();
client.Encoding = System.Text.Encoding.UTF8;
url = (response.Headers["Location"] != null) ? response.Headers["Location"] : url;
return client.DownloadString(url);
}
catch (WebException)
{
throw;
}
}
this will return an Exception, in particular:
'The remote server returned an error: (302) Moved Temporarily.'
what I did wrong?
I can see this being a useful answer for many other users so reading this article helped me arrive with the code below. It recursively handles the redirects in case there are more than 1 redirects. Finally it returns the response:
public static HttpResponseMessage MakeRequest(string url)
{
using (var client = new HttpClient())
{
var request = new HttpRequestMessage()
{
RequestUri = new Uri(url),
Method = HttpMethod.Get
};
HttpResponseMessage response = client.SendAsync(request).Result;
var statusCode = (int)response.StatusCode;
// We want to handle redirects ourselves so that we can
// determine the final redirect Location (via header)
if (statusCode >= 300 && statusCode <= 399)
{
var redirectUri = response.Headers.Location;
if (!redirectUri.IsAbsoluteUri)
{
redirectUri = new Uri(request.RequestUri.
GetLeftPart(UriPartial.Authority) + redirectUri);
}
return MakeRequest(redirectUri.AbsoluteUri);
}
return response;
}
}
Usage
HttpResponseMessage response = MakeRequest("https://uk.soccerway.com");
if (response.IsSuccessStatusCode)
{
string content = response.Content.ReadAsStringAsync().Result;
}

Authentication from C# HttpClient against Spring server JWT Tokens

I'm coding a Xamarin cross-platform mobile app. The server is a SpringMVC server which uses JWT Tokens to authenticate against each of the endpoints/webservices. So basically when I'm doing a request to a webservice for first time, before I need to hit a /authorize POST endpoint sending my email and password, the endpoint response will contain in the "Cookie" header an authenticaton token which comes as "AUTH_TOKEN={MD5-String}". Once I got the token I send the request to the endpoint, let's say /feed. But my problem is that I cannot figure out the way of setting the "Cookie" header in the C# HttpClient. I tried everything but the endpoing just keeps responding with the login screen html instead of the actual JSON response. I tried the same steps in Postman and other REST clients and It worked. So It means that I'm doing something wrong. Here's my code:
public class RestService : IRestService
{
HttpClient client;
HttpClientHandler handler;
CookieContainer cookies;
string authToken;
public List<Feed> FeedItems { get; private set; }
public RestService()
{
cookies = new CookieContainer();
handler = new HttpClientHandler();
handler.UseCookies = true; //Otherwise It'll not use the cookies container!!
handler.CookieContainer = cookies;
client = new HttpClient(handler);
client.MaxResponseContentBufferSize = 256000;
}
public async Task<List<Role>> GetFeedDataAsync()
{
//Request credentials
//Credentials validation
var credentials = new HalliganCredential()
{
email = Constants.Username,
password = Constants.Password
};
var jsonCredentials = JsonConvert.SerializeObject(credentials);
var jsonCredentialsContent = new StringContent(jsonCredentials, Encoding.UTF8, "application/json");
var authorizeUri = new Uri(Constants.AuthorizeEndpoint);
var authorizeResponse = await client.PostAsync(authorizeUri, jsonCredentialsContent);
if (authorizeResponse.IsSuccessStatusCode)
{
//If authentication went OK
IEnumerable<Cookie> responseCookies = cookies.GetCookies(authorizeUri).Cast<Cookie>();
foreach (Cookie cookie in responseCookies)
{
if (cookie.Name.Equals("AUTH-TOKEN"))
{
authToken = cookie.Value;
}
}
}
else
{
//Authentication failed throw error
throw new HttpRequestException("Authentication failed");
}
FeedItems = new List<Feed>();
//Making the GET request
var uri = new Uri(string.Format(Constants.FeedEnpoint, string.Empty));
try
{
cookies.Add(uri, new Cookie("Cookie", string.Format("AUTH_TOKEN={0}", authToken)));
client.DefaultRequestHeaders.Add("Cookie", string.Format("AUTH_TOKEN={0}", authToken));
handler.CookieContainer.Add(uri, new Cookie("Cookie", string.Format("AUTH_TOKEN={0}", authToken)));
var response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
//Credentials validation
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
FeedItems = JsonConvert.DeserializeObject<List<Feed>>(content);
}
}
catch (Exception ex)
{
Debug.WriteLine(#"ERROR {0}", ex.Message);
}
return FeedItems;
}
}
When I reach the line var content = await response.Content.ReadAsStringAsync(); the response is an HTML string instead of the actual JSON response.
I tried with several other key values for the header, although "Cookie" is the one that worked on Postman.
I tried with "Set-Cookie", "set-cookie", "Set-Cookie", setting the header as "AUTH_TOKEN". I tried all this convinations in different places like adding them in the cookie CookieContainer, in the handler CookieContainer and in the client.DefaultRequestHeaders.
I tried setting on and off the handler.UseCookies = true; //Otherwise It'll not use the cookies container!! line.
Any help will be welcome!
UPDATE
I tried with one of the suggested solutions but didn't work I tried either with UseCookies in true and false.
//Making the GET request
var baseAddress = new Uri("http://app.******.io");
using (var handler = new HttpClientHandler { UseCookies = true })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
var message = new HttpRequestMessage(HttpMethod.Get, "/api/v1/feed?api_key=sarasa");
message.Headers.Add("Cookie", string.Format("AUTH_TOKEN={0}", authToken));
message.Headers.Add("Cookie", string.Format("AUTH_TOKEN={0};", authToken));
message.Headers.Add("Set-Cookie", string.Format("AUTH_TOKEN={0}", authToken));
message.Headers.Add("AUTH_TOKEN", authToken);
var result = await client.SendAsync(message);
result.EnsureSuccessStatusCode();
if (result.IsSuccessStatusCode)
{
var content = await result.Content.ReadAsStringAsync();
FeedItems= JsonConvert.DeserializeObject<List<Feed>>(content);
}
}
return FeedItems;
UPDATE
I tried with the another solution, same results.
var baseAddress = new Uri("http://app.*****.io");
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
cookieContainer.Add(baseAddress, new Cookie("Cookie", string.Format("AUTH_TOKEN={0}", authToken)));
cookieContainer.Add(baseAddress, new Cookie("Set-Cookie", string.Format("AUTH_TOKEN={0}", authToken)));
var result = client.GetAsync("/api/v1/roles?api_key=sarasa").Result;
result.EnsureSuccessStatusCode();
if (result.IsSuccessStatusCode)
{
var content = await result.Content.ReadAsStringAsync();
RolesItems = JsonConvert.DeserializeObject<List<Role>>(content);
}
}
Is there an alternative to HttpClient?
I finally could set the Cookie header parameter, but I change HttpClient by HttpWebRequest
Getting the Cookies
//Credentials validation
var credentials = new CompanyCredential()
{
Email = Constants.Username,
Password = Constants.Password
};
var jsonCredentials = JsonConvert.SerializeObject(credentials);
var request = (HttpWebRequest) WebRequest.Create(new Uri(baseAddress, Constants.AuthorizeEndpoint));
request.ContentType = "application/json";
request.Method = "POST";
var requestStream = request.GetRequestStreamAsync().Result;
var streamWriter = new StreamWriter(requestStream);
streamWriter.Write(jsonCredentials);
streamWriter.Flush();
try
{
HttpWebResponse response = (HttpWebResponse) request.GetResponseAsync().Result;
if (response.StatusCode.Equals(HttpStatusCode.OK))
{
authToken = response.Headers["Set-Cookie"];
tokenExpireDate = DateTime.ParseExact(response.Headers["Expires"], "yyyy-MM-dd HH:mm:ss,fff",
System.Globalization.CultureInfo.InvariantCulture);
}
else
{
//Authentication failed throw error
throw new HttpRequestException("Authentication failed");
}
} catch (Exception e)
{
Debug.WriteLine(string.Format("Warning: {0}", e.Message));
}
Setting the Cookies
var request = (HttpWebRequest)WebRequest.Create(new Uri(baseAddress, endpoint));
SetHeaders(request);
if (string.IsNullOrEmpty(authToken))
{
throw new AuthTokenNullException();
}
request.Headers["Cookie"] = authToken;
request.Method = "GET";
HttpWebResponse response = request.GetResponseAsync().Result as HttpWebResponse;
if (!response.StatusCode.Equals(HttpStatusCode.OK))
{
throw new HttpRequestException(string.Format("Warning expected response as 200 and got {0}", Convert.ToString(response.StatusCode)));
}
var reader = new StreamReader(response.GetResponseStream());
string stringResponse = reader.ReadToEnd();
return JsonConvert.DeserializeObject<T>(stringResponse);

How to post parameter to Azure Service URL using WebClient in C#

I have tested/googled for hours on how to POST parameter in C# to an Azure Service without getting the Error 405.
The following code in C++ using Chilkat lib works fine
CkHttp http;
CkHttpRequest req;
http.put_SessionLogFilename("c:/temp/httpLog.txt");
req.put_HttpVerb("POST");
req.put_Path("/api/test?value=1234");
CkHttpResponse *resp = http.SynchronousRequest("http://testservice.cloudapp.net",80,false,req);
if (resp == 0 )
afxDump << http.lastErrorText() << "\r\n";
afxDump << resp->bodyStr() << "\r\n";
delete resp;
But if it uses this c# code i get the Error 405.
string uri = "http://testservice.cloudapp.net/api/test";
string parameter = "value=1234";
using (WebClient wc = new WebClient())
{
wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
string HtmlResult = wc.UploadString(uri, parameter);
}
Any hints what i do wrong?
You'll be better off using HttpClient instead of WebClient . By looking at what the C++ code does it should be something like this in C# using HttpClient
public void Test() {
using (HttpClient client = new HttpClient()) {
client.BaseAddress = new Uri("http://testservice.cloudapp.net");
var response = client.PostAsync("api/test?value=1234", new StringContent(string.Empty)).Result;
var statusCode = response.StatusCode;
var errorText = response.ReasonPhrase;
// response.EnsureSuccessStatusCode(); will throw an exception if status code does not indicate success
var responseContentAsString = response.Content.ReadAsStringAsync().Result;
var responseContentAsBYtes = response.Content.ReadAsByteArrayAsync().Result;
}
}
Here is the async version of the code above
public async Task TestAsync() {
using (HttpClient client = new HttpClient()) {
client.BaseAddress = new Uri("http://testservice.cloudapp.net");
var response = await client.PostAsync("api/test?value=1234", new StringContent(string.Empty));
var statusCode = response.StatusCode;
var errorText = response.ReasonPhrase;
// response.EnsureSuccessStatusCode(); will throw an exception if status code does not indicate success
var responseContentAsString = await response.Content.ReadAsStringAsync();
var responseContentAsBYtes = await response.Content.ReadAsByteArrayAsync();
}
}

Categories

Resources