WebApi添加了另一个Get方法



我有一个非常标准的WebApi,它可以做一些基本的CRUD操作。

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

这是我当前的文件夹控制器

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注释

但是没有成功

我讲对了吗?我如何在不添加另一个控制器的情况下做这样的事情?

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

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

有修补Http路由的方法来管理这个,但我不觉得这是Web API的设计工作方式,它强迫你遵循REST数据实体类型协议…否则,为什么不使用。net MVC控制器进行AJAX调用呢?

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

现在你的应用已经合理地组织好了,你可以考虑路由了。WebAPI现在支持属性路由。如果将这一行添加到WebApiConfig。寄存器——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"的任何get都将击中该动作。它还使调用的行为对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" }
);

如果你有其他有子资源的资源,你也可以把controllers作为一个变量留在那个自定义路由中,你仍然可以把这个动作放在一个单独的控制器中,这个控制器也会处理对"childFolders/{folderID}"路由的调用。

路由非常灵活。只要确保它的设计使api调用者和维护你的软件的人(包括你自己)都能一目了然。

WebAPI 2中的属性路由

一种方法是为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" }
        );

Actions是在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)
    {
    }

按如下方式设置路由:

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。

最新更新