如何从MVC控制器操作方法调用WebApi方法?

23

如何从 MVC 控制器操作方法调用 Web API 方法?

下面是我正在尝试的,这是我的控制器方法:

public ActionResult ProductDetail(long id)
{
    using (var client = new HttpClient())
    {
        var productDetailUrl = Url.RouteUrl(
            "DefaultApi",
            new { httproute = "", controller = "ProductDetails", id = id },
            Request.Url.Scheme
        );
        var model = client
                    .GetAsync(productDetailUrl)
                    .Result
                    .Content.ReadAsAsync<ProductItems>().Result;

        return View(model);
    }
}

我的Web API方法:

private ProductEntities products = new ProductEntities();

public IEnumerable<ProductItems> GetProductDetail(int ID)
{            
   var produc = products.ExecuteStoreQuery<ProductItems>(
                    "GetProductDetail @ProductID ", 
                    new SqlParameter("@ProductID", ID)); 

   return produc;
}

在我的MVC动作方法中返回数据后,当我尝试操作var model时出现错误:

"Newtonsoft.Json.JsonSerializationException: 无法将JSON数组“(即[1,2,3])”反序列化为类型“ProductDetails.Models.ProductItems”。 序列化的类型必须是数组或实现集合接口,如IEnumerableICollectionIList。如果要强制JSON数组进行反序列化,请向类型添加JsonArrayAttribute。第1行,第1个位置。"

有没有人能帮我解决这个问题,建议另一种更好的方法,或者纠正我在任何地方做错了?我必须在我的视图中显示返回的数据,数据应该返回到我的控制器。


我会使用ProductItem而不是ProductItems作为类/对象名称。在我的看法中,使用"var"这个词会阻碍这些问题的解决。 - granadaCoder
2个回答

15

根据异常提示“Cannot deserialize JSON array ...”,你的问题在接收端,例如反序列化不在Web API方法中而是在序列化过程中。

而你的问题是尝试将JSON反序列化为错误的类型,因为你的Web API方法类似于:

public IEnumerable<ProductItems> GetProductDetail(int ID)
所以它返回的是 IEnumerable<ProductItems> (例如一个 ProductItems 数组),但在你的控制器操作中,你尝试将响应反序列化为一个ProductItems,使用调用 ReadAsAsync<ProductItems>()
所以改变你的ReadAsAsync来反序列化为一个ProductItems数组,它应该可以工作:
var model = client
               .GetAsync(productDetailUrl)
               .Result
               .Content.ReadAsAsync<ProductItems[]>().Result;

我收到了一个异常,说传递到字典中的模型项类型为“ProductDetails.Models.ProductItems[]”,但是该字典需要一个类型为“ProductDetails.Models.ProductItems”的模型项。 - SoftwareNerd
现在问题在于您的视图 ProductDetail.cshtml,我猜您在其中使用了 @model ProductDetails.Models.ProductItems。如果您的 Web API 方法只返回一个 ProductItems,则可以在控制器中编写 return View(model[0]);。或者,更改您的 Web API 方法,以仅返回 ProductItems 而不是 IEnumerable<ProductItems>... - nemesv

8

当你的ASP.NET网站与Web API控制器一起托管时,你可以调用Web API作为普通的类方法来避免与HTTP请求相关的任何延迟。

问题不在于Web API调用。问题在于结果反序列化,这来自于ExecuteStoreQuery,请确保其中有可序列化的集合。为了确保,你可以尝试在返回之前将此方法的结果转换为列表并从web api方法返回可序列化的列表,然后定位问题。

更新:所以,问题的根源在于ExecuteStoreQuery方法的返回值。它返回ObjectResult。实际上,它实现了IEnumerable,但它是从EF返回的,在某些情况下,对象之间存在例如循环依赖关系,Json解串器无法解决。

要解决问题,只需将ObjectResult明确地转换为例如List:

public IEnumerable<ProductItems> GetProductDetail(int ID)
{            
    var products = products.ExecuteStoreQuery<ProductItems>(
              "GetProductDetail @ProductID ",     
              new SqlParameter("@ProductID", ID)); 
    return products.ToList<ProductItems>();
}

当您的对象集合来自EF且存在循环依赖关系时,您可以在Json.NET中关闭它。同时,请检查您的客户端反序列化。
var model = client.GetAsync(productDetailUrl).Result
                .Content.ReadAsAsync<List<ProductItems>>().Result;

但是,当您将ASP.NET MVC网站和ASP.NET Web API托管在一个项目中时,您不需要使用HTTP请求和序列化/反序列化等所有内容,只需像普通的类方法一样调用您的方法即可。


+1 是因为你指出了问题,并且提到可以实例化控制器并调用方法。 - Dan Harris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接