ABP框架系列之二十二:(Dynamic-Web-API-動態WebApi)


Building Dynamic Web API Controllers

This document is for ASP.NET Web API. If you're interested in ASP.NET Core, see ASP.NET Core documentation.

ASP.NET Boilerplate can automatically generate ASP.NET Web API layer for your application layer. Say that we have an application service as shown below:

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

And we want to expose this service as a Web API Controller for clients. ASP.NET Boilerplate can automatically and dynamically create a Web API Controller for this application service with a single line of configuration:

我們希望將此服務作為Web API控制器公開給客戶機。ASP.NET的模板可以自動和動態地創建一個Web API控制器的應用服務這一行的配置:

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

Thats all! An api controller is created in the address '/api/services/tasksystem/task' and all methods are now usable by clients. This configuration should be made in the Initialize method of your module.

ITaskAppService is the application service that we want to wrap with an api controller. It is not restricted to application services but this is the conventional and recommended way. "tasksystem/task" is name of the api controller with an arbitrary namespace. You should define at least one-level namespace but you can define more deep namespaces like "myCompany/myApplication/myNamespace1/myNamespace2/myServiceName". '/api/services/' is a prefix for all dynamic web api controllers. So, address of the api controller will be like '/api/services/tasksystem/task' and GetTasks method's address will be '/api/services/tasksystem/task/getTasks'. Method names are converted to camelCase since it's conventional in javascript world.

itaskappservice是應用服務,我們要用一個API控制器。它不限於應用程序服務,但這是常規的和推薦的方法。tasksystem /任務”是一個任意的命名空間API控制器名稱。你必須至少定義一個層次命名空間但你可以定義更多的深命名空間像“myCompany/myApplication/myNamespace1/myNamespace2/myServiceName”。'/api/services/'是所有動態Web API控制器的前綴。因此,該API控制器地址會像/api/services/tasksystem/task”和gettasks方法的地址將/api/services/tasksystem/task/getTasks'。方法名稱轉換成CamelCase既然JavaScript世界傳統。

ForAll Method

We may have many application services in an application and building api controllers one by one may be a tedious and forgettable work. DynamicApiControllerBuilper provides a method to build web api controllers for all application services in one call. Example:

我們可能有很多的應用服務在應用程序和API控制器的建設可能是一個繁瑣和無聊的工作。dynamicapicontrollerbuilper提供在一個呼叫建立各種應用服務的Web API控制器的方法。例子:

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

ForAll method is generic and accepts an interface. First parameter is an assembly that has classes derived from given interface. Second one is the namespace prefix of services. Say that we have ITaskAppService and IPersonAppService in given assembly. For this configuration, services will be '/api/services/tasksystem/task' and '/api/services/tasksystem/person'. To calculate service name: Service and AppService postfixes and I prefix is removed (for interfaces). Also, service name is converted to camel case. If you don't like this convention, there is a 'WithServiceName' method that you can determine names. Also, There is a Where method to filter services. This can be useful if you will build for all application services except a few one.

該方法是通用的,接受一個接口。第一個參數是具有從給定接口派生的類的程序集。第二個是服務的命名空間前綴。說我們在給定的裝配itaskappservice和ipersonappservice。此配置,將服務/api/services/tasksystem/task'和' '/api/services/tasksystem/person'。計算服務名稱:服務和AppService postfixes和我的前綴被刪除(接口)。此外,服務名稱轉換為 camel的情況。如果你不喜歡這個慣例,還有就是你可以確定的名字“withservicename法。此外,還有一個過濾服務的方法。如果您只為少數幾個應用程序服務構建此服務,那么這將非常有用

Overriding ForAll

We can override configuration after ForAll method. Example:

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

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

In this code, we created dynamic web api controllers for all application services in an assembly. Then overrided configuration for a single application service (ITaskAppService) to ignore CreateTask method.

在這個代碼中,我們為程序集中的所有應用程序服務創建動態Web API控制器。然后替換為一個單一的應用程序服務的配置(itaskappservice)忽視CreateTask方法。

ForMethods

We can use ForMethods method to better adjust each method while using ForAll method. Example:

Configuration.Modules.AbpWebApi().DynamicApiControllerBuilder
    .ForAll<IApplicationService>(Assembly.GetExecutingAssembly(), "app")
    .ForMethods(builder =>
    {
        if (builder.Method.IsDefined(typeof(MyIgnoreApiAttribute)))
        {
            builder.DontCreate = true;
        }
    })
    .Build();

In this example, I used a custom attribute (MyIgnoreApiAttribute) to check for all methods and don't create dynamic web api controller actions for those methods marked with that attribute.

在這個例子中,我使用了一個自定義屬性(myignoreapiattribute)來檢查所有的方法不創建動態Web API控制器動作的方法標記屬性。

Http Verbs

By default, all methods are created as POST. So, a client should send post requests in order to use created web api actions. We can change this behaviour in different ways.

WithVerb Method

We can use WithVerb for a method like that:

Configuration.Modules.AbpWebApi().DynamicApiControllerBuilder
    .For<ITaskAppService>("tasksystem/task")
    .ForMethod("GetTasks").WithVerb(HttpVerb.Get)
    .Build();
HTTP Attributes

We can add HttpGet, HttpPost... attributes to methods in the service interface:

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

    [HttpPut]
    void UpdateTask(UpdateTaskInput input);

    [HttpPost]
    void CreateTask(CreateTaskInput input);
}

In order to use these attributes, we should add reference to Microsoft.AspNet.WebApi.Core nuget package from your project.

Naming Convention(命名約定

Instead of declaring HTTP very for every method, you can use WithConventionalVerbs method as shown below:

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

In this case, HTTP verbs are determined by method name prefixes:

  • Get: Used if method name starts with 'Get'.
  • Put: Used if method name starts with 'Put' or 'Update'.
  • Delete: Used if method name starts with 'Delete' or 'Remove'.
  • Post: Used if method name starts with 'Post', 'Create' or 'Insert'.
  • Patch: Used if method name starts with 'Patch'.
  • Otherwise, Post is used as default HTTP verb.

We can override it for a specific method as described before.

API Explorer

All dynamic web api controllers are visible to API explorer by default (They are available in Swagger for example). You can control this behaviour with fluent DynamicApiControllerBuilder API or using RemoteService attribute defined below.

默認情況下,所有動態Web API控制器都可以在API資源管理器中可見(例如,它們可以在Swagger中使用)。你可以控制這種行為用流利的dynamicapicontrollerbuilder API或使用RemoteService屬性定義如下。

RemoteService Attribute

You can also use RemoteService attribute for any interface or method definition to enable/disable (IsEnabled) dynamic API or API explorer setting (IsMetadataEnabled).

Dynamic Javascript Proxies(動態JavaScript代理

You can use the dynamically created web api controller via ajax in javascript. ASP.NET Boilerplate also simplifies this by creating dynamic javascript proxies for dynamic web api controllers. So, you can call a dynamic web api controller's action from javascript as like a function call:

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

Javascript proxies are created dynamically. You should include the dynamic script to your page before use it:

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

Service methods return promise (See jQuery.Deferred). You can register to done, fail, then... callbacks. Service methods use abp.ajax inside. They handle errors and show error messages if needed.

服務方法返回承諾(參見。你可以注冊完成,失敗,然后…回調。使用方法使用abp.ajax里面。如果需要,它們處理錯誤並顯示錯誤消息。

AJAX Parameters

You may want to pass custom ajax parameters to the proxy method. You can pass them as second argument as sown below:

您可能希望將自定義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!');
});

All parameters of jQuery.ajax are valid here.

In addition to standard jQuery.ajax parameters, you can add abpHandleError: false to AJAX options in order to disable automatic message displaying on error cases.

除了標准的jquery.ajax參數,您可以添加abphandleerror:假ajax選項以禁用自動信息顯示錯誤的情況。

Single Service Script(單服務腳本

'/api/AbpServiceProxies/GetAll' generates all service proxies in one file. You can also generate a single service proxy using '/api/AbpServiceProxies/Get?name=serviceName' and include the script to the page as shown below:

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

Angular Integration

ASP.NET Boilerplate can expose dynamic api controllers as angularjs services. Consider the sample below:

(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;
            });
        }
    ]);
})();

We can inject a service using it's name (with namespace). Then we can call it's functions as regular javascript functions. Notice that we registered to success handler (instead of done) since it's like that in angular's $httpservice. ASP.NET Boilerplate uses $http service of AngularJs. If you want to pass $http configuration, you can pass a configuration object as the last parameter of the service method.

我們可以使用它的名稱(命名空間)來注入服務。然后我們可以把它的函數稱為常規JavaScript函數。注意我們注冊成功處理(而不是做)因為這就像在角的服務。ASP.NET樣板使用HTTP服務的AngularJS美元。如果您想通過HTTP配置,您可以將配置對象作為服務方法的最后一個參數傳遞。

To be able to use auto-generated services, you should include needed scripts to your page:

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

Enable/Disable

If you used ForAll method as defined above, the you can use RemoteService attribute to disable it for a service or for method. Use this attribute in the service interface, not in the service class.

Wrapping Results(包裝結果

ASP.NET Boilerplate wraps return values of dynamic Web API actions by AjaxResponse object. See ajax documentation for more information on this wrapping. You can enable/disable wrapping per method or per application service. See this example application service:

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

We disabled wrapping for DoIt method. This properties should be declared for interfaces, not implementation classes.

Unwrapping can be useful if you want to more control on exact return values to the client. Especially, disabling it may be needed while working 3rd party client side libraries which can not work with ASP.NET Boilerplate's standard AjaxResponse. In this case, you should also handle exceptions yourself since exception handling will be disabled (DontWrapResult attribute has WrapOnError properties that can be used to enable handling and wrapping for exceptions).

Note: Dynamic javascript proxies can understand if result is unwrapped and run properly in either case.

我們取消包裝做的方法。此屬性應聲明為接口,而不是實現類。

如果想要對客戶機精確的返回值進行更多的控制,解包可能是有用的。特別是,禁用它可以同時工作的第三方客戶端庫無法與ASP.NET樣板的標准AjaxResponse工作需要。在這種情況下,你也應該把自己從異常處理異常將被禁用(dontwrapresult屬性有wraponerror性質可以用來使搬運和包裝的例外)。

注意:動態JavaScript代理可以理解結果是否被解包並在兩種情況下正常運行。

About Parameter Binding

ASP.NET Boilerplate creates Api Controllers on runtime. So, ASP.NET Web API's model and parameter binding is used to bind model and parameters. You can read it's documentation for more information.

FormUri and FormBody Attributes

FromUri and FromBody attributes can be used in service interface to advanced control on binding.

DTOs vs Primitive Types(DTOS vs 原始類型

We strongly advice to use DTOs as method parameters for application services and web api controllers. But you can also use primitive types (like string, int, bool... or nullable types like int?, bool?...) as service arguments. More than one parameters can be used but only one complex-type parameter is allowed in these parameters (because of restriction of ASP.NET Web API).

我們強烈建議使用DTOs作為應用服務和Web API控制器方法參數。但你也可以使用原始數據類型(如字符串、整型、布爾型…或可空類型如int?,布爾?)作為服務參數。多個參數可用於只有一個復雜類型參數這些參數的允許(因為ASP.NET Web API的限制)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM