Microsoft Graph 错误响应 - 如何从 JSON 中提取 HTTP 状态代码和内部错误代码?
Microsoft Graph Error Responses - How to extract HTTP Status Code and Inner Error Code from JSON?
我目前正在我的 .NET Core 3.1 应用程序中测试 MS Graph .NET Core SDK 客户端。目的是提供我自己的本地 Web API 用户服务,该服务执行来自 Azure B2C AD 的 updates/changes/fetching 用户。
在我预期的解决方案中,我将拥有各种 HTTP 客户端微服务,它们会调用 API 命令到我的用户服务 SDK 客户端,而不是直接使用 REST 调用 Azure Graph API .
在我的用户服务应用程序中,我试图通过对发送到 Azure 的实际 SDK 命令使用 repository/interface 方法来保持整洁。这个相同的用户服务应用程序然后 return 使用我自己的 WEB API 将数据返回到我的本地 HTTP 客户端。将此用户服务应用想象成中间人效果。
下面的图片总结了环境:
这样做的目的是在更改或添加与图形服务一起使用的功能时减少工作量,即在我自己的本地应用程序之间提供一些通信标准化,并促进更好的关注点分离。此外,如果 MS 对图形进行更改 API,那么我只会更新用户服务应用程序,而不是对所有 HTTP 客户端应用程序进行代码更改。希望这是有道理的。
总之,进入正题!我提问的原因:
(请耐心等待,我是 REST 和使用接口的新手)
Graph SDK 客户端和 Azure 中的 Graph 服务 API 之间遇到的任何错误都将记录在我的用户服务应用程序中,即我将在第一时间捕获的大量 json 错误详细信息,但是我根本不需要将这种级别的详细信息传递回我所有的本地调用 HTTP 客户端。
我想要实现的是 identifying/capturing SDK 客户端和图形服务之间遇到的任何 HTTP 状态代码错误的方法,以及错误的一些基本细节,即短描述,只将这些较低级别的详细信息传递回我的本地 HTTP 客户端,即保持干净。
我正在努力了解如何在我的代码中执行此操作,特别是考虑到我同时使用一个界面使它变得更加复杂这一事实。 MS 文档确实提供了有关使用图形服务的预期错误代码列表的信息,但没有示例说明如何处理此信息以便将相关(但信息的较轻版本)传递回另一个来源。
示例场景:
- 本地 HTTP 客户端调用 [HttpGet] GetUserById 到我的用户服务 Web API
- 我的网站 API 然后使用 Graph SDK 客户端连接到 Azure B2C,获取数据并 return。
- 然后我的 WEB API 应该检查收到的信息,如果找到用户那么很好,return 用户到我调用的 HTTP 客户端。
- 如果找不到用户,或者可能发出了错误的请求(缺少属性/错误的用户 ID 等),那么我需要检查收到的 HTTP 状态错误代码,并将相同的错误代码传递回我的调用 HTTP客户端,与此同时,我不想传回从图形服务收到的大量 json 错误详细信息,我只是想传回更简单的消息,例如找不到用户。
- 简化消息的逻辑方法是利用图形服务提供的已经内置的代码消息:
如下所示:"code" 属性足以向任何调用 HTTP 客户端说明情况,如果我需要进一步调查,那么我会检查日志:
{
"error": {
"code": "invalidRange",
"message": "Uploaded fragment overlaps with existing data.",
"innerError": {
"requestId": "request-id",
"date": "date-time"
}
}
}
我只是不知道如何通过 SDK 提取从图形服务调用中收到的 HTTP 状态代码,以及如何从 json 错误消息中获取单个属性正文,然后 return 这个 reduced/simplified 信息以正确和符合 ant 的方式返回到我自己的 HTTP 客户端。到目前为止我自己的项目代码如下:
我的用户服务WEBAPI控制器:
[HttpGet("{id}")]
public async Task<IActionResult> GetUserById(string id)
{
try
{
var user = await _repository.GetUserByIdAsync(id);
}
catch(Exception ex)
{
// What am I catching here? how do I get the HTTP Status Code that
was received from the call to the Graph Service in the event of an error?
// How do i extract the code error key/value from the json received from Graph Service?
}
// Finally, how do I return this captured info to the HTTP Client?
// Ignore this part, not sufficient for my needs.
//if (user == null) return BadRequest();
//if (user != null) return Ok(user);
//else return NotFound();
}
我的界面:
namespace MicrosoftGraph_API.Repository
{
public interface IGraphClientRepo
{
public Task<List<User>> GetAllUsersAsync();
public Task<User> GetUserByIdAsync(string id);
}
}
我的 Graph SDK 客户端 Class:
public class GraphSDKClientRepo : IGraphClientRepo
{
public readonly IUserServiceClient _IUserServiceClient;
public GraphSDKClientRepo(IUserServiceClient userServiceClient)
{
_IUserServiceClient = userServiceClient;
}
public async Task<User> GetUserByIdAsync(string id)
{
var graphClient = _IUserServiceClient.InitializeGraphClient();
// Get user by object ID
var result = await graphClient.Users[id]
.Request()
.Select(e => new
{
e.DisplayName,
e.Id,
e.Identities
})
.GetAsync();
return result;
}
}
如果您的调用遇到错误,SDK 将抛出 ServiceException
。此 class 包括您正在寻找的 属性:
/// <summary>
/// The HTTP status code from the response.
/// </summary>
public System.Net.HttpStatusCode StatusCode { get; }
因此您对 Graph 的调用将类似于:
public async Task<User> GetUserByIdAsync(string id)
{
var graphClient = _IUserServiceClient.InitializeGraphClient();
// Get user by object ID
try
{
return await graphClient
.Users[id]
.Request()
.Select("id,displayName,identities")
.GetAsync();
}
catch (ServiceException)
{
throw;
}
}
你的控制器代码看起来像
[HttpGet("{id}")]
public async Task<IActionResult> GetUserById(string id)
{
try
{
return this.Ok(await _repository.GetUserByIdAsync(id));
}
catch (ServiceException ex)
{
return this.StatusCode(se.StatusCode);
}
}
如果您需要根据 HTTP 状态以不同方式处理异常,您也可以这样做
[HttpGet("{id}")]
public async Task<IActionResult> GetUserById(string id)
{
try
{
return this.Ok(await _repository.GetUserByIdAsync(id));
}
catch (ServiceException e) when(e.StatusCode == System.Net.HttpStatusCode.NotFound)
{
//
// Do "Not Found" stuff
//
return this.StatusCode(e.StatusCode);
}
catch (ServiceException e) when(e.StatusCode == System.Net.HttpStatusCode.BadRequest)
{
//
// Do "Bad Request" stuff
//
return this.StatusCode(e.StatusCode);
}
catch (ServiceException e)
{
return this.StatusCode(e.StatusCode);
}
}
我目前正在我的 .NET Core 3.1 应用程序中测试 MS Graph .NET Core SDK 客户端。目的是提供我自己的本地 Web API 用户服务,该服务执行来自 Azure B2C AD 的 updates/changes/fetching 用户。
在我预期的解决方案中,我将拥有各种 HTTP 客户端微服务,它们会调用 API 命令到我的用户服务 SDK 客户端,而不是直接使用 REST 调用 Azure Graph API .
在我的用户服务应用程序中,我试图通过对发送到 Azure 的实际 SDK 命令使用 repository/interface 方法来保持整洁。这个相同的用户服务应用程序然后 return 使用我自己的 WEB API 将数据返回到我的本地 HTTP 客户端。将此用户服务应用想象成中间人效果。
下面的图片总结了环境:
这样做的目的是在更改或添加与图形服务一起使用的功能时减少工作量,即在我自己的本地应用程序之间提供一些通信标准化,并促进更好的关注点分离。此外,如果 MS 对图形进行更改 API,那么我只会更新用户服务应用程序,而不是对所有 HTTP 客户端应用程序进行代码更改。希望这是有道理的。
总之,进入正题!我提问的原因:
(请耐心等待,我是 REST 和使用接口的新手)
Graph SDK 客户端和 Azure 中的 Graph 服务 API 之间遇到的任何错误都将记录在我的用户服务应用程序中,即我将在第一时间捕获的大量 json 错误详细信息,但是我根本不需要将这种级别的详细信息传递回我所有的本地调用 HTTP 客户端。
我想要实现的是 identifying/capturing SDK 客户端和图形服务之间遇到的任何 HTTP 状态代码错误的方法,以及错误的一些基本细节,即短描述,只将这些较低级别的详细信息传递回我的本地 HTTP 客户端,即保持干净。
我正在努力了解如何在我的代码中执行此操作,特别是考虑到我同时使用一个界面使它变得更加复杂这一事实。 MS 文档确实提供了有关使用图形服务的预期错误代码列表的信息,但没有示例说明如何处理此信息以便将相关(但信息的较轻版本)传递回另一个来源。
示例场景:
- 本地 HTTP 客户端调用 [HttpGet] GetUserById 到我的用户服务 Web API
- 我的网站 API 然后使用 Graph SDK 客户端连接到 Azure B2C,获取数据并 return。
- 然后我的 WEB API 应该检查收到的信息,如果找到用户那么很好,return 用户到我调用的 HTTP 客户端。
- 如果找不到用户,或者可能发出了错误的请求(缺少属性/错误的用户 ID 等),那么我需要检查收到的 HTTP 状态错误代码,并将相同的错误代码传递回我的调用 HTTP客户端,与此同时,我不想传回从图形服务收到的大量 json 错误详细信息,我只是想传回更简单的消息,例如找不到用户。
- 简化消息的逻辑方法是利用图形服务提供的已经内置的代码消息:
如下所示:"code" 属性足以向任何调用 HTTP 客户端说明情况,如果我需要进一步调查,那么我会检查日志:
{
"error": {
"code": "invalidRange",
"message": "Uploaded fragment overlaps with existing data.",
"innerError": {
"requestId": "request-id",
"date": "date-time"
}
}
}
我只是不知道如何通过 SDK 提取从图形服务调用中收到的 HTTP 状态代码,以及如何从 json 错误消息中获取单个属性正文,然后 return 这个 reduced/simplified 信息以正确和符合 ant 的方式返回到我自己的 HTTP 客户端。到目前为止我自己的项目代码如下:
我的用户服务WEBAPI控制器:
[HttpGet("{id}")]
public async Task<IActionResult> GetUserById(string id)
{
try
{
var user = await _repository.GetUserByIdAsync(id);
}
catch(Exception ex)
{
// What am I catching here? how do I get the HTTP Status Code that
was received from the call to the Graph Service in the event of an error?
// How do i extract the code error key/value from the json received from Graph Service?
}
// Finally, how do I return this captured info to the HTTP Client?
// Ignore this part, not sufficient for my needs.
//if (user == null) return BadRequest();
//if (user != null) return Ok(user);
//else return NotFound();
}
我的界面:
namespace MicrosoftGraph_API.Repository
{
public interface IGraphClientRepo
{
public Task<List<User>> GetAllUsersAsync();
public Task<User> GetUserByIdAsync(string id);
}
}
我的 Graph SDK 客户端 Class:
public class GraphSDKClientRepo : IGraphClientRepo
{
public readonly IUserServiceClient _IUserServiceClient;
public GraphSDKClientRepo(IUserServiceClient userServiceClient)
{
_IUserServiceClient = userServiceClient;
}
public async Task<User> GetUserByIdAsync(string id)
{
var graphClient = _IUserServiceClient.InitializeGraphClient();
// Get user by object ID
var result = await graphClient.Users[id]
.Request()
.Select(e => new
{
e.DisplayName,
e.Id,
e.Identities
})
.GetAsync();
return result;
}
}
如果您的调用遇到错误,SDK 将抛出 ServiceException
。此 class 包括您正在寻找的 属性:
/// <summary>
/// The HTTP status code from the response.
/// </summary>
public System.Net.HttpStatusCode StatusCode { get; }
因此您对 Graph 的调用将类似于:
public async Task<User> GetUserByIdAsync(string id)
{
var graphClient = _IUserServiceClient.InitializeGraphClient();
// Get user by object ID
try
{
return await graphClient
.Users[id]
.Request()
.Select("id,displayName,identities")
.GetAsync();
}
catch (ServiceException)
{
throw;
}
}
你的控制器代码看起来像
[HttpGet("{id}")]
public async Task<IActionResult> GetUserById(string id)
{
try
{
return this.Ok(await _repository.GetUserByIdAsync(id));
}
catch (ServiceException ex)
{
return this.StatusCode(se.StatusCode);
}
}
如果您需要根据 HTTP 状态以不同方式处理异常,您也可以这样做
[HttpGet("{id}")]
public async Task<IActionResult> GetUserById(string id)
{
try
{
return this.Ok(await _repository.GetUserByIdAsync(id));
}
catch (ServiceException e) when(e.StatusCode == System.Net.HttpStatusCode.NotFound)
{
//
// Do "Not Found" stuff
//
return this.StatusCode(e.StatusCode);
}
catch (ServiceException e) when(e.StatusCode == System.Net.HttpStatusCode.BadRequest)
{
//
// Do "Bad Request" stuff
//
return this.StatusCode(e.StatusCode);
}
catch (ServiceException e)
{
return this.StatusCode(e.StatusCode);
}
}