💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## 5.2 ABP表现层 - 动态WebApi层 ### 5.2.1 建立动态WebApi控制器 > 这是一篇关于ASP.NET Web API的文档。如果你对ASP.NET感兴趣,请阅读[ASP.NET Core](225864)文档。 Abp框架能够通过应用层自动生成web api: ``` csharp public interface ITaskAppService : IApplicationService { GetTasksOutput GetTasks(GetTasksInput input); void UpdateTask(UpdateTaskInput input); void CreateTask(CreateTaskInput input); } ``` 并且,我们想要暴露这个服务作为Web API Controller给客户端。那么,Abp框架通过一行关键代码的配置就可以自动、动态的为应用层建立一个web api 控制器: ```csharp DynamicApiControllerBuilder.For<ITaskAppService>("tasksystem/task").Build(); ``` 这样就OK了!建好的webapi控制器(/api/services/tasksystem/task)所有的方法都能够在客户端调用。webapi控制器通常是在[模块初始化](225823)的时候完成配置。 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中都是使用驼峰式命名方法,这里也不一样。 你也可以删除一个api方法,如下: ```csharp DynamicApiControllerBuilder .For<ITaskAppService>("tasksystem/taskService") .ForMethod("CreateTask").DontCreateAction() .Build(); ``` #### ForAll方法 在程序的应用服务层建立多个api控制器可能让人觉得比较枯燥,DynamicApiControllerBuilper提供了建立所有应用层服务的方法,如下所示: ```csharp 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方法后面重写配置,如: ```csharp DynamicApiControllerBuilder .ForAll<IApplicationService>(Assembly.GetAssembly(typeof(SimpleTaskSystemApplicationModule)), "tasksystem") .Build(); DynamicApiControllerBuilder .For<ITaskAppService>("tasksystem/task") .ForMethod("CreateTask").DontCreateAction().Build(); ``` 在上面代码中,我们为指定的程序集里面的所有Application服务创建了动态WebAPI Controllers。然后为应用服务(ITaskAppService>重写配置忽略CreateTask方法。 #### ForMethods 当我们使用ForAll方法的时候,可以使用 **ForMethods** 方法来更好的调整服务的方法,如: ```csharp 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**,如下: ```csharp DynamicApiControllerBuilder .For<ITaskAppService>("tasksystem/task") .ForMethod("GetTasks").WithVerb(HttpVerb.Get) .Build(); ``` ##### Http 特性 我们可以在服务接口的方法上使用HttpGet,HttpPost...等特性: ```csharp public interface ITaskAppService : IApplicationService { [HttpGet] GetTasksOutput GetTasks(GetTasksInput input); [HttpPut] void UpdateTask(UpdateTaskInput input); [HttpPost] void CreateTask(CreateTaskInput input); } ``` 为了能使用这些特性,我们应该在项目中引用这个包:[Microsoft.AspNet.WebApi.Core](https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Core) nuget package。 ##### 命名约定 我们可以使用 **WithConventionalVerbs** 方法来代替Http谓词,如下所示: ```csharp 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谓词的默认设置 ### 5.2.2 使用动态JavaScript代理 你可以通过ajax来动态创建web api控制器。Abp框架对通过动态js代理建立web api 控制器做了些简化,你可以通过js来动态调用web api控制器: ```javascript abp.services.tasksystem.task.getTasks({ state: 1 }).done(function (data) { //use data.tasks here.. }); ``` js代理是动态创建的,页面中需要添加引用: ```html <script src="/api/abp.ServiceProxies/GetAll" type="text/javascript"></script> ``` 服务方法(service methods)返回约定(可参见JQ的Deferred),服务方法使用Abp框架.ajax代替,可以处理、显示错误。 #### 1. Ajax参数 自定义ajax代理方法的参数: ```javascript 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' 你也可以生成单一服务代理,在页面中添加: ```html <script src="/api/abpServiceProxies/Get?name=tasksystem/task" type="text/javascript"></script> ``` #### 3. Augular框架支持 Abp框架能够公开动态的api控制器作为angularjs服务,如下所示: ```javascript (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来配置,你可以设置一个配置对象作为服务方法的一个参数。 要使用自动生成的服务,需要在页面中添加: ```html <script src="~/abp Framework/Framework/scripts/libs/angularjs/Abp Framework.ng.js"></script> <script src="~/api/abp Framework/ServiceProxies/GetAll?type=angular"></script> ``` #### Enable/Disable 如果你向上面一样使用 **ForAll** 方法,那么你可以使用 **RemoteService** 特性来禁用某个服务或方法。请在 **服务的接口** 中使用该特性,而不是在服务类中。 #### 包装结果 ABP通过 **AjaxResponse** 对象来 **包装** 动态Web API Actions的返回值。你可以Enable/Disable包装对每个方法或应用服务。如下所示: ```csharp public interface ITestAppService : IApplicationService { [DontWrapResult] DoItOutput DoIt(DoItInput input); } ``` 我们对DoIt禁用了包装。这个特性应该在添加在接口里面的方法描述上,而不是扩展类上。 如果你想更精确的控制返回值到客户端,禁用包装是非常有用的。特别是在使用第三方客户端库的时候,禁用包装是有必要的,因为ABP的标准包装 **AjaxResponse** 可能不能与之一起使用。在这种情况下,你也应该自己处理异常,因为异常处理会被禁用掉(DontWrapResult特性有个属性:WrapOnError,该属性可以被用来开启异常处理并对异常进行包装)。 >注意:动态脚本代理能够使用该结果,如果该结果是未包装,而且且能够正常的被处理。 #### 关于参数绑定 ABP在运行时创建API Controllers。所以ASP.NET Web API 的[模型和参数绑定](http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api)被使用来绑定模型和参数。你可以读取该[文档](http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api)获取更多信息。 #### FormUri 和 FormBody 特性 **FromUri 和 FromBody** 特性能够在服务接口中被使用增进对绑定的控制。 #### 数据传输对象 VS 原生类型 对于应用服务和Web API控制器,我强烈建议使用DTO来作为方法参数。但是你也可以使用原生类型(如:string,int,bool ...或可空类型,如:int?,bool? ...) 作为服务方法的参数。可以使用多个参数;但是,在这些参数中有且只能有一个 **Complex-Type** 参数,这是ASP.NET Web API 规定的。