我正在创建一个mvc Web应用程序来读取用户邮件,向用户发送邮件以及使用Microsoft Graph Api接收通知。我的应用已在https://apps.dev.microsoft.com中注册。我可以在登录后和订阅通知后获得“ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value”的返回值。但是我无法在Listen()函数中获取返回值,该函数用于侦听通知。 “对象引用未设置为对象的实例”是给出的错误。这是Listen()函数。
public async Task<ActionResult> Listen()
{
// Validate the new subscription by sending the token back to Microsoft Graph.
// This response is required for each subscription.
if (Request.QueryString["validationToken"] != null)
{
var token = Request.QueryString["validationToken"];
return Content(token, "plain/text");
}
// Parse the received notifications.
else
{
try
{
var notifications = new Dictionary<string, NotificationModel>();
using (var inputStream = new System.IO.StreamReader(Request.InputStream))
{
JObject jsonObject = JObject.Parse(inputStream.ReadToEnd());
if (jsonObject != null)
{
// Notifications are sent in a 'value' array. The array might contain multiple notifications for events that are
// registered for the same notification endpoint, and that occur within a short timespan.
JArray value = JArray.Parse(jsonObject["value"].ToString());
foreach (var notification in value)
{
NotificationModel current = JsonConvert.DeserializeObject<NotificationModel>(notification.ToString());
// Check client state to verify the message is from Microsoft Graph.
SubscriptionStore subscription = SubscriptionStore.GetSubscriptionInfo(current.SubscriptionId);
// This sample only works with subscriptions that are still cached.
if (subscription != null)
{
if (current.ClientState == subscription.ClientState)
{
// Just keep the latest notification for each resource.
// No point pulling data more than once.
notifications[current.Resource] = current;
}
}
}
if (notifications.Count > 0)
{
// Query for the changed messages.
await GetChangedMessagesAsync(notifications.Values);
}
}
}
}
catch (Exception)
{
// TODO: Handle the exception.
// Still return a 202 so the service doesn't resend the notification.
}
return new HttpStatusCodeResult(202);
}
}
// Get information about the changed messages and send to the browser via SignalR.
// A production application would typically queue a background job for reliability.
public async Task GetChangedMessagesAsync(IEnumerable<NotificationModel> notifications)
{
List<MessageViewModel> messages = new List<MessageViewModel>();
MessageModel message = new MessageModel();
string serviceRootUrl = "https://graph.microsoft.com/v1.0/";
foreach (var notification in notifications)
{
SubscriptionStore subscription = SubscriptionStore.GetSubscriptionInfo(notification.SubscriptionId);
string token;
try
{
// Get the access token for the subscribed user.
// accessToken = await GetAccessToken();
HttpContextBase httpContext = HttpContext;
token = await AuthHelper.GetAccessToken(httpContext);
}
catch (Exception e)
{
throw e;
}
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, serviceRootUrl + notification.Resource);
// Send the 'GET' request.
GraphHttpClient graphHttpClient = new GraphHttpClient(token);
HttpResponseMessage response = await graphHttpClient.SendAsync(request);
// Get the messages from the JSON response.
if (response.IsSuccessStatusCode)
{
string stringResult = await response.Content.ReadAsStringAsync();
string type = notification.ResourceData.ODataType;
if (type == "#Microsoft.Graph.Message")
{
message = JsonConvert.DeserializeObject<MessageModel>(stringResult);
MessageViewModel messageViewModel = new MessageViewModel(message);
messages.Add(messageViewModel);
}
}
}
if (messages.Count > 0)
{
NotificationService notificationService = new NotificationService();
notificationService.SendNotificationToClient(messages);
}
}
这是GetAccessToken()方法,其中使用了“ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value”。
public static async Task<string> GetAccessToken(HttpContextBase HttpContext)
{
string accessToken = string.Empty; ;
// Load the app config from web.config
string appId = ConfigurationManager.AppSettings["ida:AppId"];
string appPassword = ConfigurationManager.AppSettings["ida:AppPassword"];
string redirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"];
string[] scopes = ConfigurationManager.AppSettings["ida:AppScopes"]
.Replace(' ', ',').Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
string userId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
if (!string.IsNullOrEmpty(userId))
{
// Get the user's token cache
SessionTokenCache tokenCache = new SessionTokenCache(userId, HttpContext);
ConfidentialClientApplication cca = new ConfidentialClientApplication(
appId, redirectUri, new Microsoft.Identity.Client.ClientCredential(appPassword), tokenCache.GetMsalCacheInstance(), null);
// Call AcquireTokenSilentAsync, which will return the cached
// access token if it has not expired. If it has expired, it will
// handle using the refresh token to get a new one.
AuthenticationResult result = await cca.AcquireTokenSilentAsync(scopes, cca.Users.FirstOrDefault());
accessToken = result.AccessToken;
}
// Get the current user's ID
return accessToken;
}
答案 0 :(得分:0)
我认为您所说的是您没有从通知端点上的OWIN中间件获取用户身份。你不应该期待一个!您的通知端点从Office 365服务接收POSTS,该服务未以任何特定用户身份运行。它确实包含Authorization
标头中的承载令牌,您可以使用它来验证它是否真的来自Microsoft,但就是这样。
您应该将订阅ID映射到您身边的用户ID。这样,当收到通知时,您可以进行关联。您也不希望在该场景中使用基于会话的令牌缓存。相反,您希望您的应用程序可以全局访问某些内容,例如数据库。