.NET 6环境下Microsoft Forms认证及响应读取故障排查求助
排查Microsoft Forms Graph API访问问题
一、先确认认证与权限是否有效
- 单独测试Token获取:在
GetToken方法里添加Console.WriteLine(token);,把输出的Token复制到jwt.ms解析,检查:aud字段必须是https://graph.microsoft.comroles字段必须包含Forms.Read.All或Forms.ReadWrite.All(没有的话就是权限配置错误)
- 检查Azure AD应用配置:
- 必须添加应用权限(而非委托权限),权限选择
Forms.Read.All或更高等级 - 必须完成管理员同意(client_credentials流必须管理员同意才能生效)
- 确认表单属于当前租户,个人Microsoft账户的表单不支持Graph API访问
- 必须添加应用权限(而非委托权限),权限选择
二、代码问题修正
1. 模型类不匹配Graph API响应结构
原代码的FormResponse、QuestionResponse和Graph实际返回的JSON结构不符,以下是正确的模型类:
using System.Text.Json.Serialization; class FormResponseCollection { public List<FormResponse> Value { get; set; } } class FormResponse { public string Id { get; set; } public DateTimeOffset SubmittedDateTime { get; set; } public List<FormAnswer> Answers { get; set; } } class FormAnswer { public string QuestionId { get; set; } public string QuestionText { get; set; } public AnswerValue AnswerValue { get; set; } } class AnswerValue { public List<string> Choices { get; set; } public string Text { get; set; } public int? Rating { get; set; } // 根据你的表单类型添加其他字段,比如日期、文件等 } class TokenResponse { [JsonPropertyName("access_token")] public string AccessToken { get; set; } }
2. 添加错误捕获与调试信息
原代码无异常处理,无法定位具体错误,修改GetFormResponses方法:
private static async Task<IList<FormResponse>> GetFormResponses() { var clientId = "<your-client-id>"; var tenantId = "<your-tenant-id>"; var clientSecret = "<your-client-secret>"; var formId = "<your-form-id>"; try { var token = await GetToken(clientId, tenantId, clientSecret); Console.WriteLine($"获取到的Token前缀: {token.Substring(0, 50)}..."); // 输出前50位用于验证 using var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); var response = await httpClient.GetAsync($"https://graph.microsoft.com/v1.0/forms/{formId}/responses"); try { response.EnsureSuccessStatusCode(); } catch (HttpRequestException ex) { var errorContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"API请求失败: {ex.Message}"); Console.WriteLine($"错误详情: {errorContent}"); throw; } var content = await response.Content.ReadAsStringAsync(); var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; var formResponses = JsonSerializer.Deserialize<FormResponseCollection>(content, options).Value; return formResponses; } catch (Exception ex) { Console.WriteLine($"流程执行错误: {ex.Message}"); throw; } }
3. 调整打印逻辑
因为答案结构变化,修改PrintResponses方法适配新模型:
private static void PrintResponses(IList<FormResponse> responses) { foreach (var response in responses) { Console.WriteLine($"Response ID: {response.Id}"); Console.WriteLine($"提交时间: {response.SubmittedDateTime}"); Console.WriteLine("回答内容:"); foreach (var answer in response.Answers) { Console.WriteLine($" 问题: {answer.QuestionText}"); string answerContent = ""; if (answer.AnswerValue.Choices != null && answer.AnswerValue.Choices.Count > 0) { answerContent = string.Join(", ", answer.AnswerValue.Choices); } else if (!string.IsNullOrEmpty(answer.AnswerValue.Text)) { answerContent = answer.AnswerValue.Text; } else if (answer.AnswerValue.Rating.HasValue) { answerContent = answer.AnswerValue.Rating.Value.ToString(); } Console.WriteLine($" 回答: {answerContent}"); } Console.WriteLine(); } }
三、常见错误对应解决方案
- 401 Unauthorized:Token无效,检查client_id/tenant_id/client_secret是否正确,或Token解析后的
aud和roles是否符合要求 - 403 Forbidden:权限不足,确认Azure应用的Forms权限已添加并完成管理员同意
- 404 Not Found:表单ID错误,或表单不属于当前租户(个人表单无法访问)
- 模型解析失败:打印返回的JSON内容,调整模型类字段与实际结构匹配
内容的提问来源于stack exchange,提问作者Gabriel Santos
相关产品推荐
相关产品推荐

