5.2 ABP表现层 - 动态WebApi层
5.2.1 建立动态WebApi控制器
这是一篇关于ASP.NET Web API的文档。如果你对ASP.NET感兴趣,请阅读ASP.NET Core文档。
Abp框架能够通过应用层自动生成web api:
public interface ITaskAppService : IApplicationService
{
GetTasksOutput GetTasks(GetTasksInput input);
void UpdateTask(UpdateTaskInput input);
void CreateTask(CreateTaskInput input);
}
并且,我们想要暴露这个服务作为Web API Controller给客户端。那么,Abp框架通过一行关键代码的配置就可以自动、动态的为应用层建立一个web api 控制器:
Configuration.Modules.AbpWebApi().DynamicApiControllerBuilder.For<ITaskAppService>("tasksystem/task").Build();
这样就OK了!建好的webapi控制器 /api/services/tasksystem/task 所有的方法都能够在客户端调用。webapi控制器通常是在模块初始化的时候完成配置。
ITaskAppService 是应用层服务(application service)接口,我们通过封装让接口实现一个api控制器。ITaskAppService不仅限于在应用层服务使用,这仅仅是我们习惯和推荐的使用方法。
tasksystem/task 是api 控制器的命名空间。一般来说,应当最少定义一层的命名空间,如:公司名称/应用程序/命名空间/命名空间1/服务名称。api/services/ 是所有动态web api的前缀。所以api控制器的地址一般是这样滴:/api/services/tasksystem/task,GetTasks 方法的地址一般是这样的:/api/services/tasksystem/task/getTasks 。因为在传统的js中都是使用 驼峰式 命名方法,这里也不一样。
ForAll方法
在程序的应用服务层建立多个api控制器可能让人觉得比较枯燥,DynamicApiControllerBuilper提供了建立所有应用层服务的方法,如下所示:
Configuration.Modules.AbpWebApi().DynamicApiControllerBuilder
.ForAll<IApplicationService>(Assembly.GetAssembly(typeof(SimpleTaskSystemApplicationModule)), "tasksystem")
.Build();
ForAll方法是一个泛型接口,第一个参数是从给定接口中派生的集合,最后一个参数则是services命名空间的前缀。ForAll集合有ITaskAppService和 IpersonAppService接口。根据如上配置,服务层的路由是这样的:’/api/services/tasksystem/task‘和’/api/services/tasksystem/person‘。
服务命名约定:服务名+AppService(在本例中是person+AppService) 的后缀会自动删除,生成的webapi控制器名为“person”。同时,服务名称将采用峰驼命名法。如果你不喜欢这种约定,你也可以通过 WithServiceName 方法来自定义名称。如果你不想创建所有的应用服务层中的某些服务,可以使用where来过滤部分服务。
重写ForAll
我们可以在ForAll方法后面重写配置,如:
Configuration.Modules.AbpWebApi().DynamicApiControllerBuilder
.ForAll<IApplicationService>(Assembly.GetAssembly(typeof(SimpleTaskSystemApplicationModule)), "tasksystem")
.Build();
Configuration.Modules.AbpWebApi().DynamicApiControllerBuilder
.For<ITaskAppService>("tasksystem/task")
.ForMethod("CreateTask").DontCreateAction().Build();
在上面代码中,我们为指定的程序集里面的所有Application服务创建了动态WebAPI Controllers。然后为应用服务(ITaskAppService>重写配置忽略CreateTask方法。
ForMethods
当我们使用ForAll方法的时候,可以使用 ForMethods 方法来更好的调整服务的方法,如:
Configuration.Modules.AbpWebApi().DynamicApiControllerBuilder
.ForAll<IApplicationService>(Assembly.GetExecutingAssembly(), "app")
.ForMethods(builder =>
{
if (builder.Method.IsDefined(typeof(MyIgnoreApiAttribute)))
{
builder.DontCreate = true;
}
})
.Build();
在这个示例中,我们使用了一个自定义特性 MyIgnoreApiAttribute 来检查所有的方法,如果动态WebAPI Controller的Action上有该特性,那么我们不会为该Action创建Web API。
Http 谓词
默认,所有方法的创建是: POST。为了能够使用动态Web API Action,客户端应该使用POST发送请求。我们可以改变这个行为以不同的方式。
WithVerb 方法
我们可以对某个方法使用 WithVerb,如下:
Configuration.Modules.AbpWebApi().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.Core nuget package。
命名约定
我们可以使用 WithConventionalVerbs 方法来代替Http谓词,如下所示:
Configuration.Modules.AbpWebApi().DynamicApiControllerBuilder
.ForAll<IApplicationService>(Assembly.GetAssembly(typeof(SimpleTaskSystemApplicationModule)), "tasksystem")
.WithConventionalVerbs()
.Build();
在这种情况下,ABP可以通过方法名字的前缀来判定Http谓词:
Get:如果方法的名字是以 Get 开头
Put:如果方法的名字是以 Put 或者 Update 开头
Delete:如果方法的名字是以 Delete 或者 Remove 开头
Post:如果方法的名字是以 Post,Create 或者 Insert 开头
Path:如果方法的名字是以 Path 开头
否则 Post 被作为Http谓词的默认设置
如前所述:我们可以覆盖某个特定的方法。
API Explorer
默认在API Explorer所有的动态WebApi控制器都是可见的(在Swagger中是有效的)。你可以使用 DynamicApiControllerBuilder API来控制这个默认行为,或者使用 RemoteService 特性来定义它。
RemoteService Attribute
你也可以在 接口上或者接口中的方法上 使用 RemoteService 特性来开启/禁用(IsEnabled)动态WebApi或者API Explorer设置(IsMetadataEnabled)。
5.2.2 使用动态JavaScript代理
你可以通过ajax来动态创建web api控制器。Abp框架对通过动态js代理建立web api 控制器做了些简化,你可以通过js来动态调用web api控制器:
abp.services.tasksystem.task.getTasks({
state: 1
}).done(function (data) {
//use data.tasks here..
});
js代理是动态创建的,页面中需要添加引用:
<script src="/api/AbpServiceProxies/GetAll" type="text/javascript"></script>
服务方法返回约定(可参见jQuery.Deferred),你可以注册done,fail,then… 等回调方法。服务方法内部使用的是 abp.ajax,如果有需要的话可以使用它们处理错误和显示错误。
1. 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!');
});
所有的jq.ajax参数都是有效的。
除了标准的JQuery.ajax参数,为了禁用错误信息自动显示,你可以添加AJAX选项:abpHandleError: false。
2. 单一服务脚本
‘/api/abpServiceProxies/GetAll’将在一个文件中生成所有的代理,通过 ‘/api/abpServiceProxies/Get?name=serviceName’ 你也可以生成单一服务代理,在页面中添加:
<script src="/api/abpServiceProxies/Get?name=tasksystem/task" type="text/javascript"></script>
3. Augular框架支持
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(data) {
vm.tasks = data.tasks;
});
}
]);
})();
我们可以将名称注入服务,然后调用此服务,跟调用一般的js函数一样。注意:我们成功注册处理程序后,他就像一个augular的$http服务。ABP框架使用angular框架的$http服务,如果你想通过$http来配置,你可以设置一个配置对象作为服务方法的一个参数。
要使用自动生成的服务,需要在页面中添加:
<script src="~/Abp/Framework/scripts/libs/angularjs/abp.ng.js"></script>
<script src="~/api/AbpServiceProxies/GetAll?type=angular"></script>
Enable/Disable
如果你向上面一样使用 ForAll 方法,那么你可以使用 RemoteService 特性来禁用某个服务或方法。请在 服务的接口 中使用该特性,而不是在服务类中。
包装结果
ABP通过 AjaxResponse 对象来 包装 动态Web API Actions的返回值。你可以Enable/Disable包装对每个方法或应用服务。如下所示:
public interface ITestAppService : IApplicationService
{
[DontWrapResult]
DoItOutput DoIt(DoItInput input);
}
我们对DoIt禁用了包装。这个特性应该在添加在接口里面的方法描述上,而不是扩展类上。
如果你想更精确的控制返回值到客户端,禁用包装是非常有用的。特别是在使用第三方客户端库的时候,禁用包装是有必要的,因为ABP的标准包装 AjaxResponse 可能不能与之一起使用。在这种情况下,你也应该自己处理异常,因为异常处理会被禁用掉(DontWrapResult特性有个属性:WrapOnError,该属性可以被用来开启异常处理并对异常进行包装)。
注意:动态脚本代理能够使用该结果,如果该结果是未包装,而且且能够正常的被处理。
关于参数绑定
ABP在运行时创建API Controllers。所以ASP.NET Web API 的模型和参数绑定被使用来绑定模型和参数。你可以读取该文档获取更多信息。
FormUri 和 FormBody 特性
FromUri 和 FromBody 特性能够在服务接口中被使用增进对绑定的控制。
数据传输对象 VS 原生类型
对于应用服务和Web API控制器,我强烈建议使用DTO来作为方法参数。但是你也可以使用原生类型(如:string,int,bool …或可空类型,如:int?,bool? …) 作为服务方法的参数。可以使用多个参数;但是,在这些参数中有且只能有一个 Complex-Type 参数,这是ASP.NET Web API 规定的。