Moving emails/messages using Microsoft Graph SDK's Batch Requests - c#

My objective is to process emails in a folder and then move them to another folder to which I have the id. To ease the workload I'm trying to make use of the batch functionality.
Unfortunately, every time I try to run the batch function I'm presented with an exception with the message Code: invalidRequest Message: Unable to deserialize content..
The code in question, simplified for just one request, can be found below.
var batch = new BatchRequestContent();
var json = JsonSerializer.Serialize( new { destinationId = Graph.Me.Messages[messageId].Move(folderId).Request().RequestBody.DestinationId } );
var jsonMessage = Graph.HttpProvider.Serializer.SerializeAsJsonContent(json);
var request = Graph.Me.Messages[messageId].Move(folderId).Request().GetHttpRequestMessage();
request.Method = HttpMethod.Post;
request.Content = jsonMessage;
batch.AddBatchRequestStep(request);
var res = await Graph.Batch.Request().PostAsync(batch);
I've narrowed down the problem to be about the request.Content because without that it will go through, though getting back with a 400 error about missing body.
Copying the string from batch.ReadAsStringAsync() and pasting that directly into the Graph Explorer and using that to run the query returns a 200 success.
Based on what I've tried I'm starting to lean on it being a limitation of the SDK's batch.
Any ideas?

While this wasn't the solution I was looking for, it works as a band aid solution.
Basically, after you add all your steps to the BatchRequestContent you use the ReadAsStringAsync() to get the body of the request. Then you use the library itself to send the HTTP Request, as instructed here.
HttpRequestMessage hrm = Graph.Batch.Request().GetHttpRequestMessage();
hrm.Method = HttpMethod.Post;
hrm.Content = Graph.HttpProvider.Serializer.SerializeAsJsonContent(await batch.ReadAsStringAsync());
// Authenticate (add access token) our HttpRequestMessage
await Graph.AuthenticationProvider.AuthenticateRequestAsync(hrm);
// Send the request and get the response.
HttpResponseMessage res = await Graph.HttpProvider.SendAsync(hrm);

Related

How to get response header in c# Microsoft graph api request

I am trying to do File Copy operation in c# .net core using Microsoft graph API.
It is an asynchronous operation, and by doc, it says it returns a location in the response header to check the status of the operation,
Now the issue is I need its response header so that I can check the status of file copy operation but every time I am getting 'null' as value, I have tried following code,
DriveItem response = await graphClient.Sites[siteId].Drive.Items[itemId]
.Copy(fileName, parentReference)
.Request()
.PostAsync();
The driveItem returns null but I think at least it should have returned the additional data-carrying response status and location.
When I use online graph api it just works fine returning response and location, but it doesn't with graph client service.
Apparently it is an issue with msgraph-sdk-dotnet, at least it could be reproduced in 3.8.0 version, the error occurs while deserializing HTTP response. Probably it would be more beneficial to report it as a bug in referenced repository.
Meanwhile you could consider to construct a request for Copy a DriveItem endpoint and process response (including extracting Location header) as demonstrated below:
var message = graphClient.Sites[siteId].Drive.Items[itemId]
.Copy(fileName, parentReference)
.Request()
.GetHttpRequestMessage();
message.Method = HttpMethod.Post;
var body = new DriveItemCopyRequestBody {Name = fileName, ParentReference = parentReference};
message.Content = new StringContent(graphClient.HttpProvider.Serializer.SerializeObject(body));
message.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = graphClient.HttpProvider.SendAsync(message).Result;
Console.Write(response.Headers.Location);

Client-side Blazor using SendAsync for a GetRequest with CORS for an API request

I ran into some issues with CORS when setting up my Blazor client-side API client to make requests. I think I found the solution to that, but the solution is also throwing errors.
The main error is:
"WASM: System.Net.Http.HttpRequestException: TypeError: Failed to execute 'fetch' on 'Window': The provided value '2' is not a valid enum value of type RequestCredentials."
the code is
string link = API_RequestLoginTokenEndPoint;
Http.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
Http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Authorization", "basic:testuser:testpassword");
var requestMessage = new HttpRequestMessage(new HttpMethod("GET"), link);
requestMessage.Properties[WebAssemblyHttpMessageHandler.FetchArgs] = new
{
credentials = FetchCredentialsOption.Include
};
var response = await Http.SendAsync(requestMessage);
var responseStatusCode = response.StatusCode;
var responseBody = await response.Content.ReadAsStringAsync();
output = responseBody + " " + responseStatusCode;
I also tried changing the request message to:
var requestMessage = new HttpRequestMessage(HttpMethod.Get, link);
In case this was the ENUM the error referred to. In the Startup ConfigureServices I tried to add:
WebAssemblyHttpMessageHandler.DefaultCredentials = FetchCredentialsOption.Include;
I am using Blazor preview 9. I also tried adding some CORS code to my PHP script on the API route that should accept all origins, but the last question I posted I was told to use this method to fix the CORS problem, which now gives me a new error.
Am I doing something wrong or am I missing something? The error in the browser usually points to the line with the async request:
var response = await Http.SendAsync(requestMessage);
This is a bug not yet fixed. Use this instead :
requestMessage.Properties[WebAssemblyHttpMessageHandler.FetchArgs] = new
{
credentials = "include"
};
I had a similar issue and could not resolve the pre-flight activity.
I WAS ABLE TO SOLVE THIS BY COMMENTING OUT HTTP REDIRECTION MIDDLEWARE.
context: Blazor client calls asp.net.core api in different url.
Not sure if this a good solution but I felt I needed to mention this after
spending 1 week on this frustrating issue! Hope it helps someone.

Sharepoint online API returns: HTTP Error 400. A request header field is too long

I have a loop that will loop through records in my DB, pulling information i need and then creating 3 folders & upload a file.
This works OK for like 40 records but then it starts erroring out with the below response back from sharepoint: <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\"http://www.w3.org/TR/html4/strict.dtd\">\r\n<HTML><HEAD><TITLE>Bad Request</TITLE>\r\n<META HTTP-EQUIV=\"Content-Type\" Content=\"text/html; charset=us-ascii\"></HEAD>\r\n<BODY><h2>Bad Request - Header Field Too Long</h2>\r\n<hr><p>HTTP Error 400. A request header field is too long.</p>\r\n</BODY></HTML>
I am not sure whats going on, i read online its todo with cookies but i am using HTTPClient to send the request so i dont know how that would effect it? I also seen onlne about changing the kestrel?
Can anybody shed some light on this for me? Provide me with an easy but working solution? I dont use CSOM for integrating to sharepoint online, i use HTTP Requests, below is a sample of how i interact with sharepoint.
It seems as if i get blocked or banned temporarily cause if i wait a good bit, i can then make the same request that failed previously, and it will work! So strange.
Sample code (Used to create a resource at Sharepoint):
//Set Endpoint
var sharePointEndpoint = $"https://{hostname}/sites/{site}/_api/web/folders";
//Set default headers
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", sharePointToken); //Set token
client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
//Pre-Body data setup
var metaData = new MetaDataModel();
metaData.type = "SP.Folder";
//Body data setup
var bodyModel = new ExpandoObject() as IDictionary<string, object>;
bodyModel.Add("__metadata", metaData);
bodyModel.Add("ServerRelativeUrl", location + "/" + directoryName + "/");
//Set content headers
HttpContent strContent = new StringContent(JsonConvert.SerializeObject(bodyModel));
strContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
strContent.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("odata", "verbose"));
// Send request, grab response
var response = await client.PostAsync(sharePointEndpoint, strContent);
//Return response message
return response;
It turns out I needed to use Content-Length header when sending the request, once done I was able to successfully communicate with sharepoint without encountering this error.
More information here: https://social.technet.microsoft.com/Forums/en-US/26459f1c-945d-4112-9200-69c5a33a37ff/sharepoint-online-rest-api-returns-http-error-400-a-request-header-field-is-too-long?forum=sharepointdevelopment
Thanks.

How do I send a post request to telegram API from C#?

I have my telegram application with app's api_id and app's api_hash.
I used TLSharp library for implementing my own things. But now I need to use this https://core.telegram.org/method/auth.checkPhone telegram api method, but it's not implemented in TLSharp library!
I don't mind doing it all manually, but I don't know how!
I know how you send post requests in C#, example:
var response = await client.PostAsync("http://www.example.com/index", content);
but in this specific case I don't. Because I don't know:
1) what link should I use for sending post requests? I couldn't find it on the telegram's website.
2) what content should I pass there? Should it be just "(auth.checkPhone "+380666454343")" or maybe the whole "(auth.checkPhone "+380666454343")=(auth.checkedPhonephone_registered:(boolFalse)phone_invited:(boolFalse))" ?
So, How do I sent this post request to the telegram api? (NOT telegram bot api!)
Try to use System.Net.Http like in this example (auth request to the server):
var user = new { login = "your login", password = "your pass" };
string json = JsonConvert.SerializeObject(user);
HttpContent content = new StringContent(json, Encoding.UTF8, "application/json");
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
request.RequestUri = new Uri("server route link"); // can be like https://a100.technovik.ru:1000/api/auth/authenticate
request.Method = HttpMethod.Post;
request.Content = content;
HttpResponseMessage response = await client.SendAsync(request);
responseText.Text = await response.Content.ReadAsStringAsync();
I think based on a brief look, that it would be more along the lines of your second example, e.g.:
var phonenumber = "0123456789";
var content =
$#"(auth.checkPhone ""{phonenumber}"")"+
"=(auth.checkedPhone phone_registered: (boolFalse) phone_invited:(boolFalse))";
var result = DoHttpPost("http://some.example.com/api/etc", content);
(note: I've not listed the actual mechanics of an HTTP Request here, as that is covered in plenty of detail elsewhere - not least in the other current answer supplied to you; DoHttpPost() is not a real method and exists here only as a placeholder for this process)
And since the payload of this appears to indicate the exact function and parameters required, that you'd just send it to the base api endpoint you use for everything, but I can't say for sure...
I do note they do appear to have links to source code for various apps on the site though, so perhaps you'd be better off looking there?

RestSharp, Twitter. update_with_media cannot authorize

trying to send Tweet with the image attached, using RestSharp:
_client = new RestClient("https://api.twitter.com")
{
Authenticator = OAuth1Authenticator.ForProtectedResource(Key, Secret, Token, TokenSecret)
};
RestRequest request = new RestRequest("/1.1/statuses/update_with_media.json", Method.POST);
request.AddFile("media", att.File, att.FileName, "base64");
request.AddParameter("status", postStatus.Text);
var result = await _client.ExecuteTaskAsync(request);
The result is "Could not authenticate you" error no - 32
Thanks
UPDATE: All authentication parameters start from oauth_ and go in alphabetical order, the token, token secret, app key and app key secret are correct, the update status without media works perfectly.
UPDATE 2:
Solution
var request = new RestRequest("/1.1/statuses/update_with_media.json", Method.POST);
request.AlwaysMultipartFormData = true;
request.AddParameter("status", message, ParameterType.UrlSegment);
request.AddFile("media[]", file, filename, "application/octet-stream");
var result = _client.Execute(request);
This is actually a problem with restsharp 104.4 (version in Nuget as of time of writing)
We hit the same problem, but your solution above did not work for us. The UrlSegment parameter fails on a status update, and while it does not fail on a call to update_with_media, it also does not post the status, just the picture.
The problem is with OAuth1Authenticator, it does not ignore non-oauth POST or GET parameters, hence the authentication errors above, and why the URL segment parameter "works".
To fix this, get the latest version of RestSharp from GitHub and use that instead.
For those interested, the checkin involved was only made a month or so after the release and can be found here.

Categories

Resources