Reputation: 1964
This is a follow-up question to one of my earlier question in Stackoverflow.
I am trying to connect to Sharepoint Online Premise to get documents in the "Document Library" by using the HTTPClient and the Sharepoint 2013 REST API.
All i am doing here is a Simple Anonymous HTTP GET call using HttpClient.
The code is as follows :
System.Net.Http.HttpClient _Client = new System.Net.Http.HttpClient();
_Client.BaseAddress = new Uri("https://test.sharepoint.com/_vti_bin/ListData.svc");
_Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/atom+xml"));
_Client.DefaultRequestHeaders.Add("auth_user", "[email protected]");
_Client.DefaultRequestHeaders.Add("auth_pass", "test");
_Client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
HttpResponseMessage resp = _Client.GetAsync(new Uri("https://test.sharepoint.com/_vti_bin/ListData.svc/Documents()")).Result;
string respString = resp.Content.ReadAsStringAsync().Result;
And i am getting the following error:
Unable to read data from the transport connection: The connection was closed.
Stack Trace is as follows :
[IOException: Unable to read data from the transport connection: The connection was closed.]
System.Net.ConnectStream.EndRead(IAsyncResult asyncResult) +6501654
System.Net.Http.WebExceptionWrapperStream.EndRead(IAsyncResult asyncResult) +30
System.Net.Http.StreamToStreamCopy.BufferReadCallback(IAsyncResult ar) +54
[HttpRequestException: Error while copying content to a stream.]
[AggregateException: One or more errors occurred.]
System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) +3569193
System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification) +73
System.Threading.Tasks.Task`1.get_Result() +10522673
WebApp.Test.Page_Load(Object sender, EventArgs e) in @Location
System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +51
System.Web.UI.Control.OnLoad(EventArgs e) +92
System.Web.UI.Control.LoadRecursive() +54
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +772
I have gone through various blogs/articles to get some idea to solve the issue.But i cudn't find any solution yet.
Any thoughts on this?
Upvotes: 3
Views: 9679
Reputation: 59368
The way you are trying to pass credentials is not supported in SharePoint Online.
Microsoft released SharePointOnline Client Components SDK that contains SharePointOnlineCredentials class that provides credentials to access SharePoint Online resources.
The following example demonstrates how leverage SharePointOnlineCredentials class with HttpClient class in order to access SharePoint Online:
public class SPHttpClientHandler : HttpClientHandler
{
public SPHttpClientHandler(Uri webUri, string userName, string password)
{
CookieContainer = GetAuthCookies(webUri, userName, password);
FormatType = FormatType.Json;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
if(FormatType == FormatType.Json)
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return base.SendAsync(request, cancellationToken);
}
/// <summary>
/// Retrieve SPO Auth Cookies
/// </summary>
/// <param name="webUri"></param>
/// <param name="userName"></param>
/// <param name="password"></param>
/// <returns></returns>
private static CookieContainer GetAuthCookies(Uri webUri, string userName, string password)
{
var securePassword = new SecureString();
foreach (var c in password) { securePassword.AppendChar(c); }
var credentials = new SharePointOnlineCredentials(userName, securePassword);
var authCookie = credentials.GetAuthenticationCookie(webUri);
var cookieContainer = new CookieContainer();
cookieContainer.SetCookies(webUri, authCookie);
return cookieContainer;
}
public FormatType FormatType { get; set; }
}
public enum FormatType
{
Json,
Xml
}
Prerequisites: SharePointOnlineCredentials class from SharePoint Online Client Components SDK is used for authentication.
Usage
var handler = new SPHttpClientHandler(webUri, userName, password);
using (var client = new HttpClient(handler))
{
client.BaseAddress = webUri;
var result = client.GetAsync("/_vti_bin/ListData.svc/Documents").Result;
var content = result.Content.ReadAsStringAsync().Result;
}
Upvotes: 0
Reputation: 56
I put together a quick .NET 4.5 console app in Visual Studio 2013 and was able to get your code working with a few minor changes and am able to successfully authenticate and call SharePoint Online's REST API.
Change #1: I added project references to the two SharePoint 2013 Client-Side Object Model (CSOM) DLLs. You can install the DLLs from here: http://www.microsoft.com/en-us/download/details.aspx?id=35585. Run the installer and add project references to Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll. The reason you need those is so you can use the SharePointOnlineCredentials class.
Change #2: I added the "await" keyword to your two async calls because the code wasn't actually waiting for those calls to execute.
Change #3: I modified the HttpClient code to (1) use credentials from the SharePointOnlineCredentials object, (2) Get and use an authentication cookie from SharePoint Online, and (3) add one additional request header that's needed.
Here's the complete code snippet:
Uri uri = new Uri("https://mydomain.sharepoint.com");
SharePointOnlineCredentials creds = new SharePointOnlineCredentials(
"[email protected]", StringUtilities.ToSecureString("YourPassword"));
string authCookie = creds.GetAuthenticationCookie(uri);
var cookies = new CookieContainer();
cookies.Add(new Cookie("FedAuth", authCookie.TrimStart("SPOIDCRL=".ToCharArray()), "", uri.Authority));
System.Net.Http.HttpClientHandler handler = new System.Net.Http.HttpClientHandler()
{
Credentials = creds,
CookieContainer = cookies
};
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient(handler);
client.BaseAddress = new Uri(uri, "/_vti_bin/ListData.svc");
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/atom+xml"));
client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
client.DefaultRequestHeaders.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
var response = await client.GetAsync(new Uri(uri, "/_vti_bin/ListData.svc/Documents()"));
string text = await response.Content.ReadAsStringAsync();
I ran this and printed out the "text" variable at the end and got a valid response from SharePoint Online.
Upvotes: 1
Reputation: 11
This will help you:
ServicePointManager.ServerCertificateValidationCallback =
delegate(object s, X509Certificate certificate, X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
return true;
};
Put it before request
.
Upvotes: 1