【问题标题】:WebApi adding another Get MethodWebApi 添加另一个 Get 方法
【发布时间】:2013-05-16 15:41:05
【问题描述】:

我有一个非常标准的 WebApi,它执行一些基本的 CRUD 操作。

我正在尝试添加一些不同类型的查找,但不太确定应该如何完成。

这是我目前的 FoldersController

public class FoldersController : ApiBaseController
{
    //using ninject to pass the unit of work in
    public FoldersController(IApiUnitOfWork uow)
    {
        Uow = uow;
    }

    // GET api/folders
    [HttpGet]
    public IEnumerable<Folder> Get()
    {
        return Uow.Folders.GetAll();
    }

    // GET api/folders/5
    public Folder Get(int id)
    {
        return Uow.Folders.GetById(id);
    }

    // POST api/folders
    public HttpResponseMessage Post(Folder folder)
    {
        Uow.Folders.Add(folder);
        Uow.Commit();

        var response = Request.CreateResponse(HttpStatusCode.Created, folder);

        // Compose location header that tells how to get this Folder
        response.Headers.Location = new Uri(Url.Link(WebApiConfig.DefaultRoute, new { id = folder.Id }));

        return response;
    }

    // PUT api/folders
    public HttpResponseMessage Put(Folder folder)
    {
        Uow.Folders.Update(folder);
        Uow.Commit();
        return new HttpResponseMessage(HttpStatusCode.NoContent);
    }

    // DELETE api/folders/5
    public HttpResponseMessage Delete(int id)
    {
        Uow.Folders.Delete(id);
        Uow.Commit();

        return new HttpResponseMessage(HttpStatusCode.NoContent);
    }
}

我想做的是添加一个看起来像这样的方法

public IEnumerable<Folder> GetChildFolders(int folderID)
{
     return Uow.Folders.GetChildren(folderID);
}

因为我已经有了标准的 Get 方法,所以我不太确定该怎么做。

我最初以为我可以添加一条新路线..类似于

routes.MapHttpRoute(
        name: "ActionAndIdRoute",
        routeTemplate: "api/{controller}/{action}/{id}",
        defaults: null,
        constraints: new { id = @"^/d+$" } //only numbers for id
        );

只需在我的方法 [ActionName("GetChildren")] 中添加 ActionName 注释之类的东西

但这并没有成功。

我在正确的轨道上吗?如何在不添加另一个控制器的情况下执行此类操作?

【问题讨论】:

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


【解决方案1】:

你可能不喜欢这个答案,但我觉得它是正确的。 WebAPI 被设计为每个实体类型只有 5 个调用,GET(一项/列表项)、POST、PUT 和 DELETE。这允许使用 REST URL,例如 Folders/Get/5、Folders/Get 等。

现在,在您的场景中,您需要 ChildFolders,我可以理解它们不是不同的对象,但它们在 REST(ChildFolders/Get)等方面是不同的实体。我觉得这应该是另一个 WebAPI 控制器。

有一些方法可以修补 Http Routes 来管理这个,但我不认为 Web API 是这样设计的,它强制你遵循 REST 数据-by-entity-type 协议...否则为什么不仅仅使用 .NET MVC 控制器进行 AJAX 调用?

【讨论】:

  • 克里斯的回答非常好。但是,我想公开一个 API,它提供不同的数据集。假设对于仪表板,我的 DashboardController 应该有不同的 get 方法,它将为仪表板中的不同小部件提供数据。我的客户端代码将使用 AJAX 调用这些不同的 get 方法。在这里使用 Web API 有意义吗?
  • 我会说使用 WebAPI 非常有意义,是的!但是,您检查过 MVC 6 了吗?控制器和 WebAPI 是同一种,所以两者之间没有区别了。
  • 我将使用 MVC 控制器来创建 REST API,而不是使用 WebAPI 项目。
【解决方案2】:

正如 Chris 所说,WebAPI 背后的理念是遵循 REST 模式。考虑到这一点,由您决定您的域如何映射到该模式。如果子文件夹 文件夹并使用相同的内部逻辑,那么将 Get 放入您的 FoldersController 中可能非常有意义。如果没有,或者如果您想对子文件夹执行所有 REST 方法,则创建 ChildFoldersController 可能更有意义。

既然您的应用组织得很好,您可以考虑路由。 WebAPI 现在支持属性路由。如果您将此行添加到您的 WebApiConfig.Register -- config.MapHttpAttributeRoutes(); -- 像这样:

config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "{controller}/{id}",
    defaults: new { id = RouteParameter.Optional },
);

然后您可以将路线放在操作本身上:

[HttpGet]
[Route("folders/{folderID}/children")] // <-- notice the route here
public IEnumerable<Folder> GetChildFolders(int folderID)
{
     return Uow.Folders.GetChildren(folderID);
}

现在,您对路径“folders/{folderID}”的所有 REST 调用都将使用默认路由工作,而该路由上包含“/children”的任何获取都将执行该操作。它还使 API 的调用者非常清楚调用的行为。

您仍然可以使用正常的路由模式执行此操作:

// default route
config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

// folder children route
config.Routes.MapHttpRoute(
    name: "FolderChildrenApi",
    routeTemplate: "api/folders/{folderID}/children",
    defaults: new { controller = "Folders", action = "GetChildFolders" }
);

如果您有其他有子资源的资源,您也可以将控制器作为变量保留在该自定义路由中,并且您仍然可以将该操作放在一个单独的控制器中,该控制器还处理对路由“childFolders/{folderID}”的调用.

路由非常灵活。只需确保对其进行设计,使其对 api 调用者和维护您的软件的人员(包括您)一目了然。

这是一些属性路由信息:Attribute Routing in WebAPI 2

【讨论】:

    【解决方案3】:

    一种方法是编写特定于GetChildFolders 操作的新路由。

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
    
            config.Routes.MapHttpRoute(
                name: "DefaultApi-GetChildFolders",
                routeTemplate: "api/{controller}/GetChildFolders/{id}",
                defaults: new { action = "GetChildFolders" }
            );
    

    【讨论】:

    • 这仍然不起作用,因为我有 2 个具有相同参数的 Get 方法。当我将 GetChildFolders 发送到该操作时,它会起作用,但是当我尝试点击 api/folders/5 时,它会混淆为使用什么 GetMethod。不知道在这里做什么
    【解决方案4】:

    操作是在 WebApi 控制器中拥有两个 Get 方法的好方法。
    以下是我为了执行不同的操作而执行的操作,以及一些操作的可选额外 ID 参数:

    WebApiConfig.cs里面我有以下内容:

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}/{action}/{actionId}",
                defaults: new
                {
                    id = RouteParameter.Optional,
                    actionId = RouteParameter.Optional,
                    action = "DefaultAction"
                }
           );
    

    在控制器中:

        [ActionName("DefaultAction")]
        public AdGetResponse Get(int id)
        {
            ...
        }
    
        [ActionName("AnotherAction")]
        public HttpResponseMessage GetAnotherAction(int id, int actionId)
        {
        }
    

    【讨论】:

    • 请注意[ActionName("DefaultAction")] 注释不是强制性的。
    【解决方案5】:

    如下制作路线:

    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional },
        constraints: new { id = @"\d*" }
    );
    
    config.Routes.MapHttpRoute(
        name: "DefaultApiPlusActionAndFolderid",
        routeTemplate: "api/{controller}/{action}/{folderID}",
        defaults: null,
        constraints: new { action = @"[a-zA-Z]+", folderID = @"\d+" }
    );
    

    然后你可以有诸如

    之类的方法
    [ActionName("GetChildren")]
    public IEnumerable<Folder> GetChildFolders(int folderID)
    {
         return Uow.Folders.GetChildren(folderID);
    }
    

    您可以使用/api/folders/getchildren/123 致电。 只要确保方法中的参数值为folderID而不是id即可。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-19
      相关资源
      最近更新 更多