首页 > 解决方案 > Microsoft Graph 错误响应 - 如何从 JSON 中提取 HTTP 状态代码和内部错误代码?

问题描述

我目前正在我的 .NET Core 3.1 应用程序中测试 MS Graph .NET Core SDK 客户端。目的是提供我自己的本地 Web API 用户服务,该服务从 Azure B2C AD 执行更新/更改/获取用户。

在我的预期解决方案中,我将拥有各种 HTTP 客户端微服务,它们将调用 API 命令到我的用户服务 SDK 客户端,而不是直接使用 REST 调用 Azure Graph API。

在我的用户服务应用程序中,我试图通过对发送到 Azure 的实际 SDK 命令使用存储库/接口方法来保持清洁。然后,同一用户服务应用程序使用我自己的 WEB API 将数据返回给我的本地 HTTP 客户端。将这个用户服务应用想象成中间人效应。

下图总结环境:

在此处输入图像描述

这样做的目的是减少对 Graph Service 使用的功能进行更改或添加时的工作,即在我自己的本地应用程序之间提供一些通信标准化,并促进更好的关注点分离。此外,如果 MS 对 Graph API 进行了更改,那么我只是在更新用户服务应用程序,而不是在我的所有 HTTP 客户端应用程序中进行代码更改。希望这是有道理的。

无论如何,现在进入重点!我的问题的原因:

(请耐心等待,我是 REST 和使用接口的新手)

Graph SDK 客户端和 Azure 中的 Graph 服务 API 之间遇到的任何错误都将记录在我的用户服务应用程序中,即我将在第一时间捕获的大量 json 错误详细信息,但是我根本不需要通过这个级别详细信息返回给我所有的本地调用 HTTP 客户端。

我想要实现的是一种识别/捕获在 SDK 客户端和图形服务之间遇到的任何 HTTP 状态代码错误的方法,以及错误的一些基本细节,即简短描述并且只通过这些较低级别将详细信息返回给我的本地 HTTP 客户端,即保持清洁。

我正在努力了解如何在我的代码中执行此操作,特别是考虑到我同时使用接口的事实使它变得更加复杂。MS 文档确实提供了有关使用图形服务的预期错误代码列表的信息,但是没有示例说明如何处理此信息以便将相关(但更轻的信息版本)传回给另一个来源。

示例场景:

  1. 本地 HTTP 客户端向我的用户服务 Web API 调用 [HttpGet] GetUserById
  2. 然后,我的 Web API 使用 Graph SDK 客户端与 Azure B2C 联系,获取数据并返回。
  3. 然后,我的 WEB API 应该检查收到的信息,如果发现用户很好,则将用户返回给我的调用 HTTP 客户端。
  4. 如果找不到用户,或者可能发出了错误的请求(缺少属性/错误的用户 ID 等),那么我需要检查收到的 HTTP 状态错误代码并将相同的错误代码传递回我的调用 HTTP 客户端,同时时间,而不是传回从 Graph Service 收到的大量 json 错误详细信息,我只想传回更简化的消息,例如找不到用户。
  5. 简化消息的逻辑方法是利用 Graph Service 提供的已经烘焙的代码消息:

如下所示:“代码”属性足以向任何调用 HTTP 客户端解释情况,如果我需要进一步调查,那么我会检查日志:

 {
   "error": {
     "code": "invalidRange",
     "message": "Uploaded fragment overlaps with existing data.",
     "innerError": {
       "requestId": "request-id",
       "date": "date-time"
     }
   }
 }

我只是无法弄清楚如何提取从通过 SDK 对图形服务的调用接收到的 HTTP 状态代码,以及如何从正文中的 json 错误消息中获取单个属性,然后返回这个简化/简化的以正确和符合的方式将信息返回给我自己的 HTTP 客户端。到目前为止,我自己的项目代码如下:

我的用户服务 WEB API 控制器:

    [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 客户端类:

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;
    }
}

标签: c#asp.net-coremicrosoft-graph-apimicrosoft-graph-sdks

解决方案


如果您的调用遇到错误,SDK 将抛出一个ServiceException. 此类包括您要查找的属性:

/// <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);
    }
}

推荐阅读