ABP框架 - 动态Web Api层
本节内容:
这个文档是关于Asp.net Web Api的,如果你对Asp.net Core感兴趣,可查看Asp.net Core文档。
ABP可为你的应用层自动创建Asp.net Web Api层,假设我们有一个如下所示的应用服务:
public interface ITaskAppService : IApplicationService { GetTasksOutput GetTasks(GetTasksInput input); void UpdateTask(UpdateTaskInput input); void CreateTask(CreateTaskInput input); }
我们想把这个服务作为一个Web Api控制器暴露给客户端,ABP可通过一行配置为这个应用服务,自动动态创建一个Web Api控制器:
Configuration.Modules.AbpWebApi().DynamicApiControllerBuilder.For<ITaskAppService>("tasksystem/task").Build();
这就完事了。一个api控制器被创建在地址“/api/services/tasksystem/task”下,所有的方法客户端都可以使用。这个配置应当在你的模块的Initialize方法里完成。
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世界里就是这样约定俗成。
在一个应用里可能有很多应用服务,一个一个地创建api控制器会是一件乏味并易遗漏的工作,DynamicApiControllerBuilder提供了一个为所有应用服务创建web api控制器一次调用的方法,例如:
Configuration.Modules.AbpWebApi().DynamicApiControllerBuilder .ForAll<IApplicationService>(Assembly.GetAssembly(typeof(SimpleTaskSystemApplicationModule)), "tasksystem") .Build();
ForAll方法是一个接受一个接口的泛型,第一个参数是程序集,它包含继承自前面所指定接口的类,第二个服务的命名空间前缀,假设我们有一个包含ITaskAppService和IPersonAppService的程序集,应用上述配置,服务将是:“/api/services/tasksystem/task”和“/api/services/tasksystem/person”。为计算服务名:Service和AppService后缀和I前缀(接口)会被移除,服务名也被转换成驼峰形式。如果你不喜欢这种约定,有一个“WithServicename”方法可以确定命名。还有一个Where方法可过滤服务,这在你想为除了少数几个外的所有应用服务创建api控制器的时候,非常有用。
我们可在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();
这段代码里,我们为一个程序集里的所有应用服务创建Web Api控制器,然后为一个单独的应用服务(ITaskAppService)重写配置,忽略CreateTask方法。
当使用ForAll方法时,我们可用ForMethods方法进行更好的调用 ,例如:
Configuration.Modules.AbpWebApi().DynamicApiControllerBuilder .ForAll<IApplicationService>(Assembly.GetExecutingAssembly(), "app") .ForMethods(builder => { if (builder.Method.IsDefined(typeof(MyIgnoreApiAttribute))) { builder.DontCreate = true; } }) .Build();
在这个示例里,用一个自定义特性(MyIgnoreApiAttribute)来检查所有方法,不为标记了这个特性的方法创建动态web api控制器。
默认地,所有方法被创建成POST,所以一个客户端应用发送post请求来使用已创建的web api的actions。我们可以把这种行为修改成不同的方式。
我们可以为一个方法使用WithVerb,如:
Configuration.Modules.AbpWebApi().DynamicApiControllerBuilder .For<ITaskAppService>("tasksystem/task") .ForMethod("GetTasks").WithVerb(HttpVerb.Get) .Build();
在服务接口里,我们可以给方法添加HttpGet、HttpPost等特性:
public interface ITaskAppService : IApplicationService { [HttpGet] GetTasksOutput GetTasks(GetTasksInput input); [HttpPut] void UpdateTask(UpdateTaskInput input); [HttpPost] void CreateTask(CreateTaskInput input); }
为使用这些特性,应该在项目里添加Microsoft.Asp.net.WebApi.Core的nuget包,并引用它。
你可以使用WithconvertionalVerbs方法,代替为每个方法声明HTTP动词,如下所示:
Configuration.Modules.AbpWebApi().DynamicApiControllerBuilder .ForAll<IApplicationService>(Assembly.GetAssembly(typeof(SimpleTaskSystemApplicationModule)), "tasksystem") .WithConventionalVerbs() .Build();
这种情况下,以方法名的前缀确定HTTP动词:
- Get:如果方法名以“Get”打头。
- Put:如果方法名以“Put”或“Update”打头。
- Delete: 如果方法名以“Delete”或“Remove”打头。
- Post:如果方法名以“Post”或“Create”或“Insert”打头。
- Patch:如果方法名以“Patch”打头。
- 否则:以Post作为默认的HTTP动词。
但我们也可以用前面据说的方法重写它。
默认情况下,所有的动态Web api 控制器都会显示在Api浏览器里(例如:Swagger),不过你可以通过flunet DynamicApicontrollerBuilder Api或RemoteService特性来改变这种行为。
你可为任何的接口或方法使用RemoeteService来定义启用/禁用(IsEnabled)动态Api或Api浏览器设置(IsMetadataEnabled)。
你可以通过Javascript的ajax使用动态创建的web api控制器,ABP也通过为api控制器创建动态Javascript代理来简化调用。所以,你可以用像javascript调用函数那样的方式来调用一个动态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),你可以注册done, fail, then等回调函数,服务方法内部使用abp.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的所有参数在此都是可用的。
除了标准的jQuery.ajax参数外,你可以给AJAX选项添加abpHandleError:false,以便禁用出错时自动显示出错信息。
“/api/AbpServiceProxies/GetAll”在一个文件里生成所有服务代理。你也可以生成单独的服务代理,使用“/api/AbpServiceProxies/Get?name=serviceName”,然后在页面里包含这个脚本 ,如下所示:
<script src="/api/AbpServiceProxies/Get?name=tasksystem/task" type="text/javascript"></script>
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; }); } ]); })();
我们可以使用它的名称(包含命名空间)注入一个service,然后就可以如调用常规的Javascript函数那样调用它的函数,注意:我们注册success处理程序(代替done),因为这就像在angular的$http服务里,ABP使用Angularjs的$http服务,如果你想传递$http的configuration,你可以传递一个configuration对象,作为服务方法的最后一个参数。
为了使用自动生成的服务,你应该在你的页面里包含必要的脚本:
<script src="~/Abp/Framework/scripts/libs/angularjs/abp.ng.js"></script> <script src="~/api/AbpServiceProxies/GetAll?type=angular"></script>
如果你上面陈述的那样,使用ForAll方法,你可以为一个服务或方法,使用RemoteService特性来禁用它,要在服务接口上使用这个特性,而不是服务类上。
ABP通过AjaxResponse对象包装动态Web Api的Action返回值,更多信息查看ajax文档。你可以为每个方法或每个应用服务启用/禁用包装,请看如下应用服务示例:
public interface ITestAppService : IApplicationService { [DontWrapResult] DoItOutput DoIt(DoItInput input); }
我们为DoIt方法禁用了包装,这个属性要声明在接口里,而不是实现的类里。
如果你想更好地控制返回给客户端的值,不包装是非常有用的,尤其,在使用不支持ABP标准的AjaxResponse的第三方客户端库时,这种情况下,你也需要自己处理异常,因为异常处理也会被禁用(DontWrapResult特性拥有WrapOnError属性,它能启用异常的处理和包装)。
注意:动态javascript代理可以理解没有包装的结果,并用适应的方式处理结果。
ABP在运行时创建Api控制器,所以,Asp.net Web Api的模型与参数绑定用来绑定模型和参数,更多信息你可以阅读它们的文档。
FormUri和FormBody特性可用在服务接口里,优先控制绑定。
我们强烈建议为应用服务和web api控制器的方法使用Dto,但你可以使用简单类型(如string,int,bool...或nullable类型,如int?,bool?...)作为服务参数,可用多个简单类型参数,但只能用一个复杂类型参数(由于Asp.net Web Api的限制)。
kid1412附:英文原文:http://www.aspnetboilerplate.com/Pages/Documents/Dynamic-Web-API
kid1412声明:转载请把此段声明完整地置于文章页面明显处,并保留个人在博客园的链接:http://www.cnblogs.com/kid1412/(可点击跳转)。