何时返回IHttpActionResult和Object?

30

在使用ASP.NET Web API的示例中,我看到了两种不同的方法用于将数据返回给调用jQuery函数。第一种方法返回一个Client类型的对象,但我不确定第二种方法返回的是什么。

方法 #1(返回Client对象)

public IEnumerable<Client> GetAllClients()
{
     using (var context = new PQRSModel.PQRSEntities())
     {
       context.Configuration.ProxyCreationEnabled = false; 
       var query = context.Clients.OrderBy(c = c.OrgName);
       var customers = query.ToList();
       return customers;
     }
}

第二种方法IHttpActionResult提供了什么好处?)

public IHttpActionResult GetClient(int clientId)
{
     using (var context = new PQRSModel.PQRSEntities())
     {
       context.Configuration.ProxyCreationEnabled = false;
       var client = context.Clients.FirstOrDefault(c = c.ID == clientId);
       if (client == null)
       {
         return NotFound();
       }
       return Ok(client);
     }
}

如果第二种方法发现单个对象,是否也可能返回Client对象类型?

3个回答

21

返回IHttpActionResult提供了很好的关注点分离

您的控制器可以专注于以最合理的方式响应请求(状态代码、错误消息等)。另一层(服务层)可以专注于实际检索和转换业务数据。

副作用是,您的控制器方法变得更容易进行单元测试。考虑以下简单示例:

public class MyController : ApiController
{
    //or better yet, dependency-inject this
    SomeService _service = new SomeService();

    public IHttpActionResult Get(int id)
    {
         if (id < 0)
             return BadRequest("Some error message");

         var data = _service.GetData(id);

         if (data == null)
            return NotFound();

         return Ok(data);
    }
}

这种方法的逻辑不仅仅通过阅读就可以理解,而且您现在可以更轻松、更自然地测试逻辑,例如(使用 NUnit 语法):


Not only is this method's logic understandable just by reading it, but you could now test the logic more easily and naturally, something like (using NUnit syntax):
[TestFixture]
public class MyControllerTests
{    
    [Test]
    public void Get_WithIdLessThan0_ReturnsBadRequest()
    {
        var controller = new MyController();
        int id = -1;

        IHttpActionResult actionResult = controller.Get(id);

        Assert.IsInstanceOf<BadRequestErrorMessageResult>(actionResult);
    }
}

同样地,您可以模拟 Service 层并测试在将已知的 id 参数传递给控制器时会发生什么等情况。

这是一篇关于在 Web Api 中单元测试控制器的好文章:Unit Testing Controllers in Web Api


4
这更像是为创建服务层辩护,而不是为返回 IHttpActionResult 辩护。一旦你正确地分离了关注点,测试该操作是否引发特定的异常类型就像测试它是否返回给定的操作结果类型一样简单。 - StriplingWarrior
这正是我将那个测试用例作为我的“示例”的原因 - 在答案中编码非常简单,但阐明了这个想法。如果您认为我的答案不够充分,请添加您自己的答案。 - kaveman

10
第二种方法允许您仅返回状态代码(例如示例中的404),流文件内容和其他类型的非对象内容。

谢谢你的回答。使用'IHttpActionResult'仍然可以返回对象吗?或者当您想要返回单个对象或对象集合时,应该使用第一种方法?我问这个问题的原因是我正在阅读的示例(Microsoft文章-http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api)使用返回'IHttpActionResult'的方法来返回一个对象。我试图调整代码以返回一个Entity Framework对象。 - webworm
12
直接从方法中返回对象有几个优点。方法签名会更清晰,方法也更容易测试。当找不到对象时或输入数据错误时,可以通过抛出类型为 HttpResponseException 的异常来返回状态代码,而不是由 IHttpActionResult 返回。 - Dmitry S.
但异常是昂贵的,你的方法签名实际上与你的API没有任何关系(当没有其他信息时,API探索器会从它们那里收集信息)。Web API中不可避免的短板之一是路由概念、HTTP层和OOP的结合,而这种结合并不总是有意义的。然而,仍然可以在使用IHttpActionResult的同时解决这个问题。这就是 ResponseTypeAttribute - Dave
2
异常只是相对于某些事情来说比较昂贵。当您考虑到将给定请求连接到控制器代码所需的所有反射和I/O,然后将结果转换为响应时,抛出和捕获异常的额外开销是相当合理的。 - StriplingWarrior
2
从语义角度来看,我仍然不认为大多数状态代码是“异常”。例如,404 应该是可以预期的。但我同意对象提供了更具体的 API。就个人而言,我认为通用的方式会更好地描述 API,比如 IHttpActionResult<IEnumerable<Client>> - aaaaaa

1

使用 IHttpActionResult 的理由是它允许您从操作返回不同的状态码。

让我们考虑下面的示例:

public Client GetClient(int id)
{
   if (id <= 0)
   {
      throw new BadRequestException("Invalid Id"); // 400
   }

   var client = _myDbService.GetClient(id);
   if (client == null)
   {
      throw new NotFoundException("Client not found"); // 404
   }
   
   return Ok(client); // 200
}

我们唯一能够返回400和404的方式是通过抛出异常。

请注意,上面的代码不包括异常处理逻辑。您需要编写逻辑,在异常处理程序中返回所需的状态代码以及任何其他错误消息(如果有)。

如果我们将返回类型更改为IHttpActionResult,那么我们可以在不抛出任何异常的情况下处理失败场景:

[SwaggerResponse(HttpStatusCode.OK, "Client found", Type = typeof(Client))]
[SwaggerResponse(HttpStatusCode.BadRequest, "Invalid id")]
[SwaggerResponse(HttpStatusCode.NotFound, "Client not found")]
public IHttpActionResult GetClient(int id)
{
   if (id <= 0)
   {
      return BadRequest("Invalid id");
   }

   var client = _myDbService.GetClient(id);
   if (client == null)
   {
      return NotFound();   
   }
   
   return Ok(client);
}

请注意,您可以使用SwaggerResponse属性为每个状态代码指定返回类型。

我不知道哪种方法更好,但似乎微软更喜欢第二个选项

这种方法的一个缺点[第一个选项]是你不能直接返回错误代码,例如404。但是,你可以抛出HttpResponseException来处理错误代码。


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