-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdynamic-web-api.html
245 lines (231 loc) · 12.1 KB
/
dynamic-web-api.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<link type="text/css" rel="stylesheet" href="bootstrap.min.css" />
</head>
<body>
<div class="document-contents">
<h3 id="DocDynamicApi">
Building Dynamic Web API Controllers</h3>
<p>
ASP.NET Boilerplate can automatically generate <strong>web api layer</strong>
for your <strong>application layer</strong>. Say that we have an
<a href="/Pages/Documents/Application-Services">application
service</a> as shown below:</p>
<pre lang="cs">public interface ITaskAppService : IApplicationService
{
GetTasksOutput GetTasks(GetTasksInput input);
void UpdateTask(UpdateTaskInput input);
void CreateTask(CreateTaskInput input);
}</pre>
<p>
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:</p>
<pre lang="cs">DynamicApiControllerBuilder.For<ITaskAppService>("tasksystem/task").Build();</pre>
<p>
Thats
all! An api controller is created in the address '<strong>/api/services/tasksystem/task</strong>' and all methods are now usable by clients.
This configuration should be made in the
<a href="/Pages/Documents/Module-System">module initialization</a>. </p>
<p><strong>ITaskAppService</strong> is the application service that we want to
wrap with an api controller. It is not restricted to application services but
this is the traditional and recommended way. "<strong>tasksystem/task</strong>" 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". '<strong>/api/services/</strong>'
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 <strong>camelCase</strong> since it's conventional in javascript
world.</p>
<h4>ForAll Method</h4>
<p>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:</p>
<pre lang="cs">DynamicApiControllerBuilder
.ForAll<IApplicationService>(Assembly.GetAssembly(typeof(SimpleTaskSystemApplicationModule)), "tasksystem")
.Build();</pre>
<p>ForAll method is generic and accepts an interface. First parameter is an
assembly that has classes derived from given interface. The last one is
namespace prefix of services. Say that we have ITaskAppService and
IPersonAppService in given assembly. For this configuration, services will be
'/api/services/<strong>tasksystem/task</strong>' and '/api/services/<strong>tasksystem/person</strong>'.
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 '<strong>WithServiceName</strong>' method that you can
determine names. Also, There is a <strong>Where</strong> method to filter services. This can be
useful if you will build for all application services except a few one.</p>
<h4>Overriding ForAll</h4>
<p>We can override configuration after ForAll method. Example:</p>
<pre lang="cs">DynamicApiControllerBuilder
.ForAll<IApplicationService>(Assembly.GetAssembly(typeof(SimpleTaskSystemApplicationModule)), "tasksystem")
.Build();
DynamicApiControllerBuilder
.For<ITaskAppService>("tasksystem/task")
.ForMethod("CreateTask")<strong>.DontCreateAction()
</strong> .Build();</pre>
<p>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.</p>
<h4>Http Verbs</h4>
<p>By default, all methods are created as <strong>POST</strong>. So, a client should send post
requests in order to use created web api actions. We can change this behaviour
in different ways.</p>
<h5>WithVerb Method</h5>
<p>We can use WithVerb for a method like that:</p>
<pre lang="cs">DynamicApiControllerBuilder
.For<ITaskAppService>("tasksystem/task")
.ForMethod("GetTasks")<strong>.WithVerb(HttpVerb.Get)</strong>
.Build();</pre>
<h5>HTTP Attributes</h5>
<p>We can add HttpGet, HttpPost... attributes to methods in the service
interface:</p>
<pre lang="cs">public interface ITaskAppService : IApplicationService
{
<strong>[HttpGet]</strong>
GetTasksOutput GetTasks(GetTasksInput input);
<strong>[HttpPut]</strong>
void UpdateTask(UpdateTaskInput input);
<strong>[HttpPost]</strong>
void CreateTask(CreateTaskInput input);
}</pre>
<p>In order to use these attributes, we should add reference to
<a href="https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Core" target="_blank">Microsoft.AspNet.WebApi.Core</a>
nuget package from your project.</p>
<h5>Naming Convention</h5>
<p>Instead of declaring HTTP very for every method, you can use <strong>
WithConventionalVerbs</strong> method as shown below:</p>
<pre lang="cs">DynamicApiControllerBuilder
.ForAll<IApplicationService>(Assembly.GetAssembly(typeof(SimpleTaskSystemApplicationModule)), "tasksystem")
<strong>.WithConventionalVerbs()</strong>
.Build();</pre>
<p>In this case, HTTP verbs are determined by method name prefixes:</p>
<ul>
<li><strong>Get</strong>: Used if method name starts with 'Get'.</li>
<li><strong>Put</strong>: Used if method name starts with 'Put' or 'Update'.</li>
<li><strong>Delete</strong>: Used if method name starts with 'Delete' or
'Remove'.</li>
<li><strong>Post</strong>: Used if method name starts with 'Post' or
'Create'.</li>
<li>Otherwise, <strong>Post</strong> is used <strong>as default</strong>
HTTP verb.</li>
</ul>
<p>We can override it for a specific method using WithVerb method or HTTP
attributes.</p>
<h3 id="DocDynamicProxy">
Dynamic Javascript Proxies</h3>
<p>
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:</p>
<pre lang="js">abp.services.tasksystem.task.getTasks({
state: 1
}).done(function (result) {
//use result.tasks here...
});</pre>
<p>
Javascript proxies are created dynamically. You should include the dynamic script
to your page before use it:</p>
<pre lang="xml"><script src="/api/AbpServiceProxies/GetAll" type="text/javascript"></script></pre>
<p>Service methods return promise (See
<a href="http://api.jquery.com/category/deferred-object/" target="_blank">
jQuery.Deferred</a>). You can register to done, fail, then... callbacks.
Service methods use
<a href="/Pages/Documents/Javascript-API/AJAX">abp.ajax</a> inside. They
handle errors and show error messages if needed.</p>
<h4 id="DocAjaxParams">AJAX Parameters</h4>
<p>You may want to pass custom ajax parameters to the proxy method. You can pass
them as second argument as sown below:</p>
<pre lang="js">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!');
});</pre>
<p>All parameters of
<a href="http://api.jquery.com/jQuery.ajax/" target="_blank">jQuery.ajax</a> are
valid here.</p>
<h4 id="DocSingleService">Single Service Script</h4>
<p>'/api/AbpServiceProxies/GetAll' generates all service proxies in one file.
You can also generate a sinle service proxy using
'/api/AbpServiceProxies/Get?name=<em>serviceName</em>' and include the script to the
page as shown below:</p>
<pre lang="xml"><script src="/api/AbpServiceProxies/Get?name=tasksystem/task" type="text/javascript"></script></pre>
<h4 id="DocAngularSupport">Angular Integration</h4>
<p>ASP.NET Boilerplate can expose dynamic api controllers as <strong>angularjs services</strong>.
Consider the sample below:</p>
<pre lang="js">(function() {
angular.module('app').controller('TaskListController', [
'$scope', '<strong>abp.services.tasksystem.task</strong>',
function($scope, <strong>taskService</strong>) {
var vm = this;
vm.tasks = [];
<strong>taskService.getTasks</strong>({
state: 0
}).success(function(result) {
vm.tasks = result.tasks;
});
}
]);
})();</pre>
<p>We can inject a <strong>service</strong> using it's name (with namespace).
Then we can call it's <strong>functions</strong> as regular javascript
functions. Notice that we registered to <strong>success</strong> handler
(instead of done) since it's like that in angular's <strong>$http</strong>
service. ASP.NET Boilerplate uses $http service of AngularJs. If you want to
pass $http <strong>configuration</strong>, you can pass a configuration object as the last
parameter of the service method.</p>
<p>To be able to use auto-generated services, you should include needed scripts
to your page:</p>
<pre lang="xml"><script src="~/Abp/Framework/scripts/libs/angularjs/abp.ng.js"></script>
<script src="~/api/AbpServiceProxies/GetAll?type=angular"></script></pre>
<h3>Wrapping Results</h3>
<p>ASP.NET Boilerplate <strong>wraps</strong> return values of dynamic Web
API actions by <strong>AjaxResponse</strong> object. See
<a href="/Pages/Documents/Javascript-API/AJAX">ajax documentation</a> for
more information on this wrapping. You can enable/disable wrapping <strong>per
method</strong> or <strong>per application service</strong>. See this
example application service:</p>
<pre lang="cs">public interface ITestAppService : IApplicationService
{
<strong>[DontWrapResult]</strong>
DoItOutput DoIt(DoItInput input);
}</pre>
<p>We disabled wrapping for DoIt method. This properties should be declared
for <strong>interfaces</strong>, not implementation classes.</p>
<p>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
<strong>3rd party client side libraries</strong> which can not work with ASP.NET
Boilerplate's standard AjaxResponse. In this case, you should handle
exceptions yourself.</p>
<p>Note: Dynamic javascript proxies can understand if result is unwrapped and
run properly in either case.</p>
<h3>About Parameter Binding</h3>
<p>ASP.NET Boilerplate creates Api Controllers on runtime. So, ASP.NET Web API's
<a href="http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api" target="_blank">
model and parameter binding</a> is used to bind model and parameters. You can
read it's
<a href="http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api" target="_blank">
documentation</a> for more information.<strong> </strong></p>
<h4>FormUri and FormBody Attributes</h4>
<p><strong>FromUri</strong> and <strong>FromBody</strong> attributes can be used
in service interface to advanced control on binding. </p>
<h4>DTOs vs Primitive Types</h4>
<p>We strongly advice to use
<a href="http://www.aspnetboilerplate.com/Pages/Documents/Data-Transfer-Objects">
DTO</a>s 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.</p>
</div>
</body>
</html>