1 | # themost
|
2 | MOST Web Framework 2.0 **Codename Blueshift**
|
3 |
|
4 | The new version of [MOST Web Framework](http://github.com/kbarbounakis/most-web)
|
5 | comes with a set of new features for building modern data-driven web applications and services.
|
6 |
|
7 | ## Generate a new application
|
8 |
|
9 | Install [MOST Web Framework CLI](http://github.com/kbarbounakis/most-web-cli) globally:
|
10 |
|
11 | npm install @themost/cli -g
|
12 |
|
13 | and generate a new project by executing:
|
14 |
|
15 | themost generate new project <project name>
|
16 |
|
17 | where [project name] is the name of the project which is going to be generated e.g.
|
18 |
|
19 | themost generate new project test-app
|
20 |
|
21 | MOST Web Framework approaches a web application structure with a wind set of naming conventions.
|
22 | So, a classic application structure seems like:
|
23 |
|
24 | + app
|
25 | + assets
|
26 | ...
|
27 | + vendor
|
28 | ...
|
29 | + server
|
30 | + controllers
|
31 | root-controller.js
|
32 | + config
|
33 | + models
|
34 | Thing.json
|
35 | User.json
|
36 | Group.json
|
37 | ...
|
38 | app.json
|
39 | routes.json
|
40 | + views
|
41 | + root
|
42 | index.html.ejs
|
43 | + shared
|
44 | master.html.ejs
|
45 |
|
46 |
|
47 | Navigate to new project directory and start application by executing:
|
48 |
|
49 | npm run serve
|
50 |
|
51 | ## Use Controllers
|
52 |
|
53 | Controllers are classes which handle requests and are stored in server/controllers
|
54 | directory.
|
55 |
|
56 |
|
57 | //# server/controllers/hello-controller.js
|
58 |
|
59 | import HttpBaseController from '@themost/web/controllers/base';
|
60 | import {httpController,httpGet, httpAction} from '@themost/web/decorators';
|
61 |
|
62 | @httpController()
|
63 | export default class RootController extends HttpBaseController {
|
64 |
|
65 | constructor(context) {
|
66 | super(context);
|
67 | }
|
68 |
|
69 | /**
|
70 | * GET /hello.html
|
71 | * @returns {Promise<any>}
|
72 | */
|
73 | @httpGet()
|
74 | @httpAction('hello')
|
75 | hello() {
|
76 | return Promise.resolve({
|
77 | "message": "Hello World!"
|
78 | });
|
79 | }
|
80 |
|
81 | }
|
82 |
|
83 | Generate a new controller by executing the following command:
|
84 |
|
85 | themost generate controller <controller name>
|
86 |
|
87 | e.g.
|
88 |
|
89 | themost generate controller Test
|
90 |
|
91 | This command will generate a new controller in server/controllers/test-controller.js.
|
92 | The new module exports a default class named TestController. TestController class extends HttpBaseController class which
|
93 | is one of the available controller classes of @themost.
|
94 | The framework uses the above naming convention (test-controller) to identify an HTTP controller available to handle requests.
|
95 |
|
96 | @themost uses EcmaScript decorators for defining HTTP controllers actions and methods.
|
97 | An HTTP controller class has @httpController() decorator to let framework identify it as
|
98 | a valid HTTP controller.
|
99 |
|
100 |
|
101 | @httpController()
|
102 | export default class RootController extends HttpBaseController
|
103 | ...
|
104 |
|
105 | An instance method of an HTTP controller may be marked as an HTTP action by defining one or more of the
|
106 | available decorators for HTTP actions.
|
107 |
|
108 | @httpGet()
|
109 | @httpAction('hello')
|
110 | hello() {
|
111 | return Promise.resolve({
|
112 | "message": "Hello World!"
|
113 | });
|
114 | }
|
115 |
|
116 | These decorators are:
|
117 |
|
118 | - @httpGet() to handle GET requests
|
119 | - @httpPost() to handle POST requests
|
120 | - @httpPut() to handle PUT requests
|
121 | - @httpDelete() to handle DELETE requests
|
122 | - @httpPatch() to handle PATCH requests
|
123 | - @httpHead() to handle HEAD requests
|
124 |
|
125 | @httpAction() decorator is used to define an HTTP action name
|
126 |
|
127 | @httpGet()
|
128 | @httpAction('hello')
|
129 | hello() {
|
130 | ...
|
131 | }
|
132 |
|
133 | #### HTTP results
|
134 |
|
135 | The result of an HTTP action may be an instance of HttpResult class or any other class which derives from HttpResult class.
|
136 |
|
137 |
|
138 | ###### HttpController#json(any)
|
139 |
|
140 | Returns a application/json result
|
141 |
|
142 | @httpGet()
|
143 | @httpAction('hello')
|
144 | hello() {
|
145 | return this.json({
|
146 | "message": "Hello World!"
|
147 | });
|
148 | }
|
149 |
|
150 | ###### HttpController#xml(any)
|
151 |
|
152 | Returns an application/xml result
|
153 |
|
154 | @httpGet()
|
155 | @httpAction('hello')
|
156 | hello() {
|
157 | return this.xml({
|
158 | "message": "Hello World!"
|
159 | });
|
160 | }
|
161 |
|
162 | ###### HttpController#content(string)
|
163 |
|
164 | Returns a text/html content result
|
165 |
|
166 | @httpGet()
|
167 | @httpAction('hello')
|
168 | hello() {
|
169 | return this.view(`
|
170 | <h2>Hello World!</h2>
|
171 | `);
|
172 | }
|
173 |
|
174 | ###### HttpController#view(any)
|
175 |
|
176 | Returns a text/html result
|
177 |
|
178 | @httpGet()
|
179 | @httpAction('hello')
|
180 | hello() {
|
181 | return this.view({
|
182 | "message": "Hello World!"
|
183 | });
|
184 | }
|
185 |
|
186 | ###### HttpController#file(physicalPath, fileName)
|
187 |
|
188 | Returns a content result based on the mime type of the physical file
|
189 |
|
190 | @httpGet()
|
191 | @httpAction('hello')
|
192 | hello() {
|
193 | return this.file(path.resolve(process.cwd(),'./app/assets/hello.pdf'));
|
194 | }
|
195 |
|
196 | ###### HttpController#redirect(url)
|
197 |
|
198 | Redirects the current HTTP request
|
199 |
|
200 | @httpGet()
|
201 | @httpAction('hello')
|
202 | hello() {
|
203 | return this.redirect('/login.html');
|
204 | }
|
205 |
|
206 | @themost uses a set of view engines for rendering content:
|
207 |
|
208 | **EJS View Engine**
|
209 |
|
210 | based on [Embedded JavaScript templates](https://github.com/mde/ejs)
|
211 |
|
212 | <div class="container">
|
213 | <div class="row">
|
214 | <div class="col-lg-12 text-center">
|
215 | <h2 class="mt-5"><%=model.message%></h2>
|
216 | </div>
|
217 | </div>
|
218 | </div>
|
219 |
|
220 | **Vash View Engine**
|
221 |
|
222 | based on [Vash, Razor syntax, for JavaScript templates](https://github.com/kirbysayshi/vash)
|
223 |
|
224 | <html lang="en">
|
225 | <head><title>MOST Web Framework</title></head>
|
226 | <body>
|
227 | <h2>@model.message</h2>
|
228 | </body>
|
229 | </html>
|
230 |
|
231 | **Jade View Engine**
|
232 |
|
233 | based on [Pug – robust, elegant, feature rich template engine for Node.js](https://github.com/pugjs/pug)
|
234 |
|
235 | doctype html
|
236 | html(lang="en")
|
237 | head
|
238 | title MOST Web Framework
|
239 | body
|
240 | h2= model.message
|
241 |
|
242 | **AngularJS for Server**
|
243 |
|
244 | @themost presents an new template engine based on the popular [AngularJS 1.x framework](https://github.com/angular/angular.js)
|
245 | This template is running in paraller with other templates engines and allows the use of AngularJS in server-side rendering.
|
246 |
|
247 | The following example contains an AngularJS server module and a simple directive which includes a hello message in an element:
|
248 |
|
249 | //# server/modules/server-app.js
|
250 | /**
|
251 | * Initializes angular server application
|
252 | * @param angular
|
253 | */
|
254 | export function bootstrap(angular) {
|
255 | let serverExtensions = angular.module('server-extensions',[]);
|
256 | serverExtensions
|
257 | .directive('serverHello', function() {
|
258 | return {
|
259 | restrict: 'A',
|
260 | scope: {
|
261 | serverHello: '='
|
262 | },
|
263 | link: function (scope, element) {
|
264 | element.html(scope.serverHello);
|
265 | }
|
266 | };
|
267 | });
|
268 | return angular.module('server',['server-extensions']);
|
269 | }
|
270 |
|
271 |
|
272 | Include the following html markup:
|
273 |
|
274 | <p server-hello="'This is a hello message from server application'"></p>
|
275 |
|
276 | e.g.
|
277 |
|
278 | <div class="container">
|
279 | <div class="row">
|
280 | <div class="col-lg-12 text-center">
|
281 | <h2 class="mt-5"><%=model.message%></h2>
|
282 | <p server-hello="'This is a hello message from server application'"></p>
|
283 | </div>
|
284 | </div>
|
285 | </div>
|
286 |
|
287 | and start using AngularJS directives in any server-side view.
|
288 |
|
289 | The result of an HTTP action may be also a Promise of any object (including an instance of HttpResult class).
|
290 |
|
291 | @httpGet()
|
292 | @httpAction('hello')
|
293 | hello() {
|
294 | return Promise.resolve({
|
295 | "message": "Hello World!"
|
296 | });
|
297 | }
|
298 |
|
299 | or
|
300 |
|
301 | @httpGet()
|
302 | @httpAction('hello')
|
303 | hello() {
|
304 | return new Promise((resolve, reject) => {
|
305 | //...
|
306 | return resolve({
|
307 | "message": "Hello World!"
|
308 | });
|
309 | });
|
310 | }
|
311 |
|
312 | ## Use routing
|
313 |
|
314 | MOST Web Framework holds application routings in server/config/routes.json file.
|
315 |
|
316 | [
|
317 | {
|
318 | "url": "/?",
|
319 | "controller": "root",
|
320 | "action": "index"
|
321 | },
|
322 | {
|
323 | "url": "/:action.html",
|
324 | "controller": "root",
|
325 | "format": "html"
|
326 | },
|
327 | {
|
328 | "url": "/:action.json",
|
329 | "controller": "root",
|
330 | "format": "json"
|
331 | },
|
332 | {
|
333 | "url": "/:action.xml",
|
334 | "controller": "root",
|
335 | "format": "xml"
|
336 | },
|
337 | {
|
338 | "url": "/{controller:plural}/?",
|
339 | "action": "index"
|
340 | },
|
341 | {
|
342 | "url": "/{controller:plural}/:action.html",
|
343 | "format": "html"
|
344 | },
|
345 | {
|
346 | "url": "/{controller:plural}/:action.json",
|
347 | "format": "json"
|
348 | },
|
349 | {
|
350 | "url": "/{controller:plural}/:action.xml",
|
351 | "format": "xml"
|
352 | }
|
353 | ]
|
354 |
|
355 | An application route may have a set of parameters that are defined with a colon followed by parameter name e.g. /test/:id/edit.html
|
356 |
|
357 | {
|
358 | "url": "/:action.html",
|
359 | "controller": "root",
|
360 | "format": "html"
|
361 | }
|
362 |
|
363 | The example above defines an application route for each action of RootController.
|
364 | The static parameter [controller] defines the controller which is going to serve the action. The [action] parameter defines the target action.
|
365 | The [format] parameter defines the content-type of the action result.
|
366 |
|
367 | Any application route may have typed route parameters.
|
368 |
|
369 | /users/{id:int}/edit.json
|
370 |
|
371 | This route defines a parameter named [id] which it must be an integer. Any other value will be ignored. Typed route parameters are:
|
372 |
|
373 | Format | Type |
|
374 | --- | --- |
|
375 | {param:int} | Defines an integer parameter (e.g. /users/12450/edit.json )
|
376 | {param:boolean} | Defines a boolean parameter
|
377 | {param:float} | Defines a float parameter
|
378 | {param:guid} | Defines a GUID parameter e.g. /users/2812EAEE-29C5-4810-AD9C-E6A50E97DE40/edit.json is served based on the following route: /users/{id:guid}/edit.json
|
379 | {param:plural} | Defines a string parameter that is used with its plural. This typed parameter is used for controller names e.g. /users/100/edit.html is served based on /{controller:plural}/{id:int}/edit.html where finally controller parameter is "user".
|
380 | {param:date} | Defines a DateTime parameter wrapped in single quotes e.g. /orders/calculate('2018-02-01') is served based on the following route: /orders/calculate\\({targetDate:date}\\)
|
381 | {param:string} | Defines a string parameter wrapped in single quotes e.g. /users('admin@example.com')/edit.json is served based on the following route: /users\\({email:date}\\)/edit.json
|
382 |
|
383 | Route parameters may be passed as action arguments.
|
384 |
|
385 | import HttpBaseController from '@themost/web/controllers/base';
|
386 | import {httpController,httpGet, httpAction} from '@themost/web/decorators';
|
387 |
|
388 | @httpController()
|
389 | export default class TestController extends HttpBaseController {
|
390 |
|
391 | constructor(context) {
|
392 | super(context);
|
393 | }
|
394 |
|
395 | ...
|
396 |
|
397 | @httpGet()
|
398 | @httpAction('multiply')
|
399 | multiply(a, b) {
|
400 | return Promise.resolve({
|
401 | result: a * b
|
402 | });
|
403 | }
|
404 |
|
405 | ...
|
406 |
|
407 | }
|
408 |
|
409 | The TestController.multiply(a,b) has two float arguments. A route for this action may be:
|
410 |
|
411 | {
|
412 | "url": "/test/multiply\\({a:float},{b:float}\\)",
|
413 | "controller": "test",
|
414 | "action": "multiply",
|
415 | "format": "json"
|
416 | }
|
417 |
|
418 | An HTTP request on /test/multiply(4.5,5.5) will produce the following response:
|
419 |
|
420 | {
|
421 | "result": 24.75
|
422 | }
|
423 |
|
424 | Any other request (like /test/multiply(x,5.5)) will fail with 404 Not Found HTTP result
|
425 |
|
426 | An HTTP controller action may validate any parameter with @httpParam decorator.
|
427 | This powerfull decorator allows a final parameter validation before the execution of an HTTP action.
|
428 |
|
429 |
|
430 | import HttpBaseController from '@themost/web/controllers/base';
|
431 | import {httpController,httpGet, httpAction} from '@themost/web/decorators';
|
432 |
|
433 | @httpController()
|
434 | export default class TestController extends HttpBaseController {
|
435 |
|
436 | constructor(context) {
|
437 | super(context);
|
438 | }
|
439 |
|
440 | ...
|
441 |
|
442 | @httpGet()
|
443 | @httpParam({ name: "b", type:"NonNegativeNumber" })
|
444 | @httpParam({ name: "a", type:"NonNegativeNumber" })
|
445 | @httpAction('multiply')
|
446 | multiply(a, b) {
|
447 | return Promise.resolve({
|
448 | result: a * b
|
449 | });
|
450 | }
|
451 |
|
452 | ...
|
453 |
|
454 | }
|
455 |
|
456 | In this case any HTTP request where a or b parameter is a negative number (e.g. /test/multiply(-2.5,5.5)) will fail with 400 Bad Request HTTP error.
|
457 |
|
458 | Note: @httpParam() decorators must be placed in reverse order.
|
459 |
|
460 | An @httpParam decorator may have the following attributes
|
461 |
|
462 | Attribute | Description
|
463 | --- | ---
|
464 | name | Defines the argument name
|
465 | type | Defines the argument type based on application defined types (e.g. Integer, NonNegativeInteger, PositiveInteger, Email, Date, DateTime, Number etc.)
|
466 | required | Indicates whether this parameter is required or not even if this is not described in application route
|
467 | minLength | Defines the minimum length of a parameter
|
468 | maxLength | Defines the maximum length of a parameter
|
469 | minValue | Defines the minimum value for the given parameter
|
470 | maxValue | Defines the maximum value for the given parameter
|
471 | message | Defines a validation message for this parameter
|
472 | pattern | Defines a regular expression for the given parameter
|
473 |
|
474 |
|
475 | ## Use static content
|
476 |
|
477 | A @themost web application may serve static files. HttpApplication#useStaticContent(contentPath) enables the serving of static files of the specified directory.
|
478 |
|
479 | //# server/server.js
|
480 | import {HttpApplication} from '@themost/web/app';
|
481 | import path from 'path';
|
482 | import {TraceUtils} from '@themost/common/utils';
|
483 | import {AngularServerModule} from "@themost/web/angular/module";
|
484 | import {LocalizationStrategy, I18nLocalizationStrategy} from "@themost/web/localization";
|
485 | //initialize app
|
486 | let app = new HttpApplication(path.resolve(__dirname));
|
487 | ...
|
488 | //use static content
|
489 | app.useStaticContent(path.resolve('./app'));
|
490 | ...
|
491 |
|
492 |
|