401 Unauthorized on SECOND HttpClient/HttpWebRequest call - c#

I have a application that uses the SharePoint 2010 REST API.
In the process of creating an Item there are multiple request done after each other:
1 Call: Getting Items from List: Succes
2 Call: Create Item: 401 Unauthorized
This is the same if I do it like this:
1 Call: Create Item: Succes
2 Call: Delete Item: 401 Unauthorized
What I know is that my functions work separately they DON'T work when they are called after each other.
When I close the application (Windows Phone 8.1 app) after creating a item and when restarted try to delete the item it works.
First I thought it had to do with the way I handle my fields so I changed them to NULL in a finally statement but that didn't work.
public async Task<bool> CreateNewItem(NewItem myNewItem)
{
try
{
StatusBar statusBar = await MyStatusBar.ShowStatusBar("Creating new List Item.");
//Retrieving Settings from Saved file
mySettings = await MyCredentials.GetMySettings();
myCred = new NetworkCredential(mySettings.UserName, mySettings.Password, mySettings.Domain);
using (var handler = new HttpClientHandler { Credentials = myCred })
{
HttpClient client = new HttpClient(handler);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
NewItem newItem = myNewItem;
var jsonObject = JsonConvert.SerializeObject(newItem);
HttpResponseMessage response = await client.PostAsync(new Uri(baseUrl + listNameHourRegistration), new StringContent(jsonObject.ToString(), Encoding.Unicode, "application/json"));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response.EnsureSuccessStatusCode();
string responseMessage = await response.Content.ReadAsStringAsync();
client.Dispose();
if (responseMessage.Length > 0)
return true;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return false;
}
finally
{
request = null;
response = null;
myCred = null;
mySettings = null;
}
return false;
}

Just run into the same problem.
Anyway, the 2nd request does not follow the same authentication procedure. Even if you initialize a new HttpClient object. I sniffed the HTTP traffic.
After the 1st request I am doing another with different credentials. This is also ending in a 401. I am really confused...
Seems the NTLM Handshake stucks at the 2nd of 6 steps
http://www.innovation.ch/personal/ronald/ntlm.html
Edit:
You may want to use the CSOM.
http://social.msdn.microsoft.com/Forums/office/en-US/efd12f11-cdb3-4b28-a9e0-32bfab71a419/windows-phone-81-sdk-for-sharepoint-csom?forum=sharepointdevelopment

While I still don't know what the actual problem is, at least I found a workaround: Use the WebRequest class instead of HttpClient.

I was running into this same error when I realized I was adding the headers each time I was calling the endpoint. Hopefully this will help someone.
Instead I initialized the HttpClient instance in my class constructor and set the headers there. Also I learned it is better practice to only use 1 instance instead of recreating with "using" (See this article https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/)
I'm invoking CallApiAsync from another class in a loop.
Here's my final solution:
class ApiShared
{
private HttpClient client;
public ApiShared() {
client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", ConfigurationManager.AppSettings["ApiKey"]);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public async Task<ApiResponse_Root> CallApiAsync(string endpoint)
{
// Make API call
Uri endpointUri = new Uri(endpoint);
var stringTask = client.GetStringAsync(endpointUri);
var data = JsonConvert.DeserializeObject<ApiResponse_Root>(await stringTask);
return data;
}
}

On a windows machine you can resolve this with this registry setting change:
Go to the following Registry entry:
Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa
Now add a new DWORD to the Lsa folder called: DisableLoopBackCheck and set this to 1

I see that this question has been posted long back. But I don't see a correctly working solution posted yet to this thread.
I faced exactly the same issue where the next requests kept on failing returning me 401 UnAuthorized.
I figured out using fiddler that from SECOND request onwards, there was a Cookie added to the request which was possibly a result of Set-Cookie response sent by the server along with first response.
So here's how I tackled the situation - Make UseCookies false:
new HttpClientHandler { Credentials = myCred, UseCookies = false }
This should resolve your issue. Hope this helps someone who's looking for a solution to a similar issue.

Related

Trouble posting more than once to webapi from Blazor component- "This instance has already started one or more requests." [duplicate]

I am creating an application in .Net Core 2.1 and I am using http client for web requests. The issue is I have to send parallel calls to save time and for that I am using Task.WhenAll() method but when I hit this method I get the error "This instance has already started one or more requests. Properties can only be modified before sending the first request" Previously I was using RestSharp and everything was fine but I want to use httpclient. Here is the code:
public async Task<User> AddUser(string email)
{
var url = "user/";
_client.BaseAddress = new Uri("https://myWeb.com/");
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(Constants."application/json"));
_client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
var json = new {email = email }
var response = await _client.PostAsJsonAsync(url,json);
if (response .IsSuccessStatusCode)
{ ....
Here is the constructor:
private readonly HttpClient _httpClient;
public UserRepository(HttpClient httpClient)
{
_httpClient = httpClient;
}
Method calling:
var user1 = AddUser("user#user.com");
var user2 = AddUser("test#test.com");
await Task.WhenAll(user1, user2);
and here is the startup configuation:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
So what am I doing wrong? Do I need to change AddSingleton with AddTransient() or is there any other issue. One more question do I need to use _client.Dispose() after the response because the tutorial which I followed didn't use dispose method so I am little confused in that.
HttpClient.DefaultRequestHeaders (and BaseAddress) should only be set once, before you make any requests. HttpClient is only safe to use as a singleton if you don't modify it once it's in use.
Rather than setting DefaultRequestHeaders, set the headers on each HttpRequestMessage you are sending.
var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Accept.Clear();
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent("{...}", Encoding.UTF8, "application/json");
var response = await _client.SendAsync(request, CancellationToken.None);
Replace "{...}" with your JSON.
Maybe my two cents will help someone.
I ran into this issue when refreshing the page when debugging the application.
I was using a singleton, but each refresh, it was trying to set the base address. So I just wrapped it in a check to see if the base address had already been set.
The issue for me was, it was trying to set the baseAddress, even though it was already set. You can't do this with a httpClient.
if (_httpClient.BaseAddress == null)
{
_httpClient.BaseAddress = new Uri(baseAddress);
}
The issue is caused by resetting BaseAddress and headers for the same instance of the httpclient.
I tried
if (_httpClient.BaseAddress == null)
but I am not keen on this.
In my opinion, a better soloution is to use the httpclientFactory. This will terminate and garbage collect the instance of the httpclient after its use.
private readonly IHttpClientFactory _httpClientFactory;
public Foo (IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public httpresponse Bar ()
{
_httpClient = _httpClientFactory.CreateClient(command.ClientId);
using var response = await _httpclient.PostAsync(uri,content);
return response;
// here as there is no more reference to the _httpclient, the garbage collector will clean
// up the _httpclient and release that instance. Next time the method is called a new
// instance of the _httpclient is created
}
It Works well when you add the request url and the headers at the message, rather than at the client. So better not to assign to BaseAddress Or the header DefaultRequestHeaders if you will use them for many requests.
HttpRequestMessage msg = new HttpRequestMessage {
Method = HttpMethod.Put,
RequestUri = new Uri(url),
Headers = httpRequestHeaders;
};
httpClient.SendAsync(msg);

How to diagnose a 401 error attempting to get an OAuth2 bearer token in c# .NET Core?

I have some limited skills in c++ and have recently moved in C# (asp.net) and azure Web services. As a PoC I'm trying to make REST calls into PayPal (which I'll need to be using professionally in 3 -6 months).
I've set up my personal PayPal account using the instructions here and I get a bearer token back using curl as described in the link. Awesome.
I'm now trying to do this from .NET Core C# and all I get is a 401 error. I've examined the request and it seems the same as the curl in terms of headers; the base64 encoded credentials I think I'm adding are the same as the ones in the verbose curl log (I examined the two base64 strings by eye) so it must be something I'm doing (or not doing) in the set up of the call. I'm looking for suggestions, pointers, or flat out laughter at the obvious mistake I've made.
I've set up what I believe to be a named client thus:
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("PayPal", c =>
{
c.BaseAddress = new Uri("https://api.sandbox.paypal.com/v1/");
c.DefaultRequestHeaders.Add("Accept", "application/json");
c.DefaultRequestHeaders.Add("Accept-Language", "en_US");
});
(with all the other stuff that comes free with VS under it omitted for brevity).
I attempt the call thus:
string clientCredString = CLIENTID + ":" + SECRET;
var clientCreds = System.Text.Encoding.UTF8.GetBytes(clientCredString);
var client = _clientFactory.CreateClient("PayPal");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", System.Convert.ToBase64String(clientCreds));
var messageBody = new Dictionary<string,string > ();
messageBody.Add("grant_type", "client_credientials");
var request = new HttpRequestMessage(HttpMethod.Get, "oauth2/token")
{
Content = new FormUrlEncodedContent(messageBody)
};
string token;
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
token = JsonConvert.DeserializeObject<string>(json);
}
else
{
throw new ApplicationException("Well that failed");
}
and get a 401 code for my trouble.
Suggestions for troubleshooting, better methods of doing this and laughter at my foolishness all welcomed.
Update:
I read the documentation, a couple of items stand out to me:
Requires a verb of post.
Uses FormUrlEncodedContent for client credentials.
Basic auth requires username and password (Client Id & Secret)
I believe the syntax should be:
var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "...");
request.Content = new Dictionary<string, string>() { "grant_type", "client_credentials" };
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", $"{Encoding.UTF8.GetBytes($"{id}:{secret}")}");
HttpResponseMEssage = response = await client.PostAsync(request);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
For the benefit of future readers:
It was, as suggested, an encoding problem. The line:
var clientCreds = System.Text.Encoding.UTF8.GetBytes(clientCredString);
needed to be
var clientCreds = System.Text.Encoding.ASCII.GetBytes(clientCredString);
It should also be noted that this particular operation requires a POST not a GET as I was using, but once I started sending properly encoded requests the errors started to make a lot more sense.

Web API2 proxy doesn't redirect the requests

I have a proxy controller in order to redirect the Ajax requests and pass the same cookies from the current domain to the Web API endpoint, but it doesn't work as what I expected. e.g. the cookies in "https://www.example.com", the Web API URL "https://api.example.com/xyz/abc/". What I am trying to do is sending an Ajax request to
"https://www.example.com/api/proxy/something"
and hoping it to be redirected to
"https://api.example.com/xyz/abc/something" with the same settings (especially the cookies).
Here is the API controller in the web site:
public class ProxyController : ApiController
{
private string _baseUri = "https://api.example.com/xyz/abc/";
[AcceptVerbs(Http.Get, Http.Head, Http.MkCol, Http.Post, Http.Put)]
public async Task<HttpResponseMessage> Proxy()
{
using (HttpClient http = new HttpClient())
{
string proxyURL = this.Request.RequestUri.AbsolutePath;
int indexOfProxy = proxyURL.IndexOf("proxy/") + 6;
_baseUri = _baseUri + proxyURL.Substring(indexOfProxy, proxyURL.Length - indexOfProxy);
this.Request.RequestUri = new Uri(_baseUri);
//For some reason Ajax request sets Content in the Get requests, because
//of that it was denied complaining about "cannot send a content-body"
if (this.Request.Method == HttpMethod.Get)
{
this.Request.Content = null;
}
return await http.SendAsync(this.Request);
}
}
}
It doesn't redirect the requests. In the response, the Requested URL is the same as the original request. The Host in the request header is "www.example.com" instead of "api.example.com', I am going nuts with this issue for the last few days.
HttpClient will work fine. We have done this many times. However, cookies are tricky as they are tied to the domain. Here is some code of a proxy without cookies that should get you started.
[AcceptVerbs(Http.Get, Http.Head, Http.MkCol, Http.Post, Http.Put)]
public async Task<HttpResponseMessage> Proxy()
{
var request = this.Request;
var proxyUri = this.GetProxyUri(request.RequestUri);
request.RequestUri = proxyUri;
request.Headers.Host = proxyUri.Host;
if (request.Method == HttpMethod.Get)
{
request.Content = null;
}
//todo: Clone all cookies with the domain set to the domain of the proxyUri. Remove the old cookies and add the clones.
using (var client = new HttpClient())
{
//default is 60 seconds or so
client.Timeout = TimeSpan.FromMinutes(5);
return await client.SendAsync(request, HttpCompletionOption.ResponseContentRead);
}
}
private string _baseUri = "https://api.example.com/xyz/abc/";
private Uri GetProxyUri(Uri originalUri)
{
var proxyUri = originalUri.AbsolutePath;
var indexOfProxy = proxyUri.IndexOf("proxy/") + 6;
var finalUri = _baseUri + proxyUri.Substring(indexOfProxy, proxyUri.Length - indexOfProxy);
return new Uri(finalUri);
}
You may not be able to get cookies to work properly because of the domain switching. Your client and server will be limited to it's domain. If you own the proxy destination, you may want to change it to allow other mechanisms besides cookies. Can you use headers, or querystring, etc? The domain just may be a killer.
I had exactly this kind of problem like two days ago.
Haven't figured out exactly what was causing it, I'm going to investigate today and let you know.
But until then, you can try using RestSharp, it fixed my problem.
I think it is a problem with the BaseAddress property of the HttpClient, it somehow gets initialized with the initial address and makes the request to that address instead of the proxy one.
I will investigate and let you know.

ASP.Net Web API Request URI Error

I have an ASP.Net Web API that generally works fine. I have a Winforms client application that does GET requests. The client application runs on our corporate network (the API is hosted as an Azure Website). Occasionally, and inconsistently, the HttpClient calls I make add what seem to be corporate URLs in front of my GET call.
Example: I try to call send an HttpClient request to the following URL: 'http://xyzxyz.azurewebsites.net/api/user/1'
but the actual request made is:
'http://usgaabc1iru01/B0000D0000N0001F0000S0000R0004/http://xyzxyz.azurewebsites.net/api/user/1'
This obviously causes an error.
I've asked our IT department what may be happening and they are at a loss. Hoping someone could point me in the right direction.
Edit:
Here's the code I use. First I have a static method I call everything I make a call to the API to get the HttpClient (is this awkward/bad perhaps):
public static HttpClient GetHttpClient()
{
var credentials = new NetworkCredential(GlobalVariables.CurrentUser.UserName, GlobalVariables.CurrentUser.Password);
HttpClientHandler handler = new HttpClientHandler();
handler.UseDefaultCredentials = false;
handler.Credentials = credentials;
HttpClient client = new HttpClient(handler);
client.BaseAddress = new Uri(PublicClasses.GlobalVariables.BaseUriString);
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
GlobalVariables.CredentialedHttpClient = client;
}
return GlobalVariables.CredentialedHttpClient;
}
}
Here's a simple GET call I use:
public static List<Project> GetAllProjects()
{
try
{
HttpClient client = GetHttpClient();
HttpResponseMessage response = client.GetAsync("api/project").Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
var projects = response.Content.ReadAsAsync<IEnumerable<Project>>().Result;
return (List<Project>)projects;
}
else
{
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
return null;
}
}
catch (Exception)
{
throw;
}
}
I don't have an answer for why is this happening but I came across similar issue in a web form (not a win forms client). That is solved by using base meta tag.I am not sure if that solves your problem, but you can give a try.
You can use base address with HttpClient like this (if you are not already doing this):
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://xyzxyz.azurewebsites.net/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("api/user/1");
if (response.IsSuccessStatusCode)
{
//add your code
}
}

Using a Keep-Alive connection in WinRT's HttpClient class?

Our WinRT app is incredibly slow when opening connections to our servers. Requests take ~500ms to run. This is blocking some of our scenarios.
When debugging, we noticed that when Fiddler is active, the requests are much faster - ~100ms per request. Some searches later we understood that was because Fiddler was using Keep-Alive connections when proxying calls, which makes our proxied calls much faster.
We double-checked this in two ways.
We set UseProxy to false and observed that the request went back to being slow.
We turned off Fiddler's "reuse connections" option and observed that the requests went back to being slow.
We tried enabling keep-alive through the Connection header (.Connection.Add("Keep-Alive")) but this does not seem to have any effect - in fact, the header seems to be blatantly ignored by the .NET component and is not being sent on the request (again, by inspecting thru Fiddler).
Does anyone know how to set keep-alive on requests in Windows 8, WinRT, HttpClient class?
The following sets the correct headers to turn on keep-alive for me (client is an HttpClient)
client.DefaultRequestHeaders.Connection.Clear();
client.DefaultRequestHeaders.ConnectionClose = false;
// The next line isn't needed in HTTP/1.1
client.DefaultRequestHeaders.Connection.Add("Keep-Alive");
If you want to turn keep-alive off, use
client.DefaultRequestHeaders.Connection.Clear();
client.DefaultRequestHeaders.ConnectionClose = true;
Try using the HttpContent class to add the headers - something like this based on (but untested) http://social.msdn.microsoft.com/Forums/en-CA/winappswithcsharp/thread/ce2563d1-cd96-4380-ad41-6b0257164130
Behind the scenes HttpClient uses HttpWebRequest which would give you direct access to KeepAlive but since you are going through HttpClient you can't directly access that property on the HttpWebRequest class.
public static async Task KeepAliveRequest()
{
var handler = new HttpClientHandler();
var client = new HttpClient(handler as HttpMessageHandler);
HttpContent content = new StringContent(post data here if doing a post);
content.Headers.Add("Keep-Alive", "true");
//choose your type depending what you are sending to the server
content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
HttpResponseMessage response = await client.PostAsync(url, content);
Stream stream = await response.Content.ReadAsStreamAsync();
return new StreamReader(stream).ReadToEnd();
}
EDIT
Since you only want GET, you can do that with:
public static async Task KeepAliveRequest(string url)
{
var client = new HttpClient();
var request = new HttpRequestMessage()
{
RequestUri = new Uri("http://www.bing.com"),
Method = HttpMethod.Get,
};
request.Headers.Add("Connection", new string[] { "Keep-Alive" });
var responseMessage = await client.SendAsync(request);
return await responseMessage.Content.ReadAsStringAsync();
}

Categories