【问题标题】:RestSharp How to deal with different JSON response formats for same queryRestSharp 如何处理相同查询的不同 JSON 响应格式
【发布时间】:2017-08-02 06:56:34
【问题描述】:

我已经为此苦苦挣扎了一段时间,我认为是时候寻求帮助了。

我正在使用 RestSharp 连接到我的本地 PHP REST API。我返回api结果的格式如下(JSON):

{
"estado": "login_correcto",
"content": {
   "id": 1,
   "nombreusuario": "daniaguado",
   "contrasena": "qwerty",
   "email": "daniel@zadecon.com",
   "nivelacceso": 5,
    "created_at": "2017-08-01 10:31:16",
    "updated_at": "-0001-11-30 00:00:00"
    }
}

为此,我创建了一个自定义Usuario 类(用户)和一个自定义ResponseUsuario,如下所示:

class Usuario
{
    public int id { get; set; }
    public string nombreusuario { get; set; }
    public string contrasena { get; set; }
    public string email { get; set; }
    public int nivelacceso { get; set; }
    public string created_at { get; set; }
    public string updated_at { get; set; }
}

然后是ResponseUsuario:

class ResponseUsuario
{
    public string estado { get; set; }
    public Usuario content { get; set; }
}

当响应正常(202)并且用户存在时,响应被解析正常。

但是,当登录不正确时,在“内容”中我返回的是一条消息,而不是用户:

{
 "estado": "login_incorrecto",
 "content": "La contraseña es incorrecta / Password incorrect"
}

所以,如果我在那里使用ResponseUsuario,queryResult.Data 为空,因为它无法将内容映射到Usuario 类。事实是,如果我不使用 ResponseUsuario 而是使用 Response 类,其中内容变量是类型对象,我无法将其转换为 Usuario 并且我无法处理它。

这是更通用的 Response 类,我知道我应该将其用于所有查询:

class Response
{
    public string estado { get; set; }
    public object content { get; set; }
}

最后,这是我的 RestSharp 查询:

ApiClient api = new ApiClient(); // Just create the client with some common parameters

var request = new RestRequest("usuarios/login", RestSharp.Method.POST);

request.AddParameter("nombreusuario", textbox_usuario.Text);
request.AddParameter("contrasena", textbox_contrasena.Text);

var queryResult = api.cliente.Execute<ResponseUsuario>(request);

if (queryResult.StatusCode == System.Net.HttpStatusCode.OK)
{
    Usuario u = (Usuario) queryResult.Data.content; // Gives error when using Execute<Response> (object content)
    String s = queryResult.Data.estado;

    Console.Out.WriteLineAsync($"OK - {u.nombreusuario} - {u.email} - Acceso: {u.nivelacceso}");
} 
else
{
    Console.Out.WriteLineAsync($"Query Failed: {queryResult.StatusDescription}, estado: {queryResult.Data.estado}"); // Giving error when using Execute<ResponseUsuario>
}

如何将演员表固定为 Usuario(或内容的匹配类)?我认为 Response 类应该是我对 API 的所有查询所共有的,然后将 Content 部分转换为它的适当类,但我不知道该怎么做。

【问题讨论】:

    标签: c# json wpf rest restsharp


    【解决方案1】:

    聚会真的迟到了。当 API 在错误与 200 时返回不同的结构时,我遇到了类似的问题。我放弃了 RestSharp 的类型化调用并显式反序列化有效负载。

    var response = RestClient.Execute<IdamValidationResultApiModel>(request);
    if (response.IsSuccessful)
       var t1 = JsonConvert.DeserializeObject<FirstType>(response.Content);
    else
       var t2 = JsonConvert.DeserializeObject<SecondType>(response.Content);
    

    【讨论】:

      【解决方案2】:

      检查 (queryResult.Data.content 是否为 Usuario) 并仅在其为真时进行转换

      否则转换为字符串。

      【讨论】:

      • 这对我不起作用。我知道它什么时候是 Usuario,因为它正在返回 202 状态。问题是,当使用Execute&lt;Response&gt;(记住,内容是类型对象)时,queryResult.Data.content 是System.Collections.Generic.Dictionary,并且不能转换为Usuario,即使我知道它是Usuario
      • 如果响应包含字典中的值,这意味着您可以直接转换,为什么不编写从字典对象到用户对象的解析器?
      • 那么我需要为我收到的每个响应创建一个解析器吗?我计划为每个响应内容设置不同的对象。我需要为每个查询创建一个解析器吗?
      【解决方案3】:

      每个响应是否返回不同的 Http 状态码?我猜你正在从你的休息 api 返回一个 401。如果是这种情况:

      然后您可以使用开关来处理其他状态代码。

          HttpStatusCode statusCode = queryResult.StatusCode;
          int numericStatusCode = (int)statusCode;
      
          switch (numericStatusCode)
          {
              case 202:
                  dynamic content = queryResult.Data.content;
                  Usuario u = content.content; 
                  String s = queryResult.Data.estado;
                  Console.Out.WriteLineAsync($"OK - {u.nombreusuario} - {u.email} - Acceso: {u.nivelacceso}");
                  break;
              case 401:
                  //Deal with the failed login
                  break;
          }
      

      最后,您收到错误的原因是您尝试将类型为 UsarioResponse 的响应对象转换为 Usario。您需要导航 json 以找到 content.content(如上所示)

      【讨论】:

      • 那不行。我实际上是通过 statusCode 进行分离的。 queryResult.Data.content 实际上是内容,而不是 content.content。它可以被命名为土豆,它会是queryResult.Data.potato。问题不存在。问题仍然存在,无法从 Dictionary 投射到 Usuario。最后我需要制作一个自定义解析器。
      【解决方案4】:

      我终于解决了这个问题,从字典对象生成自定义反射到类,也就是最后一个自定义解析器。

      我新建了一个类Tools并添加了转换函数:

      public static T CastToObject<T>(IDictionary<string, object> dict) where T : class
      {
         Type type = typeof(T);
         T result = (T)Activator.CreateInstance(type);
         foreach (var item in dict)
         {
             type.GetProperty(item.Key).SetValue(result, item.Value, null);
         }
         return result;
       }
      

      在 Api Query 上,根据状态码添加了一个 switch-case:

      var queryResult = api.cliente.Execute<Response>(request);
      
      switch(queryResult.StatusCode)
      {
          case System.Net.HttpStatusCode.OK:
              Usuario u = Tools.CastToObject<Usuario>((IDictionary<string, object>)queryResult.Data.content);
              String status = queryResult.Data.estado;
              Console.Out.WriteLineAsync($"OK - {u.nombreusuario} - {u.email} - Acceso: {u.nivelacceso}");
              break;
          case System.Net.HttpStatusCode.NotFound:
                  Console.Out.WriteLineAsync($"Login Failed: User does not exists");
                  break;
          case System.Net.HttpStatusCode.Unauthorized:
                  Console.Out.WriteLineAsync($"Login Failed: Incorrect Password");
                  break;
          default:
                  Console.Out.WriteLineAsync($"Error when connecting to the API");
                  break;
      }
      

      此外,我还需要处理包含content 字段内列表的响应,但我认为该过程将类似于此过程:为content 字段创建自定义解析器并将其转换为我的班级.

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-28
        • 2015-10-18
        • 1970-01-01
        • 2020-08-27
        • 1970-01-01
        相关资源
        最近更新 更多