C# 使用Microsoft Graph将C Asp.Net Core中的文件上载到Sharepoint/OneDrive,无需用户交互
当我尝试使用Microsoft Graph API使用守护程序应用程序将文件上载到OneDrive时,收到错误400错误请求。我使用的是HttpClient,而不是GraphServiceClient,因为后者假定与DelegatedAuthenticationProvider进行交互并与DelegatedAuthenticationProvider一起工作 该应用程序已在AAD中注册,并具有正确的应用程序权限Microsoft Graph/File ReadWrite.All 该注册针对一个租户,按照说明,没有重定向url 主方法Upload通过Helper AuthenticationConfig获取AccessToken,并使用Helper ProtectedApiCallHelper将文件放入OneDrive/SharePoint 使用AccessToken、图形Url和要上载为Helper ProtectedApiCallHelper.PostWebApi的文件C# 使用Microsoft Graph将C Asp.Net Core中的文件上载到Sharepoint/OneDrive,无需用户交互,c#,asp.net-core,sharepoint,microsoft-graph-api,onedrive,C#,Asp.net Core,Sharepoint,Microsoft Graph Api,Onedrive,当我尝试使用Microsoft Graph API使用守护程序应用程序将文件上载到OneDrive时,收到错误400错误请求。我使用的是HttpClient,而不是GraphServiceClient,因为后者假定与DelegatedAuthenticationProvider进行交互并与DelegatedAuthenticationProvider一起工作 该应用程序已在AAD中注册,并具有正确的应用程序权限Microsoft Graph/File ReadWrite.All 该注册针对一个租
public async Task PostWebApi(string webApiUrl, string accessToken, IFormFile fileToUpload)
{
Stream stream = fileToUpload.OpenReadStream();
var x = stream.Length;
HttpContent content = new StreamContent(stream);
if (!string.IsNullOrEmpty(accessToken))
{
var defaultRequestHeaders = HttpClient.DefaultRequestHeaders;
HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream"));
defaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
// Here the 400 Bad Request happens
HttpResponseMessage response = await HttpClient.PutAsync(webApiUrl, content);
if (response.IsSuccessStatusCode)
{
return;
}
else
{
//error handling
return;
}
}
}
编辑
请参阅下面的工作解决方案。您可以使用GraphServiceClient,而无需使用客户端id和客户端密码进行用户交互。首先,创建一个名为GraphAuthProvider的类:
public class GraphAuthProvider
{
public async Task<GraphServiceClient> AuthenticateViaAppIdAndSecret(
string tenantId,
string clientId,
string clientSecret)
{
var scopes = new string[] { "https://graph.microsoft.com/.default" };
// Configure the MSAL client as a confidential client
var confidentialClient = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithAuthority($"https://login.microsoftonline.com/{tenantId}/v2.0")
.WithClientSecret(clientSecret)
.Build();
// Build the Microsoft Graph client. As the authentication provider, set an async lambda
// which uses the MSAL client to obtain an app-only access token to Microsoft Graph,
// and inserts this access token in the Authorization header of each API request.
GraphServiceClient graphServiceClient =
new GraphServiceClient(new DelegateAuthenticationProvider(async (requestMessage) =>
{
// Retrieve an access token for Microsoft Graph (gets a fresh token if needed).
var authResult = await confidentialClient
.AcquireTokenForClient(scopes)
.ExecuteAsync();
// Add the access token in the Authorization header of the API request.
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
})
);
return graphServiceClient;
}
}
然后,您可以创建经过身份验证的GraphServiceClient,并使用它们上载文件,例如到SharePoint:
GraphServiceClient _graphServiceClient = await _graphAuthProvider.AuthenticateViaAppIdAndSecret(
tenantId,
clientId,
appSecret);
using (Stream fileStream = new FileStream(
fileLocation,
FileMode.Open,
FileAccess.Read))
{
resultDriveItem = await _graphServiceClient.Sites[sites[0]]
.Drives[driveId].Root.ItemWithPath(fileName).Content.Request().PutAsync<DriveItem>(fileStream);
}
关于权限:您可能需要的权限不仅仅是Files.ReadWrite.All。据我所知,应用程序需要应用程序权限Sites.ReadWrite.All才能将文档上载到SharePoint。您可以使用GraphServiceClient,而无需使用客户端id和客户端密码进行用户交互。首先,创建一个名为GraphAuthProvider的类:
public class GraphAuthProvider
{
public async Task<GraphServiceClient> AuthenticateViaAppIdAndSecret(
string tenantId,
string clientId,
string clientSecret)
{
var scopes = new string[] { "https://graph.microsoft.com/.default" };
// Configure the MSAL client as a confidential client
var confidentialClient = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithAuthority($"https://login.microsoftonline.com/{tenantId}/v2.0")
.WithClientSecret(clientSecret)
.Build();
// Build the Microsoft Graph client. As the authentication provider, set an async lambda
// which uses the MSAL client to obtain an app-only access token to Microsoft Graph,
// and inserts this access token in the Authorization header of each API request.
GraphServiceClient graphServiceClient =
new GraphServiceClient(new DelegateAuthenticationProvider(async (requestMessage) =>
{
// Retrieve an access token for Microsoft Graph (gets a fresh token if needed).
var authResult = await confidentialClient
.AcquireTokenForClient(scopes)
.ExecuteAsync();
// Add the access token in the Authorization header of the API request.
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
})
);
return graphServiceClient;
}
}
然后,您可以创建经过身份验证的GraphServiceClient,并使用它们上载文件,例如到SharePoint:
GraphServiceClient _graphServiceClient = await _graphAuthProvider.AuthenticateViaAppIdAndSecret(
tenantId,
clientId,
appSecret);
using (Stream fileStream = new FileStream(
fileLocation,
FileMode.Open,
FileAccess.Read))
{
resultDriveItem = await _graphServiceClient.Sites[sites[0]]
.Drives[driveId].Root.ItemWithPath(fileName).Content.Request().PutAsync<DriveItem>(fileStream);
}
关于权限:您可能需要的权限不仅仅是Files.ReadWrite.All。据我所知,应用程序需要应用程序权限Sites.ReadWrite.All才能将文档上载到SharePoint。根据文档:
如果在没有用户的情况下使用客户端凭据流M2M流,则应使用以下请求:
PUT /drives/{drive-id}/items/{parent-id}:/{filename}:/content
而不是:
https://graph.microsoft.com/v1.0/drive/items/{Id_Of_Specific_Folder}/proefdocument.txt/content
根据文件:
如果在没有用户的情况下使用客户端凭据流M2M流,则应使用以下请求:
PUT /drives/{drive-id}/items/{parent-id}:/{filename}:/content
而不是:
https://graph.microsoft.com/v1.0/drive/items/{Id_Of_Specific_Folder}/proefdocument.txt/content
这是使用GraphServiceClient的最后一个工作示例
public async Task<DriveItem> UploadSmallFile(IFormFile file, bool uploadToSharePoint)
{
IFormFile fileToUpload = file;
Stream ms = new MemoryStream();
using (ms = new MemoryStream()) //this keeps the stream open
{
await fileToUpload.CopyToAsync(ms);
ms.Seek(0, SeekOrigin.Begin);
var buf2 = new byte[ms.Length];
ms.Read(buf2, 0, buf2.Length);
ms.Position = 0; // Very important!! to set the position at the beginning of the stream
GraphServiceClient _graphServiceClient = await AuthenticateViaAppIdAndSecret();
DriveItem uploadedFile = null;
if (uploadToSharePoint == true)
{
uploadedFile = (_graphServiceClient
.Sites["root"]
.Drives["{DriveId}"]
.Items["{Id_of_Targetfolder}"]
.ItemWithPath(fileToUpload.FileName)
.Content.Request()
.PutAsync<DriveItem>(ms)).Result;
}
else
{
// Upload to OneDrive (for Business)
uploadedFile = await _graphServiceClient
.Users["{Your_EmailAdress}"]
.Drive
.Root
.ItemWithPath(fileToUpload.FileName)
.Content.Request()
.PutAsync<DriveItem>(ms);
}
ms.Dispose(); //clears memory
return uploadedFile; //returns a DriveItem.
}
}
这是使用GraphServiceClient的最后一个工作示例
public async Task<DriveItem> UploadSmallFile(IFormFile file, bool uploadToSharePoint)
{
IFormFile fileToUpload = file;
Stream ms = new MemoryStream();
using (ms = new MemoryStream()) //this keeps the stream open
{
await fileToUpload.CopyToAsync(ms);
ms.Seek(0, SeekOrigin.Begin);
var buf2 = new byte[ms.Length];
ms.Read(buf2, 0, buf2.Length);
ms.Position = 0; // Very important!! to set the position at the beginning of the stream
GraphServiceClient _graphServiceClient = await AuthenticateViaAppIdAndSecret();
DriveItem uploadedFile = null;
if (uploadToSharePoint == true)
{
uploadedFile = (_graphServiceClient
.Sites["root"]
.Drives["{DriveId}"]
.Items["{Id_of_Targetfolder}"]
.ItemWithPath(fileToUpload.FileName)
.Content.Request()
.PutAsync<DriveItem>(ms)).Result;
}
else
{
// Upload to OneDrive (for Business)
uploadedFile = await _graphServiceClient
.Users["{Your_EmailAdress}"]
.Drive
.Root
.ItemWithPath(fileToUpload.FileName)
.Content.Request()
.PutAsync<DriveItem>(ms);
}
ms.Dispose(); //clears memory
return uploadedFile; //returns a DriveItem.
}
}
@问题1991谢谢你的明确指示。resultDriveItem是什么类型的变量?这是否应该是一个HttpResponseMessage?@QuestionPS1991您的解决方案也同样有效!非常感谢你的帮助。我现在面临另一个挑战,那就是让这条流正常工作。到目前为止,在这两种解决方案中,Memorystream都保持为空0字节。由于我没有得到任何错误,一切都“正常”,但没有结果。结果的类型是DriveItem。至于流,ifformFile.OpenReadStream只是打开流,您仍然需要读取该文件。这应该让你开始:我已经解决了。我将把解决方案添加到此讨论中。非常感谢much@QuestionPS1991谢谢你的明确指示。resultDriveItem是什么类型的变量?这应该是HttpResponseMessage吗?@QuestionPS1991您的解决方案同样有效!非常感谢你的帮助。我现在面临另一个挑战,那就是让这条流正常工作。到目前为止,在这两种解决方案中,Memorystream都保持为空0字节。由于我没有得到任何错误,一切都“正常”,但没有结果。结果的类型是DriveItem。至于流,ifformFile.OpenReadStream只是打开流,您仍然需要读取该文件。这应该让你开始:我已经解决了。我将把解决方案添加到此讨论中。非常感谢