动态Web API层

返回总目录

本篇目录

构建动态Web API控制器

ABP可以自动地为应用层生成Web API 层。比如说我们有一个应用层如下所示:

public interface ITaskAppService : IApplicationService
{
    GetTasksOutput GetTasks(GetTasksInput input);
    void UpdateTask(UpdateTaskInput input);
    void CreateTask(CreateTaskInput input);
}

我们想把这个服务作为Web API控制器暴露给客户端。ABP只需要一行配置就可以为该应用服务创建一个Web API控制器:

DynamicApiControllerBuilder.For<ITaskAppService>("tasksystem/task").Build();

OK了!在地址为'/api/services/tasksystem/task'的地方就创建了一个API控制器,现在客户端可以使用该应用服务的所有方法。这个配置应该在模块的Initlize方法中完成。

我们使用一个API控制器封装的ITaskAppService是一个应用服务。使用API控制器对应用服务进行封装不是强制的,但是这是传统推荐的方式。 "tasksystem/task"一个具有随机命名空间的API控制器的名字。你应该至少定义一级的命名空间,但是你也可以定义更深层次的命名空间,比如 "myCompany/myApplication/myNamespace1/myNamespace2/myServiceName"'/api/services'是所有动态生成的Web API控制器的前缀。因此,该API控制器的地址将会是这个样子的 '/api/services/tasksystem/task',而GetTasks方法的地址将会是 '/api/services/tasksystem/task/getTasks'。因为在javascript中惯例遵循 camelCase规则,所以方法名都转成了camelCase格式。

ForAll 方法

在应用服务层可能会有很多的应用服务,如果要为这些应用服务都构建API控制器的话,一个一个地构建简直是费时费力的事情。没关系,ABP中的DynamicApiControllerBuilder提供了一个为所有应用服务构建Web API控制器的方法,这样我们只需要调用一次就行了。例如:

DynamicApiControllerBuilder
    .ForAll<IApplicationService>(Assembly.GetAssembly(typeof(SimpleTaskSystemApplicationModule)), "tasksystem")
    .Build();

ForAll方法是接收接口类型的泛型方法。第一个参数是一个程序集,该程序集中含有派生自给定接口的类。最后一个参数是服务前缀的命名空间。比如说我们在给定的程序集中有ITaskAppService和IPersonAppService,对于这个配置的话,服务地址将会是 '/api/services/tasksystem/task' 和 '/api/services/tasksystem/person'。计算服务名称的方法是:移除Service或者AppService后缀,以及I前缀(对于接口来说)。此外,服务名称会转成camel Case(驼峰命名)的格式。如果你不喜欢这种转换,那么使用'WithServiceName'来决定服务发名称。此外,还有一个过滤服务的Where方法。除了个别应用服务之外,这个方法在你为其他所有的应用服务构建API控制器时很有用。

重写ForAll 方法

在ForAll方法之后我们可以重写配置。例如:

DynamicApiControllerBuilder
    .ForAll<IApplicationService>(Assembly.GetAssembly(typeof(SimpleTaskSystemApplicationModule)), "tasksystem")
    .Build();

DynamicApiControllerBuilder
    .For<ITaskAppService>("tasksystem/task")
    .ForMethod("CreateTask").DontCreateAction()
    .Build();

在上面的代码中,我们为一个程序集中所有的应用服务构建了动态的Web API控制器。然后又为一个应用服务(ITaskAppService)重写了配置,目的是忽略该应用服务中的CreateTask方法。

Http动词

默认情况下,创建的方法都只能POST请求。我们也可以使用不同的方法来改变这种行为。
WithVerb方法

我们可以为一个方法使用WithVerb,像下面那样:

DynamicApiControllerBuilder
    .For<ITaskAppService>("tasksystem/task")
    .ForMethod("GetTasks").WithVerb(HttpVerb.Get)
    .Build();

HTTP特性

我们可以在应用服务的接口的方法上添加HttpGet,HttpPost等特性。

public interface ITaskAppService : IApplicationService
{
    [HttpGet]
    GetTasksOutput GetTasks(GetTasksInput input);

    [HttpPut]
    void UpdateTask(UpdateTaskInput input);

    [HttpPost]
    void CreateTask(CreateTaskInput input);
}

使用这些特性之前,应该在项目中添加Microsoft.AspNet.WebApi.CoreNuget包的引用。

命名规范

不用为每个方法都声明HTTP动词,你可以使用如下所示的WithConventionalVerbs方法:

DynamicApiControllerBuilder
    .ForAll<IApplicationService>(Assembly.GetAssembly(typeof(SimpleTaskSystemApplicationModule)), "tasksystem")
    .WithConventionalVerbs()
    .Build();

在这种情况下,Http动词会由方法名的前缀决定:

  • Get:方法名以Get开头。
  • Put:方法名以Put或Update开头。
  • Delete:方法名以Delete或Remove开头。
  • Post:方法名以Post或Create开头。
  • 其他情况,Post是HTTP动词的默认值

我们可以通过对特定的方法使用WithVerb方法或者HTTP特性来覆盖上述惯例。

动态Javascript代理

在Javascript中,可以经由Ajax使用动态创建的web api控制器。ABP通过为动态的web api控制器创建动态的Javascript代理简化了这个。因此,可以在Javascript中像调用一个function一样来调用一个动态的web api 控制器action:

abp.services.tasksystem.task.getTasks({
    state: 1
}).done(function (result) {
    //use result.tasks here...
});

Javascript代理是动态创建的。使用之前应该将下面动态的脚本包括在页面上。

<script src="/api/AbpServiceProxies/GetAll" type="text/javascript"></script>

服务方法返回了promise(查看jQuery.Deferred)。可以在返回的promise后面继续注册done,fail,then等回调函数。服务方法内部使用了abp.ajax。如果需要的话,它们会处理错误并显示错误信息。

Ajax参数

你可以把一个自定义的ajax参数作为第二个参数传给代理方法。

abp.services.tasksystem.task.createTask({
    assignedPersonId: 3,
    description: 'a new task description...'
},{ //override jQuery's ajax parameters
    async: false,
    timeout: 30000
}).done(function () {
    abp.notify.success('successfully created a task!');
});

jQuery.ajax的所有参数在这里都是有效的。

单一服务脚本

'/api/AbpServiceProxies/GetAll'会在一个文件中生成所有的服务代理。使用'/api/AbpServiceProxies/Get?name=serviceName'也可以生成一个单独的服务代理,只需要在页面中包括下面的代码:

<script src="/api/AbpServiceProxies/Get?name=tasksystem/task" type="text/javascript"></script>

Angular支持

ABP可以将动态的API控制器暴露给AngularJs服务。思考下面的例子:

(function() {
    angular.module('app').controller('TaskListController', [
        '$scope', 'abp.services.tasksystem.task',
        function($scope, taskService) {
            var vm = this;
            vm.tasks = [];
            taskService.getTasks({
                state: 0
            }).success(function(result) {
                vm.tasks = result.tasks;
            });
        }
    ]);
})();

我们可以使用服务的名字(包含命名空间)注射一个服务。然后,可以作为正常的Javascript函数调用它的function。注意,我们注册到了success句柄上(而不是done),因为它就像在angular的$http服务中。ABP使用AngularJs的$http服务。如果

你想要传递$http配置,可以作为服务方法的最后一个参数传递一个配置对象。

要使用自动生成的服务,应该在页面中包含需要的脚本:

<script src="~/Abp/Framework/scripts/libs/angularjs/abp.ng.js"></script>
<script src="~/api/AbpServiceProxies/GetAll?type=angular"></script>

Durandal支持

ABP可以在一个Durandal应用的模块中注入服务代理。看下面的viewmodel:

define(['service!tasksystem/task'],
    function (taskService) {
        //taskService can be used here
    });

ABP配置Durandal(实际上是Require.js)来理解这个'service!'前缀,然后注入合适的javascript服务代理。

返回结果封装

ABP通过 AjaxResponse封装了动态Web API的action的返回值。查看《Ajax文档》获取更多关于封装的信息。你可以为每个应用服务或者每个方法开启或者禁用封装。看下面这个应用服务的例子:

public interface ITestAppService : IApplicationService
{
    [DontWrapResult]
    DoItOutput DoIt(DoItInput input);
}

这里我们为DoIt方法禁用了封装。这个特性应该为接口声明而不是实现类。

如果你想更好地控制客户端的返回值,那么不封装返回的结果可能是很有用的。特别地,当使用不能和ABP标准的AjaxResponse协作的第三方客户端库时,可能需要禁用封装。这种情况下,你要自己处理异常。

注意:动态javascript代理可以理解返回的结果是否封装和运行正常。

关于参数绑定

ABP在运行时创建了API控制器。因此,ASP.NET Web API的模型和参数绑定可以用于绑定模型和参数。

FromUri和FromBody特性

为了在绑定时进行高级控制,可以在服务接口上使用FromUri和FromBody特性。

DTOs vs原始类型

我们强烈建议为应用服务和Web API控制器的方法使用DTO作为参数类型,但是你也可以使用原始类型(如string,int,bool或者可空的类型如int?,bool?)作为参数类型。虽然可以在应用服务中使用不止一个参数,但是最好用一个复杂的类型将多个参数整合起来,否则客户端就不会生成动态代理服务。在日志记录中就会看到如下图所示的错误:

posted @ 2016-01-06 14:50 tkbSimplest 阅读(...) 评论(...) 编辑 收藏