【问题标题】:Web Api interaction between multiple controller endpoints多个控制器端点之间的 Web Api 交互
【发布时间】:2017-06-23 09:21:43
【问题描述】:

我正在寻找一些建设性的建议。我正在尝试映射多个控制器端点如何相互交互,这样我最终不会在多个控制器中编写相同的代码。请允许我用一个简单的看板示例来说明。

我在这里考虑两个(或三个)控制器:BoardController、CardController、SubCardController(未定)。现在让我们忽略卡片可以组织成列表的事实,因为我们将有一个 ListController 位于 Board 和 Card 控制器之间。

板控制器:

public class BoardController : ApiController {
    // among basic CRUD methods I have a method that returns single board
    // implementation #1
    // /api/board/getbyid?id=123
    public HttpResponseMessage GetById(int id) {
        var board = dataStore.GetById(id);
        if (board == null)
            return Request.CreateResponse(HttpStatusCode.NotFound);

        return Request.CreateResponse(HttpStatusCode.OK, board);
    }

    // implementation #2
    // /api/board/getbyid?id=123
    public HttpResponseMessage GetById(int id) {
        var board = GetById(id);
        if (board == null)
            return Request.CreateResponse(HttpStatusCode.NotFound);

        return Request.CreateResponse(HttpStatusCode.OK, board);
    }

    [NonAction]
    // normally methods like this one I declare as private
    // but in this case CardController needs to call this method as well
    public static Board GetById(int id) {
        return dataStore.GetById(id);
    }
}

请允许我澄清dataStore 是对另一个单独负责数据访问的控制器的引用。

卡控制器:

public class CardController : ApiController {
    // among basic CRUD methods I might want to call BoardControllers GetById(id) method (to verify if board exists for example)
    // implementation #1
    public HttpResponseMessage GetAll(int boardId) {
        // call BoardController.GetById(id) by issueing HTTP request
        HttpRequestMessage _request = new HttpRequestMessage(HttpMethod.Get, requestUri);
        HttpResponseMessage _response = Client.SendAsync(_request).Result;
        Board board = _response.Content.ReadAsAsync<Board>().Result;
        if (board == null /* || more condition(s) */)
            return Request.CreateResponse(HttpStatusCode.NotFound);

        // get cards and return
    }

    // implementation #2
    public HttpResponseMessage GetAll(int boardId) {
        // call BoardController.GetById(id) static method
        var board = _boardCtrl.GetById(boardId);
        if (board == null /* || more condition(s) */)
            return Request.CreateResponse(HttpStatusCode.NotFound);

        // get cards and return
    }
}

这是我的两个替代解决方案。一方面实现#1 不需要额外的静态方法,但另一方面,我认为从一个控制器操作发出 HTTP 请求以访问另一个控制器操作是不好的。

让我知道你们的想法。特别是如果有更整洁的选择。

【问题讨论】:

  • 创建一个服务并将其注入您的控制器以访问您想要的模型。尽量让你的控制器保持精简。不需要控制器中的所有逻辑。使用注入服务方法可以在需要的地方重用它。
  • @Nkosi 这是一个快速响应。我想我明白你在说什么,但我不知道如何实现它。你能把我链接到一个很好的例子吗?干杯。
  • @Nkosi 我想我找到了你想要的东西:asp.net/web-api/overview/advanced/dependency-injection

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


【解决方案1】:

创建服务并根据需要将它们注入到依赖的控制器中。控制器应该保持精简,而不是专注于实现问题。

public interface IBoardService {
    Board GetById(int id);
    //...other code removed for brevity
}

public class BoardController : ApiController {
    readonly IBoardService dataSource;
    public BoardController(IBoardService dataSource) {
        this.dataSource = dataSource;
    }

    // /api/board/getbyid?id=123
    public IHttpActionResult GetById(int id) {
        var board = dataSource.GetById(id);    
        return board == null ? NotFound() : OK(board);
    }
}

如果需要也可以复用板服务注入CardController

public class CardController : ApiController {
    readonly IBoardService boardService;
    readonly ICardService cardService;

    public CardController(ICardService cardService, IBoardService boardService) {
        this.cardService = cardService;
        this.boardService = boardService;
    }   

    public IHttpActionResult GetAll(int boardId) {
        var board = boardService.GetById(boardId);
        if (board == null /* || more condition(s) */)
            return NotFound();

        // get cards from card service and return
    }

}

使用注入服务方法可以在需要的地方重用它。

另外,请尽量保持控制器的精简。

不需要控制器中的所有逻辑。

卡服务的实现可以依赖于板服务,并且只公开一个GetAllByBoardId(int id),这将减少CardController 中的逻辑,就像您的示例一样。

【讨论】:

  • 非常整洁。谢谢你的例子。
  • 使用单一职责认为控制器的唯一目的是将数据(模型)传递给视图,而不是实现逻辑。它应该将控制权(请原谅双关语)委托(反转)给服务。
  • 问:您将板接口命名为 IBoardService。以Service 后缀命名这些类是否普遍被接受?或者我可以用我选择的任何后缀命名我的名字吗? IBoardController 或 IBoardInfo 等?
  • 称它为控制器会产生误导。 `您选择的服务、存储库、上下文。一旦它正确地传达了它的目的并避免混淆。
猜你喜欢
  • 1970-01-01
  • 2012-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-19
  • 1970-01-01
相关资源
最近更新 更多