Sharepoint REST set metadata after document upload - c#

I have a C# console application (which will ultimately be a windows service when it's finished). The purpose is to migrate a document store (500k+ documents) over to SharePoint 2013.
Note, this is a standalone C# app that will run on a server that does not have SharePoint installed. I am using pure REST via HttpClient calls.
Uploading a document and setting its metadata has 3 steps:
Upload the document (POST)
e.g. baseURL + "web/" + "GetFolderByServerRelativeUrl('/sites" + libPath + "')/Files/Add(url='" + filename + "',overwrite=true)";
Retrieve details for the list item associated with this document (GET)
e.g. baseURL + "web/lists/getbytitle('" + docLibName + "')/items?$filter=Title eq '" + filenameNoExt + "'"
These first 2 steps are working fine.
Update the metadata
This is the bit that's causing me sleepless nights now. I can't seem to figure out what exactly I should be sending to the web service, and anything I try just gives errors. I can't find any examples of anyone who's tried to do this; and the MS documentation is very sparse.
The MS documentation from http://msdn.microsoft.com/en-us/library/office/dn292552%28v=office.15%29.aspx says:
The following example shows how to update a list by using the MERGE method.
url: http: //siteurl/_api/web/lists(guid'list GUID')
method: POST
body: { '__metadata': { 'type': 'SP.List' }, 'Title': 'New title' }
Headers:
Authorization: "Bearer " + accessToken
X-RequestDigest: form digest value
IF-MATCH": etag or "*"
X-HTTP-Method: MERGE,
accept: "application/json;odata=verbose"
content-type: "application/json;odata=verbose"
content-length:length of post body
In my application, I have set the type in the query to SP.Data.CertificatesItem, which is what was returned in step 2; and I'm trying to set the Certificate Type field to the specified value, rather than the document title:
string body = "{ '__metadata': { 'type': '" + spItemType + "' }, 'CertificateType': 'Medical Certificate'}";
(there are several fields which need to be set; I am just hard-coding one field here for testing purposes. The fields which need to be set depend on the content type of the document library.)
Additionally, the URL is slightly different. Where the example had:
http: //siteurl/_api/web/lists(guid'list GUID')
I have (specific example, slightly anonymized):
http: //siteurl/_api/Web/Lists(guid'cdcbef76-8bc0-4a68-9279-f6f3b6cbd3c3')/Items(2)
Otherwise, my call is identical to the example above.
I am getting the error message:
{"error":{"code":"-1, Microsoft.SharePoint.Client.InvalidClientQueryException","message":{"lang":"en-US","value":"The parameter __metadata does not exist in method GetById."}}}
I don't understand what I'm doing wrong! And I can't find any concrete examples anywhere.
From the page: http://msdn.microsoft.com/en-us/library/office/dn292553%28v=office.15%29.aspx :
If you want to update a file's metadata, you'll have to construct an endpoint that reaches the file as a list item. You can do this because each folder is also a list, and each file is also a list item. Construct an endpoint that looks like this: https: //siteurl/_api/web/lists/getbytitle('Documents')/items(). Working with lists and list items with REST explains how to update a list item's metadata.
Can anyone provide any insight here?
UPDATE:
Seems the X-HTTP-Method wasn't getting set correctly. I fixed that, now I'm getting:
{"error":{"code":"-1, Microsoft.SharePoint.Client.InvalidClientQueryException","message":{"lang":"en-US","value":"An unexpected 'PrimitiveValue' node was found when reading from the JSON reader. A 'StartObject' node was expected."}}}

I came across your question while looking into this myself. I have documented how to do file uploads, creating, updating and retrieving list items including those with Termstore data all in .NET using the HttpClient with no JQuery required.
Example for updating a file from the HttpClient using REST:
client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
client.BaseAddress = new System.Uri(url);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("X-RequestDigest", digest);
client.DefaultRequestHeaders.Add("X-HTTP-Method", "MERGE");
client.DefaultRequestHeaders.Add("IF-MATCH", "*");
HttpContent strContent = new StringContent(String.Concat("{ '__metadata': { 'type': 'SP.List' }, 'Title': '", filename, "' }"));
strContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
strContent.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("odata", "verbose"));
HttpResponseMessage updateResponse = await client.PostAsync(String.Concat("_api/web/lists/GetByTitle('Project Photos')/Items(", id, ")"), strContent);
updateResponse.EnsureSuccessStatusCode();
if (updateResponse.IsSuccessStatusCode)
{}
https://arcandotnet.wordpress.com/2015/04/01/sharepoint-2013-rest-services-using-c-and-the-httpclient-for-windows-store-apps/
Hopefully this helps.
Arcan.NET

Related

Error 400: Bad Request while posting through LinkedIn Share API

I have checked a bunch of questions on StackOverflow which may be termed as related but most of them are either too old or too specific, and doesn't help me much.
While working on an application that helps schedule posts on LinkedIn, I follow below process:
Allow user to Sign-up using their LinkedIn account.
After they submit for Sign-up, I store Access Token, Refresh Token and relevant TTL along with the user details.
When they schedule a post at a specific time, I have an Azure function (serverless) that checks for any post to submit every minute, and supposed to post on user's behalf.
Using the Access Tokenthat I saved earlier, I hit https://api.linkedin.com/v2/me to get the person:id of the user. Everything works as expected until here and gets the person:id using GetPersonID()
See my code below for posting using API:
string pid = GetPersonID();
sstring PostCode = #"{""content"":{""contentEntities"":[{""entityLocation"":""" + URL + #""",""thumbnails"":[{""imageSpecificContent"":{},""resolvedUrl"":""" + OriginalURL + #"""}]}],""description"":"" + URL + "",""title"":""" + Title + #"""},""distribution"":{""linkedInDistributionTarget"":{}},""owner"":""urn:li:person:" + pid + #""",""subject"":""" + Title + #""",""text"":{""text"":""" + Description + #"""}}";
string outJ = JsonConvert.SerializeObject(PostCode);
WebClient clientx = new WebClient();
clientx.Headers.Add("Authorization", "Bearer " + access_token);
clientx.Headers.Add("X-Restli-Protocol-Version", "2.0.0");
clientx.Headers.Add("x-li-format", "json");
clientx.Headers["Content-Type"] = "application/json";
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
string LinkedInAccessTokenURL = "https://api.linkedin.com/v2/shares";
string result = clientx.UploadString(LinkedInAccessTokenURL, "POST", data: outJ);
A Sample Json Post
{"content":{"contentEntities":[{"entityLocation":"http://www.test.com","thumbnails":[{"imageSpecificContent":{},"resolvedUrl":"https://picsum.photos/200/300"}]}],"description":" + URL + ","title":"This is a test post"},"distribution":{"linkedInDistributionTarget":{}},"owner":"urn:li:person:<person:id>","subject":"This is a test post for LinkedIn API","text":{"text":"ashdjahs dadjh asdjahs da\nahs djashdjkashdas\nahsd jasdhkjasdh "}}
Everything works until the last line of the above code. Upon POST the LinkedIn API throws an error System.Net.WebException: 'The remote server returned an error: (400) Bad Request.' (LinkedIn documentation reference). I've even tried posting the value in PostCode using Postman and it works perfectly fine, which tells me that the JSON generated it correct.
But despite multiple tries and fixes, nothing seems to work for actual Post.
Exception thrown:
Data: {System.Collections.ListDictionaryInternal}
HResult: -2146233079
HelpLink: null
InnerException: null
Message: "The remote server returned an error: (400) Bad Request."
Response: {System.Net.HttpWebResponse}
Source: "System.Net.Requests"
StackTrace: " at System.Net.HttpWebRequest.GetResponse()\r\n at System.Net.WebClient.GetWebResponse(WebRequest request)\r\n at System.Net.WebClient.DownloadBits(WebRequest request, Stream writeStream)\r\n at System.Net.WebClient.UploadBits(WebRequest request, Stream readStream, Byte[] buffer, Int32 chunkSize, Byte[] header, Byte[] footer)\r\n at System.Net.WebClient.UploadDataInternal(Uri address, String method, Byte[] data, WebRequest& request)\r\n at System.Net.WebClient.UploadString(Uri address, String method, String data)\r\n at System.Net.WebClient.UploadString(String address, String method, String data)"
Status: ProtocolError
TargetSite: {System.Net.WebResponse GetResponse()}
Marking as resolved.
The issue was apparently with the Json being generated. Probably it was encoding, but I'm still confused on how Postman was able to post it without any issues.
I created relevant class, and then using its object, I serialized it into Json using Newtonsoft. Which after passing to the WebClient object rendered the correct results.
Lessons learnt!
I know manually creating a JSON is never a good idea (what I actually did earlier, to save time), but at times being lazy can make you work harder than you would have in the first place. So if we're planning to do an API call with JSON as payload, its never a waste of time to create a relevant class and then serialize to Json.

How to "create the video" after file uploaded to DailyMotion using c#

Im following the instructions from here to publish a new video on DailyMotion, using c# and a WebClient.
i successfully got the auth-token, then an upload url, then the actual file to upload. im stuck at step 4, called: "create the video"
it states to POST url=<the url i got from previous step> to https://api.dailymotion.com/me/videos (with the Authorization token in the header), but all my attempts result in "bad request" - without further explanation.
any ideas?
using (var client = new WebClient())
{
var createRequest = $"url={videoUpload.url}";
client.Headers.Add("Authorization", $"Bearer {authToken.access_token}");
client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
var createVideo = client.UploadString("https://api.dailymotion.com/me/videos", "POST", createRequest);
}
also tried:
var createRequest = $"url={HttpUtility.UrlEncode(videoUpload.url)}";
I tried your code and my video was created successfully. As explained in our documentation a 400 error is related to a missing/invalid parameter.
I assume you are trying to send the upload url (returned in step 2) instead of the url returned by step 3 (url of your uploaded file).
You can find an article (with examples of returned values) which use a simplified way to upload on Dailymotion here.

Download Autodesk Forge viewables using C#

I'am trying to download the Forge viewables in a C# application, but I'm having trouble downloading the svf file. The following code returns "The requested resource does not exist." If I run the same urn in the node.js extractor example it works fine. I'm not that familiar with using REST api's in C# and is probably missing a header or parameter. I have tried lots of combinations without success. Hopefully someone can point me in the right direction.
var urn = "urn:adsk.viewing:fs.file:<xxxxxURNxxxxxx>/output/0/0.svf";
var client = new RestClient("https://developer.api.autodesk.com");
var request = RestRequest("/derivativeservice/v2/derivatives/{" + urn + "}", Method.GET);
request.AddParameter("Authorization",string.Format("Bearer " + Configuration.Default.AccessToken),ParameterType.HttpHeader);
var result = client.Execute(request);
I think you just need to remove the curly braces from the URL, so use RestRequest("/derivativeservice/v2/derivatives/" + urn, Method.GET);

Incorrect Format for JSON deserialization

I am very much out of my element on this. In C# I am writing a method to get data back from a website using REST. Per the documentation on the website I should use something like this:
var client = new RestClient(url + "token");
var request = new RestRequest(Method.POST);
request.AddParameter("application/x-www-form-urlencoded",
"grant_type=password&username=" + UserName +
"&password=" + Password +
"&tenant=" + Company,
ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
to get a response that looks like this:
{
"access_token": "generated_token_value",
"token_type": "bearer",
"expires_in": 2591999
}
However, I have no earthly clue to how to read that info. I'm assuming that the JSON response is in my "response" variable, but beyond that I'm at a loss. I've done a little digging and have found Json.NET should be helpful, but it's over my head. Their documentation suggests:
Product deserializedProduct = JsonConvert.DeserializeObject<Product>(output);
However if I convert that into something that looks correct for mine (creating a "Responses" Class and then):
Responses responses = JsonConvert.DeserializeObject<Responses>(response);
I get an error in VS under the "response" saying "cannot convert from 'RestSharp.IRestResponse' to 'string'.
I feel like I just need a little nudge to get over this hump.
If you go to the repository of RestSharp you'll see that it has special property called Content which contains JSon in a string format.
Now you can use JsonConvert.DeserializeObject<Responses>(response.Content); to retrieve your object.
Responses responses = JsonConvert.DeserializeObject<Responses>(response.Content);
Another option could be declaring the response for the out type you're expecting
IRestResponse<Responses> response = client.Execute<Responses>(request);

HttpRequest.UrlReferrer does not appear to exist

I have a web application which uses MVC4, but now realised it really should be a Web API and as such I'm moving over to a ASP.NET 4.5.2 web API 2 project.
The problem is, where I was using Request.ServerVariables[] in my MVC project I have been advised that it's not the 'correct' way in a Web API, as such, I should be using Request.RequestUrl
The example on the MSDN is simply
Uri MyUrl = Request.UrlReferrer; //kaboom for me
Response.Write("Referrer URL Port: " + Server.HtmlEncode(MyUrl.Port.ToString()) + "<br>");
Response.Write("Referrer URL Protocol: " + Server.HtmlEncode(MyUrl.Scheme) + "<br>");
The issue is, despite referencing System.Web there is no property for Request are UrlReferrer - the only properties are Content, Headers, Method, Properties, RequestUri and Version.
Why can't I use Request.UrlReferrer as per the MSDN example?
I suspect you just need to get the current request in a different way, due to being in a static method rather than in a context where you can refer to a Request instance property:
var request = HttpContext.Current;
var referrer = request.UrlReferrer;
...

Categories