将Web API调用的结果存储到Generic类型类中

时间:2014-02-19 15:44:34

标签: c# c#-4.0 asp.net-web-api

我有这个简单的模型类,其定义如下:

public class CountryLanguage
{
    public string Name { get; set; }
    public string ShortName { get; set; }
    public string Description { get; set; }
}

我有这个Web API,它返回一个IEnumerable的CountryLanguage:

[HttpGet]
public IEnumerable<CountryLanguage> Get()
{        
    List<CountryLanguage> list = new List<CountryLanguage>();
    list.Add(new CountryLanguage());
    list.Add(new CountryLanguage());
    return list;
}

我有这个类,我想存储Web API调用的结果:

public class ResponseResult<T>
{
    public HttpStatusCode StatusCode { get; set; }
    public string Message { get; set; }
    public T Payload { get; set; }
}

最后,这是调用Web API的代码:

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, actionToCallWithParameters);

var response = httpClient.SendAsync(request).Result;

ResponseResult<T> responseResult = new ResponseResult<T>();
responseResult.StatusCode = response.StatusCode;
responseResult.Message = response.Content.ReadAsStringAsync().Result;
responseResult.Payload = response.Content.ReadAsAsync<T>().Result;                

return responseResult;

如果Web API返回CountryLanguage对象,我没有将此对象存储到我的泛型类型属性有效内容中的问题。

但是如果Web API返回IEnumerable,我会收到此错误:

  

无法将当前JSON数组(例如[1,2,3])反序列化为类型“CountryLanguage”,因为该类型需要JSON对象(例如{\“name \”:\“value \”})才能正确反序列化。要修复此错误,请将JSON更改为JSON对象(例如{\“name \”:\“value \”})或将反序列化类型更改为数组或实现集合接口的类型(例如ICollection,IList)像可以从JSON数组反序列化的List。 JsonArrayAttribute也可以添加到类型中,以强制它从JSON数组反序列化。

我的问题是:是否可以“规范化”此代码,以便可以将对象或IEnumerable存储到T类型的有效负载属性中?

3 个答案:

答案 0 :(得分:1)

通过此更改,我能够解决问题但我不认为它完全回答了您的问题。我将在下面给出你的答案的解决方案,但这是我为解决运行时问题而添加的内容,它会返回你的列表。

var responseResult = new ResponseResult<IEnumerable<CountryLanguage>>
{
    StatusCode = response.StatusCode,
    Message = response.Content.ReadAsStringAsync().Result,
    Payload = response.Content.ReadAsAsync<IEnumerable<CountryLanguage>>().Result
 };

基本上只需将T转换为IEnumerable,如果在类或方法中将T定义为CountryLanguage,则可以将其设置为IEnumerable,但是您的代码不会显示完整的类或方法。

现在回到您的另一个问题,从一个通用方法返回对象或对象列表。第一个响应是正确的,你只需要返回对象和box / unbox并使用反射和其他非常令人不快的代码,这些代码可能被封装但仍然有点令人讨厌。因此,如果您的目标是将此返回的有效负载用于CountryLanguage以外的其他内容,则可以尝试以下操作。创建一个名为PayloadWrapperthat的新类,包含两个属性,如:

public class PayloadWrapper<T>
{
    public T Item { get; set; }
    public IEnumerable<T> Items { get; set; }
}

然后你可以在你的webapi和客户端设置on或另一个,检查设置了哪个。

public PayloadWrapper<CountryLanguage> Get()
{
    var languages = new List<CountryLanguage> {new CountryLanguage(), new CountryLanguage()};

    var payload = new PayloadWrapper<CountryLanguage>();
    payload.Items = languages;
    payload.Item = null;
    return payload;
}

在客户端:

var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:34213/api/values/");
 var httpClient = new HttpClient();
 var response = httpClient.SendAsync(request).Result;

  var responseResult = new ResponseResult<PayloadWrapper<CountryLanguage>>
  {
       StatusCode = response.StatusCode,
       Message = response.Content.ReadAsStringAsync().Result,
       Payload = response.Content.ReadAsAsync<PayloadWrapper<CountryLanguage>>().Result
   };

您的Payload现在是一个包含一个项目或同一项目列表的单个对象。可用于发送符合此模式的任何内容。

答案 1 :(得分:0)

其实你的逻辑不对。如果在运行时您的T类型充当“CountryLanguage”类型,则您的Payload属性将充当CountryLanguage属性。这就是当您尝试将IEnumerable值分配给属性时抛出错误的原因,因为您将IEnumerable对象分配给不可枚举的属性。
基本上,在运行时,系统正在这样做:

CountryLanguage Payload = IEnumerable<CountryLanguage>(variable);

这显然会引发错误。

答案 2 :(得分:0)

好的,可以将IEnumerable存储到泛型类型T的变量中。

如果我希望返回的数据是一个对象,我需要进行的调用是:

ResponseResult<CountryLanguage> response = serviceConnector.GetData <CountryLanguage>(actionToCall);

如果我期待来自通话的IEnumerable:

ResponseResult<IEnumerable<CountryLanguage>> response =
                serviceConnector.GetData<IEnumerable<CountryLanguage>>(actionToCall);

所以我的错误是我在调用代码时没有指定IEnumerable类型。