Error 400: Bad Request while posting through LinkedIn Share API - c#

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.

Related

Unknown 405 error on C# code + Fine tuning

I started learning C# recently, and found a practical application that isn't too difficult to accomplish, but I need some help figuring out my link I'm pretty sure. Let me show you.
using System;
using System.Net;
using System.Text;
... Namespace class etc etc
Console.WriteLine("Trying...");
WebClient client = new WebClient();
var nameValue = new System.Collections.Specialized.NameValueCollection();
nameValue.Add("FIRST NAME", "myname");
nameValue.Add("LAST NAME", "mylname");
nameValue.Add("GRADE", "mygrade");
Uri uri = new Uri("https://docs.google.com/forms/d/e/1FAIpQLScB206NtDOPl4_lQfCk9SZwqKfVRARVm0dSLujSj4qkWulp3Q/viewform");
byte[] response = client.UploadValues(uri, "POST", nameValue);
string result = Encoding.UTF8.GetString(response);
The linked form is one I created to model my schools. It's an attendance form (HS senior!) and I'm just trying to get it from my email and auto fill it every morning (lol), but I'm focused on this section first. My code generates a 405:
Unhandled exception. System.Net.WebException: The remote server returned an error: (405) Method Not Allowed.
at System.Net.HttpWebRequest.GetResponse()
at System.Net.WebClient.GetWebResponse(WebRequest request)
at System.Net.WebClient.DownloadBits(WebRequest request, Stream writeStream)
at System.Net.WebClient.UploadBits(WebRequest request, Stream readStream, Byte[] buffer, Int32 chunkSize, Byte[] header, Byte[] footer)
at System.Net.WebClient.UploadValues(Uri address, String method, NameValueCollection data)
at AttendanceAutomaticac.Program.Main(String[] args) in C:\Users\Lee\source\repos\AttendanceAutomaticac\Program.cs:line 18
Edit: Can I use any apis for this that someone knows of?
Bad link probably, but it works? I found this code via a link to google on a forum, and modified it slightly to fit my form. I'm also not quite sure whether or not the 3rd value will work for a single choice answer... So, I just need any other solution / explanation, I'm not too great at debugging and problem solving errors beyond StackOverflow in C# yet as I have minimal experience with it.

Sharepoint REST set metadata after document upload

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

c# httpclient POST request: cant send special characters

I have a program that should login to site, it uses POST requests, and all goes fine, until one of the values contain special character('%' for example).
captcha = "ABCDE" //all goes fine and well, server accept captcha
captcha = "ABC&%" //server dont accept captcha and return fail
//here is the bad part:
string request = "password=" + HttpUtility.UrlEncode(encpass, Encoding.UTF8) +
"&username=" + login + "&captcha_text=" + HttpUtility.UrlEncode(captcha, Encoding.UTF8);
Also, i ofcourse googled it, and checked all i could find. I though i need to "warn" server abaut encoding, so i added
request.Headers.TryAddWithoutValidation("Content-Type", #"application/x-www-form-urlencoded; charset=UTF-8");
but it still did not helped me.
Content types and way request should look like i get from Firebug, so if i can find some answers there - please point.
modify0: Also, i compared what my program send to server with browser request(using Firebug) and my request is completley same. Only difference - my request dont get accepted in values it contain special-characters.
modify1: Also, server have no problems handling special-characters when i check it in browser. For example it(browser) sent "K&YF82" as "captcha_text=K%26YF82"(same value in addres propereties and request body) and all worked fine. UrlEncode do same replacement, but in my program it doesnt get accepted by server.
SOLUTION:
{ password:"df464dsj", username:"username", captchaText:"ABC&%", remember_login:"false" }
insteat of
password=f2341f14f&username=username&captha...
Are you dealing with REST application??
If yes then send your post data in request body instead of query string.
Also have a look at the stack post at : Special characters pose problems with REST webservice communication

HttpRequest (PHP to C#) - deserialize a string

I am trying to send a string to a C# app via php, here is my code:
PHP
//set up variables
$theData = 'test
to see if a string can be deserialized';
$url = 'http://localhost:5900/';
//create the httprequest object
$httpRequest_OBJ = new httpRequest($url, HTTP_METH_POST, $options);
//add the raw post data
$httpRequest_OBJ->setRawPostData ($theData);
//send the http request
$result = $httpRequest_OBJ->send();
//get the object
$response = $result->getBody();
C#
Here is where it fails:
var methodRequestSerializer = new XmlSerializer(typeof(MethodRequest));
var methodRequest = methodRequestSerializer.Deserialize(reader) as MethodRequest;
PHP throws back the following error:
Fatal error: Uncaught exception 'HttpInvalidParamException' with message 'Empty or too short HTTP message: ''' in C:\xampp\htdocs\httpreq.php:39 inner exception 'HttpRequestException' with message 'server returned nothing (no headers, no data); Empty reply from server (http://localhost:5900/)' in C:\xampp\htdocs\httpreq.php:29 Stack trace: #0 C:\xampp\htdocs\httpreq.php(39): HttpRequest->send() #1 {main} thrown in C:\xampp\htdocs\httpreq.php on line 39
thanks.
When you say your reader comes from new StreamReader(context.Request.InputStream), I'm assuming that context is the ASP.NET HttpContext object, and your C# code is somehow deployed so as to receive an HttpRequest.
If so, the stream will contain just the bytes from the body of your HTTP request, which in the case of your example will just be the string "test to see if a string can be deserialized". Since this is not valid XML you should expect the Deserialize method call to fail.
You haven't told us what MethodRequest is, nor what an instance would look like when XML serialized. If the string you sent in the HTTP POST request were a valid XML-serialized instance of MethodRequest, I would expect your C# Deserialize method call to succeed.
If you'd be a little less coy and share fuller details of what you're trying to do and how, we might be able to help more easily.

Invoking another URL

string url = "http://foo.com/bar?id=" + id + "&more=" + more;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
I m trying make a call to another server, and getting back the following:
|FATAL|The remote server returned an error: (406) Not Acceptable. (REF #1)
System.Net.WebException: The remote server returned an error: (406) Not Acceptable.
Why am i getting this error? and how to fix this?
According to the RFC
10.4.7 406 Not Acceptable
The resource identified by the request
is only capable of generating response
entities which have content
characteristics not acceptable
according to the accept headers sent
in the request.
Review the accept headers that your request is sending , and the content server in that URL ; )
BONUS
To see the accept headers: browse to
the URL and use FireBug (HTML tab).
To set the accept headers into your
request use the HttpWebRequest
Members.

Categories