How to retrieve image files from Sharepoint Image Library (List) through REST API using C# in Windows Store App - rest

I am building a Windows Store App where I have to upload and download images from a SharePoint 2013 site. I tried the REST API but all I am getting is XML data containing the properties of the file but I am not able to get the actual file. Can someone please help me with this?
Following is the code that I am trying with:
HttpClientHandler handler = new HttpClientHandler();
handler.UseDefaultCredentials = false;
handler.Credentials = new NetworkCredential(userName, password, domain);
HttpClient client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("Accept", "application/atom+xml");
client.DefaultRequestHeaders.Add("ContentType", "application/atom+xml;type=entry");
var response = await client.GetAsync("server/site/_api/web/ListName/Items(1)/File");
Byte[] bArray = await response.Content.ReadAsByteArrayAsync();

The following example demonstrates how to retrieve a specific file content from library:
url: http://site url/_api/web/lists/getbytitle('<list title>')/items(<item id>)/File/$value
method: GET
headers:
Authorization: "Bearer " + accessToken
Example
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var response = client.GetAsync("https://tenant.sharepoint.com/_api/web/lists/getbytitle('Documents')/items(1)/File/$value", HttpCompletionOption.ResponseHeadersRead).Result;
var fileContent = response.Content.ReadAsByteArrayAsync().Result;
References
Working with folders and files with REST

Related

How to pass a file through multiple api in .net

I'm trying to download a file from Sharepoint using a REST API. Because my app is written in .Net Core, and the CSOM library doesn't support it, I've made a "sharepoint proxy" in .Net Framework, which is a single app hosted on Azure.
Now I have a problem, while trying to download a file. I send a request from Postman to my app in .Net Core, which send another request to the sharepoint proxy, which (at last) send a GET request to Sharepoint REST API. In result, I become in Sharepoint proxy a Stream from sharepoint REST API, which I try to forward back to my app. I have no idea, which format should I use to send the file. I tried WebStream, FileStream and byte[], but in each case I got an unreadable file.
Download method in .Net Core App
public async Task<Stream> DownloadFile(SharePointFileUrl spInfo)
{
var restUrl = $"{siteUrl}/downloadFile";
using (var httpClient = new HttpClient())
{
var content = new StringContent(JsonConvert.SerializeObject(spInfo), Encoding.UTF8, "application/json");
var webResponse = await httpClient.PostAsync(restUrl, content);
return await webResponse.Content.ReadAsStreamAsync();
}
}
Endpoint in Sharepoint proxy
public byte[] DownloadFile([FromBody] SharePointFileUrl fileInfo)
{
return _spService.DownloadFile(fileInfo.FileUrl);
}
Download method in Sharepoint proxy
public byte[] DownloadFile(string url)
{
var restUrl = $"{_siteUrl}/_api/web/GetFileByServerRelativeUrl('/{url}')/$value";
var request = CreateBaseRequest("GET", restUrl);
request.Headers.Add("X-RequestDigest", _formDigest);
WebResponse fileResponse = request.GetResponse();
var input = fileResponse.GetResponseStream();
using (MemoryStream ms = new MemoryStream())
{
input.CopyTo(ms);
return ms.ToArray();
}
}
Thank you in advance for any help. Of course, I've googled my problem, but without result.
In your Core app try this.
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(new Uri("<file url string>"));
return File(req.GetResponse().GetResponseStream(), "<Content type>", "<download file name>");

Proper way to do a PATCH request

Im using ASP.Net Core 2. I need to update a user in a Azure AD with Microsofts Graph API. The API documentation states that i should send the properties of the user in the body and specify the user in the URL like so:
https://graph.windows.net/myorganization/users/{user_id}?api-version
The documentation states that it should be a PATCH request. But HTTPRequestMessage does not accept PATCH as a HttpMethod. What is the proper way to make a PATCH request with asp.net core 2?
When i google i find that all answers suggests using JsonPatch, but that is a format that is not supported by Microsoft Graph API.
This is what i have so far....
var client = new HttpClient();
var requestUri = $"{_azureAdOptions.GraphInstance}/{_azureAdOptions.GraphVersion}/{_azureAdOptions.Domain}/users/me";
var request = new HttpRequestMessage(HttpMethod., requestUri);
var accessToken = await _authenticationHelper.GetAccessTokenAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var response = await client.SendAsync(request);
var responseString = await response.Content.ReadAsStringAsync();
Given the nature of Http, and the craziness of developers, you can create your own http server with custom methods. The class HttpMethod was created with this in mind allowing you to specify the method as string:
var method = new HttpMethod("PATCH"); // Patch
var request = new HttpRequestMessage(method , requestUri); // Use patch
Note: The new version of HttpClient comes with Patch method by default.

How to call SSRS Rest-Api V1.0 with custom security implemented (NOT SOAP)

I have implemented the custom security on my reporting services 2016 and it displays the login page once the URL for reporting services is typed on browser URL bar (either reports or reportserver)
I am using the following code to pass the Credentials
when i use the code WITHOUT my security extension it works and looks like this
ICredentials _executionCredentials;
CredentialCache myCache = new CredentialCache();
Uri reportServerUri = new Uri(ReportServerUrl);
myCache.Add(new Uri(reportServerUri.GetLeftPart(UriPartial.Authority)),
"NTLM", new NetworkCredential(MyUserName, MyUserPassword));
_executionCredentials = myCache;
when i use the code WITH the security extension it doesnt work and looks like this
ICredentials _executionCredentials;
CredentialCache myCache = new CredentialCache();
Uri reportServerUri = new Uri(ReportServerUrl);
myCache.Add(new Uri(reportServerUri.GetLeftPart(UriPartial.Authority)),
"Basic", new NetworkCredential(MyUserName, MyUserPassword));
_executionCredentials = myCache;
and i get an Exception saying "The response to this POST request did not contain a 'location' header. That is not supported by this client." when i actually use this credentials
Is "basic" the wrong option ?
Have anyone done this ?
Update 1
Well it turns out that my SSRS is expecting an Authorisation cookie
which i am unable to pass (according to fiddler, there is no cookie)
HttpWebRequest request;
request = (HttpWebRequest)HttpWebRequest.Create("http://mylocalcomputerwithRS/Reports_SQL2016/api/v1.0");
CookieContainer cookieJar = new CookieContainer();
request.CookieContainer = cookieJar;
Cookie authCookie = new Cookie("sqlAuthCookie", "username:password");
authCookie.Domain = ".mydomain.mylocalcomputerwithRS";
if (authCookie != null)
request.CookieContainer.Add(authCookie);
request.Timeout = -1;
HttpWebResponse myHttpWebResponse = (HttpWebResponse)request.GetResponse();
That's how I got it (SSRS 2017; api v2.0). I took the value for the "body" from Fiddler:
var handler = new HttpClientHandler();
var httpClient = new HttpClient(handler);
Assert.AreEqual(0, handler.CookieContainer.Count);
// Create a login form
var body = new Dictionary<string, string>()
{
{"__VIEWSTATE", "9cZYKBmLKR3EbLhJvaf1JI7LZ4cc0244Hpcpzt/2MsDy+ccwNaw9hswvzwepb4InPxvrgR0FJ/TpZWbLZGNEIuD/dmmqy0qXNm5/6VMn9eV+SBbdAhSupsEhmbuTTrg7sjtRig==" },
{"__VIEWSTATEGENERATOR", "480DEEB3"},
{ "__EVENTVALIDATION", "IS0IRlkvSTMCa7SfuB/lrh9f5TpFSB2wpqBZGzpoT/aKGsI5zSjooNO9QvxIh+QIvcbPFDOqTD7R0VDOH8CWkX4T4Fs29e6IL92qPik3euu5QpidxJB14t/WSqBywIMEWXy6lfVTsTWAkkMJRX8DX7OwIhSWZAEbWZUyJRSpXZK5k74jl4x85OZJ19hyfE9qwatskQ=="},
{"txtUserName", "User"},
{"txtPassword", "1"},
{"btnLogin","Войти"}
};
var content = new FormUrlEncodedContent(body);
// POST to login form
var response = await httpClient.PostAsync("http://127.0.0.1:777/ReportServer/Logon.aspx", content);
// Check the cookies created by server
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
var cookies = handler.CookieContainer.GetCookies(new Uri("http://127.0.0.1:777/ReportServer"));
Assert.AreEqual("sqlAuthCookie", cookies[0].Name);
// Make new request to secured resource
var myresponse = await httpClient.GetAsync("http://127.0.0.1:777/Reports/api/v2.0/Folders");
var stringContent = await myresponse.Content.ReadAsStringAsync();
Console.Write(stringContent);
As an alternative you can customize SSRS Custom Security Sample quite a bit.
I forked Microsoft's Custom Security Sample to do just what you are describing (needed the functionality at a client long ago and reimplemented as a shareable project on GitHub).
https://github.com/sonrai-LLC/ExtRSAuth
I created a YouTube walkthrough as well to show how one can extend and debug SSRS security with this ExtRSAuth SSRS security assembly https://www.youtube.com/watch?v=tnsWChwW7lA
TL; DR; just bypass the Microsoft example auth check in Login.aspx.cs and put your auth in Page_Load() or Page_Init() event of Login.aspx.cs- wherever you want to perform some custom logging check- and then immediately redirect auth'd user to their requested URI.

Enhanced Security Error while Visual Studio Team Services Rest API

I'm currently trying to use the Rest APIs exposed by Visual Studio Team Services (was Visual Studio Online) to obtain work item information. I seem to be able to connect however when I look at the response to my query its a html page with a Enhanced Security Error message. I believe that this is due to the Enhanced Security option in IE but I'm calling this from my client machine and I can only see options on how to turn this off on a server.
this is the call i'm making
using (var client = new HttpClient())
{
var token = "xxxxxxxxxxxx";
var apiVersion = "1.0";
var account = "xxxxxxxx";
var query = "Select [System.Id] From WorkItems Where[System.WorkItemType] = 'WorkItem' order by [System.CreatedDate] desc";
var url = "https://" + account + ".visualstudio.com/Core/_apis/wit/";
// Execute a query that returns work item IDs matching the specified criteria
using (var request = new HttpRequestMessage(HttpMethod.Post, url + "wiql"))
{
request.Headers.Add("Authorization", "Bearer " + token);
request.Headers.Add("Accept", "application/json;api-version=" + apiVersion);
Dictionary<string, string> body = new Dictionary<string, string>
{
{
"query", query
}
};
request.Content = new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json");
using (var response = await client.SendAsync(request))
{
var content = await response.Content.ReadAsStringAsync();
var workItems = JObject.Parse(content)["workItems"] as JArray;
string[] ids = workItems.Select<JToken, string>(w => (w["id"] + "")).Take(10).ToArray<string>();
string idsString = String.Join(",", ids);
// Get details for the last 10
using (var detailsRequest = new HttpRequestMessage(HttpMethod.Get, url + "workitems?ids=" + idsString + "&fields=System.Id,System.Title"))
{
detailsRequest.Headers.Add("Authorization", "Bearer " + token);
detailsRequest.Headers.Add("Accept", "application/json;api-version=" + apiVersion);
using (var detailsResponse = await client.SendAsync(detailsRequest))
{
var detailsContent = await detailsResponse.Content.ReadAsStringAsync();
var detailsWorkItems = JObject.Parse(detailsContent)["value"] as JArray;
foreach (dynamic workItem in detailsWorkItems)
{
Console.WriteLine("Work item: {0} ({1})",
workItem.fields["System.Id"],
workItem.fields["System.Title"]
);
}
}
}
}
}
}
any help with this would be appreciated,
thanks
Chris
You can add related sites to trusted sites (for example: https://app.vssps.visualstudio.com, https://login.live.com etc…).
Internet option=>Security
Select Trusted sites
Click sites
Type website address and click add
The simple way to know which URLs need to be added, you could send a simple Get Rest request (e.g. get work item REST API), it will pop up a window that contains site URL (will pop up many times for different URL), add these URL to trusted sites list.
Update:
Based on the response result, it isn’t related to enhanced security, the result means it isn’t authenticated. So the token is invalid, it is access token of OAuth, you need to get access token after register your app to VSTS.
More information, you can refer to this article.
There is a OAuth sample that you can refer. After you get access token, add it to request header and retrieve data from VSTS.
If you want to access VSTS through personal access token, the code like this: (check this article)
try
{
var username = "username";
var password = "password";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", username, password))));
using (HttpResponseMessage response = client.GetAsync(
"https://{account}.visualstudio.com/DefaultCollection/_apis/build/builds").Result)
{
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}

Azure REST WebClient PUT Blob

I'm trying to simply upload a new blob to an Azure Storage countainer using WebClient like this :
var sas = "[a new generated sas with Read, Write, List & Delete permissions]";
var sData = "This is a test!";
var sEndPoint = "http://myaccount.blob.core.windows.net/mycontainer/MyTest.txt" + sas;
var clt = new WebClient();
var res = await clt.UploadStringTaskAsync(sEndPoint, "PUT", sData);
This is giving me a "(400) Bad Request." error. Am I doing anything wrong here?
Thanks
(By the way, I need to use REST instead of Client API since I'm in a Silverlight project)
You would need to define a request header (x-ms-blob-type) for blob type and set it's value to BlockBlob. Also for Put requests you would need to define the Content-Length request header as well. I wrote a blog post on Shared Access Signatures and performing some blob operations using that (with both REST API and Storage Client library) which you can read here: http://gauravmantri.com/2013/02/13/revisiting-windows-azure-shared-access-signature/.
and here's the code from that post on uploading blob. It uses HttpWebRequest/HttpWebResponse instead of WebClient:
static void UploadBlobWithRestAPISasPermissionOnBlobContainer(string blobContainerSasUri)
{
string blobName = "sample.txt";
string sampleContent = "This is sample text.";
int contentLength = Encoding.UTF8.GetByteCount(sampleContent);
string queryString = (new Uri(blobContainerSasUri)).Query;
string blobContainerUri = blobContainerSasUri.Substring(0, blobContainerSasUri.Length - queryString.Length);
string requestUri = string.Format(CultureInfo.InvariantCulture, "{0}/{1}{2}", blobContainerUri, blobName, queryString);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri);
request.Method = "PUT";
request.Headers.Add("x-ms-blob-type", "BlockBlob");
request.ContentLength = contentLength;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(Encoding.UTF8.GetBytes(sampleContent), 0, contentLength);
}
using (HttpWebResponse resp = (HttpWebResponse)request.GetResponse())
{
}
}
When testing against the blob emulator this is the code I need to get it working:
var connection = ConfigurationManager.AppSettings["AzureStorageConnectionString"];
var storageAccount = CloudStorageAccount.Parse(connection);
var client = new WebClient();
client.Headers.Add("x-ms-blob-type", "BlockBlob");
client.Headers.Add("x-ms-version", "2012-02-12");
client.UploadData(string.Format(#"{0}/$root/{1}{2}", storageAccount.BlobEndpoint, myFileName, sharedAccessSignature), "PUT", _content);