How to do a POST batch request in Miscrosoft Graph .NET SDK - microsoft-graph-api

I'm trying to do a batch request using MS Graph .NET SDK as shown here: https://learn.microsoft.com/en-us/graph/sdks/batch-requests?tabs=csharp
The only problem is that when I run the code, nothing happens.
I'm trying to move a set of emails (stored in a list) to another mail folder.
Am I missing anything?
The move request is here https://learn.microsoft.com/en-us/graph/api/message-move?view=graph-rest-1.0&tabs=http
When used in a single query it works, but not when batching.
Below you will find the code, in this case I'm looping to 20 just to test as 20 is the maximum queries per batch.
Thanks in advance.
for (int i = 0; i < 20; i++)
{
var mail = invalidMessages[i];
var userRequest = client.Me.Messages[mail.Id]
.Move(failureFolderID)
.Request();
requestID = batchRequestContent.AddBatchRequestStep(userRequest);
}
var returnedResponse = await client.Batch.Request().PostAsync(batchRequestContent);
EDIT: I tried to change the method to POST
userRequest.Method = System.Net.Http.HttpMethod.Post;
but I get a ServiceException: 'Code: BadRequest
Message: Write request id : fe23b1c1-663d-4499-829a-291d04a12b48 does not contain Content-Type header or body.'

The Microsoft Graph message-move API call you are attempting to use is a POST request
The Microsoft Batch API handles POST requests differently than the other API methods.
As per https://learn.microsoft.com/en-us/graph/sdks/batch-requests?tabs=csharp
POST requests are handled a bit differently.
The SDK request builders generate GET requests, so
you must get the HttpRequestMessage and convert to a POST
To have a successful post with the batch API you need to
Create an HttpRequestMessage
provide a value for the HttpRequestMessage's Content property which houses the POST requests payload
So if I applied this to your code I would first create a class to represent the POST payload for the message-move API. As per https://learn.microsoft.com/en-us/graph/api/message-move?view=graph-rest-1.0&tabs=http
the POST API has one property called destinationId
destinationId - The destination folder ID, or a well-known folder name. For a list of supported well-known folder names, see mailFolder resource type.
public class MailMovePayload
{
public string destinationId { get; set; }
}
then I would use an instance of this class in this modified version of you code
string str = events.Content.ReadAsStringAsync().Result;
events.Method = HttpMethod.Post;
for (int i = 0; i < 20; i++)
{
var mail = invalidMessages[i];
//get the request message object from your request
var userRequestMessage = client.Me.Messages[mail.Id]
.Move(failureFolderID)
.GetHttpRequestMessage();
//set the message API method
userRequestMessage.Method = HttpMethod.Post;
//create the payload, I am assuming failureFolderID is
//the name of the folder where the mail will be moved to
var payloadData = new MailMovePayload { destinationId = failureFolderID };
//make the JSON payload for the request message
userRequestMessage.Content = new StringContent(JsonConvert.SerializeObject(payloadData), Encoding.UTF8, "application/json")
requestID = batchRequestContent.AddBatchRequestStep(userRequestMessage);
}
var returnedResponse = await client.Batch.Request().PostAsync(batchRequestContent);
return httpRequestMessage;
}

Related

"The audience claim value is invalid for current resource" when using ChunkedUploadProvider for Attachment in Microsoft Graph Client API

I am trying to use the following code, but am getting "Message: The audience claim value is invalid for current resource. Audience claim is 'https://graph.microsoft.com', request url is 'https://outlook.office.com/api/beta/Users..."
I get it on the provider.GetUploadChunkRequests(); call below:
AttachmentItem attachmentItem= new AttachmentItem
{
Name = [Name],
AttachmentType = AttachmentType.File,
Size = [Size]
};
var session = graphClient.Users[USEREMAIL].Messages[MESSAGEID].Attachments.CreateUploadSession(attachmentItem).Request().PostAsync().Result;
var stream = new MemoryStream(BYTEARRAY);
var maxSizeChunk = DEFAULT_CHUNK_SIZE;
var provider = new ChunkedUploadProvider(session, graphClient, stream, maxSizeChunk);
var chunkRequests = provider.GetUploadChunkRequests();
(I am using the graphClient to send emails successfully, and have also used it to upload large files using the uploadSession method)
From Andrue Eastman on GitHub:
You are most likely getting the error because of using the ChunkedUploadPorvider instead of using the FileUploadTask to upload the attachment which is setting the Auth header to cause the error you are receiving.
To use the file upload task, follow the following steps
First create an upload session and handing it over to the task as illustrated.
// Create task
var maxSliceSize = 320 * 1024; // 320 KB - Change this to your chunk size. 4MB is the default.
LargeFileUploadTask<FileAttachment> largeFileUploadTask = new LargeFileUploadTask<FileAttachment>(uploadSession, stream, maxSliceSize);
Create an upload monitor (optional)
// Setup the progress monitoring
IProgress<long> progress = new Progress<long>(progress =>
{
Console.WriteLine($"Uploaded {progress} bytes of {stream.Length} bytes");
});
The service only returns location URI which can be read off from the result object as follows.
UploadResult<FileAttachment> uploadResult = null;
try
{
uploadResult = await largeFileUploadTask.UploadAsync(progress);
if (uploadResult.UploadSucceeded)
{
Console.WriteLine(uploadResult.Location);//the location of the object
}
}
catch (ServiceException e)
{
Console.WriteLine(e.Message);
}

.Net Core 2. How to upload a file with metadata in same request?

I am trying to upload a file to a .net core 2 rest API while in the same POST request also upload metadata (which will be stored in database). By metadata I mean a few info about the file (uploader, date/time of upload and just a few more) as Json format.
I have searched and tried different ways found on Stackoverflow but none worked. I am right now at this point.
The API Controller:
[HttpPost]
[Consumes("multipart/form-data")]
public async Task<ActionResult> upload(IFormFile file, MyMetadata metadata)
{
// my logic
}
The client:
using (var fileStream = File.OpenRead(filePath))
{
using (var multiPartFormDataContent = new MultipartFormDataContent())
{
multiPartFormDataContent.Add
(
new StreamContent(fileStream)
{
Headers =
{
ContentLength = fileStream.Length,
ContentType = new MediaTypeHeaderValue("application/octet-stream")
}
}
, "file"
, Path.GetFileName(filePath)
);
var link = /mycontroller/upload;
var resultTask = client.PostAsync(link, multiPartFormDataContent);
var result = await resultTask;
I have tried adding the model to multPartFormDataContent but without success.
So what I am asking for, is a complete solution Controller and Client in same answer that will work.

How to convert Office files to PDF using Microsoft Graph

I'm looking for a way to convert Office files to PDF.
I found out that Microsoft Graph could be used.
I'm trying to download converted PDF using Microsoft Graph from OneDrive.
I'd like to convert .docx to .pdf.
However, when I sent the following request, I did not receive a response even if I waited.
GET https://graph.microsoft.com/v1.0/users/{id}/drive/root:/test.docx:/content?format=pdf
Also, the error code is not returned.
If syntax is wrong, an error code will be returned as expected.
It will not return only when it is correct.
In addition, I can download the file if I do not convert.
GET https://graph.microsoft.com/v1.0/users/{id}/drive/root:/test.docx:/content
Is my method wrong or else I need conditions?
If possible, please give me sample code that you can actually do.
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
client.BaseAddress = new Uri(graphUrl);
var result = await client.GetAsync("/v1.0/users/xxxxxxxxxxxxxxxxxxxxxxxxx/drive/root:/test.docx:/content?format=pdf");
:
I would like to elaborate a bit Marc's answer by providing a few examples for HttpClient.
Since by default for HttpClient HttpClientHandler.AllowAutoRedirect property is set to True there is no need to explicitly follow HTTP redirection headers and the content could be downloaded like this:
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
client.BaseAddress = new Uri("https://graph.microsoft.com");
var response = await client.GetAsync($"/v1.0/drives/{driveId}/root:/{filePath}:/content?format=pdf");
//save content into file
using (var file = System.IO.File.Create(fileName))
{
var stream = await response.Content.ReadAsStreamAsync();
await stream.CopyToAsync(file);
}
}
In case if follow HTTP redirection is disabled, to download the converted file, your app must follow the Location header in the response as demonstrated below:
var handler = new HttpClientHandler()
{
AllowAutoRedirect = false
};
using (HttpClient client = new HttpClient(handler))
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
client.BaseAddress = new Uri("https://graph.microsoft.com");
var response = await client.GetAsync($"/v1.0/drives/{driveId}/root:/{filePath}:/content?format=pdf");
if(response.StatusCode == HttpStatusCode.Redirect)
{
response = await client.GetAsync(response.Headers.Location); //get the actual content
}
//save content into file
using (var file = System.IO.File.Create(fileName))
{
var stream = await response.Content.ReadAsStreamAsync();
await stream.CopyToAsync(file);
}
}
The API doesn't return the converted content directly, it returns a link to the converted file. From the documentation:
Returns a 302 Found response redirecting to a pre-authenticated download URL for the converted file.
To download the converted file, your app must follow the Location header in the response.
Pre-authenticated URLs are only valid for a short period of time (a few minutes) and do not require an Authorization header to access.
You need to capture the 302 and make a 2nd call to the URI in the Location header in order to download the converted file.

How can i Pull Profile image from sharepoint

I'am new in API's & trying to pull user profile from sharepoint i use following code but don't know about servername? domainname? and username?
const string serverUrl = "http://sharepoint.com/";
const string targetUser = "ttgdev-my.sharepoint.com\\testuser1#ttgdev.guru";
// Connect to the client context.
ClientContext clientContext = new ClientContext(serverUrl);
// Get the PeopleManager object and then get the target user's properties.
PeopleManager peopleManager = new PeopleManager(clientContext);
PersonProperties personProperties = peopleManager.GetPropertiesFor(targetUser);
// Load the request and run it on the server.
// This example requests only the AccountName and UserProfileProperties
// properties of the personProperties object.
clientContext.Load(personProperties, p => p.AccountName, p => p.UserProfileProperties);
clientContext.ExecuteQuery();
foreach (var property in personProperties.UserProfileProperties)
{
Console.WriteLine(string.Format("{0}: {1}",
property.Key.ToString(), property.Value.ToString()));
}
Console.ReadKey(false);
Please guide me it will give me the error in
{"The property or field 'UserProfileProperties' has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested."}
in the following line
clientContext.ExecuteQuery();
Most likely it is related with the format of targetUser variable. PeopleManager.GetPropertiesFor method expects accountName parameter to be specified in the proper format, in case of SharePoint Online it should be specified in claims format, for example:
i:0#.f|membership|jdow#contoso.onmicrosoft.com
For more details about Claims format follow this article.
So, in your case targetUser value should be replaced from ttgdev-my.sharepoint.com\\testuser1#ttgdev.guru to i:0#.f|membership|testuser1#ttgdev.guru
The following example demonstrates how to retrieve user profile picture via CSOM API:
using (var ctx = TokenHelper.GetClientContextWithAccessToken(webUri.ToString(), accessToken))
{
// Get the PeopleManager object and then get the target user's properties.
var peopleManager = new PeopleManager(ctx);
PersonProperties personProperties = peopleManager.GetPropertiesFor(targetUser);
//Retrieve picture property
var result = peopleManager.GetUserProfilePropertyFor(accountName, "PictureURL");
ctx.ExecuteQuery();
Console.WriteLine("Picture Url: {0}",result.Value);
}

How to change the default values of start and count parameters when picking the companies followed by a user in linkedin?

I am making a web-application in ASP.NET. I have used oauth to get profile fields of a user. I need the names of the companies followed by the user, but the problem is that the default value is set to 20. so, if the user is following more than 20 companies i am not able to get it. Please tell me how can i modify the start and count values. Iv used this url to make the call http://api.linkedin.com/v1/people/~:(following:(people,companies,industries,news-sources),educations).. Please help asap..
var requestHeader = GetUserProfileAuthorizationHeader();
var queryString = CreateQueryString();
var request = WebRequest.Create(RequestProfileUrl + queryString);
request.Headers.Add("Authorization", requestHeader.ToString());
request.Method = HttpMethod.Get;
try
{
var response = request.GetResponse();
using (var responseStream = response.GetResponseStream())
{
var reader = new StreamReader(responseStream);
var responseText = reader.ReadToEnd();
reader.Close();
return responseText;
}
}
Here public static string RequestProfileUrl = "http://api.linkedin.com/v1/people/~:(following:(companies:(id,name,size,industry),industries))"; And the method CreateQueryString() does this queryString = "?format=xml"; whenever i try to add something like queryString = "?format=xml&start=0&count=40"; it gives error dispite the number of companies followed being more than 60.. maybe i need to pass the query parameters in between the RequestProfileUrl i.e near the company somehow..
Did you tried adding ?start=x&count=y to the url?
Probably if you're getting an error when you add query parameters to the URL, you're not adding those parameters in the way that your OAuth library expects them to be added. You need to figure out how to add the parameters so they're added to the signature generation process or your signature will be invalid and you'll get a 401 error back from the server.

Resources