mvc-middleware
Advanced tools
Comparing version 3.0.0-rc.1 to 3.0.0-rc.2
@@ -97,3 +97,9 @@ "use strict"; | ||
return (request, response, next) => { | ||
const controller = this.container.resolve(controllerConstructor, request, response); | ||
let controller; | ||
if (this.container) { | ||
controller = this.container.resolve(controllerConstructor, request, response); | ||
} | ||
else { | ||
controller = new controllerConstructor(request, response); | ||
} | ||
if (!controller) { | ||
@@ -100,0 +106,0 @@ next(); |
@@ -68,3 +68,9 @@ import { readdir } from 'fs/promises'; | ||
return (request, response, next) => { | ||
const controller = this.container.resolve(controllerConstructor, request, response); | ||
let controller; | ||
if (this.container) { | ||
controller = this.container.resolve(controllerConstructor, request, response); | ||
} | ||
else { | ||
controller = new controllerConstructor(request, response); | ||
} | ||
if (!controller) { | ||
@@ -71,0 +77,0 @@ next(); |
{ | ||
"name": "mvc-middleware", | ||
"version": "3.0.0-rc.1", | ||
"version": "3.0.0-rc.2", | ||
"description": "Mvc middleware for express with API similar to .NET MVC", | ||
@@ -16,4 +16,7 @@ "author": "Artem Ignatev <art.ser.ignatiev@gmail.com>", | ||
"middleware", | ||
"server", | ||
".net controller" | ||
".net controller", | ||
"express", | ||
"express cheap-di", | ||
"express dependency injection", | ||
"cheap-di" | ||
], | ||
@@ -20,0 +23,0 @@ "dependencies": { |
175
README.md
@@ -9,27 +9,87 @@ # mvc-middleware | ||
## How to use | ||
* [Installation](#installation) | ||
* [How to use](#how-to-use) | ||
* [Dependency injection](#dependency-injection) | ||
* [Decorators](#decorators) | ||
* [MvcController methods](#methods) | ||
* [More examples](#more-examples) | ||
## <a name="installation"></a> Installation | ||
```shell | ||
npm i mvc-middleware | ||
``` | ||
## <a name="how-to-use"></a> How to use | ||
```ts | ||
// src/index.ts | ||
import { container } from "cheap-di"; | ||
import express, { Router } from 'express'; | ||
import { json } from 'body-parser'; | ||
import cors from 'cors'; | ||
import express from 'express'; | ||
import http from 'http'; | ||
import { MvcMiddleware } from 'mvc-middleware'; | ||
import path from 'path'; | ||
const app = express(); | ||
// Path to folder where you have you controllers. | ||
// Middleware will search controlelrs recursively. | ||
// Path to folder where you have your controllers. | ||
// Middleware will search controllers recursively. | ||
// Each file with '.ts' extension and default export and | ||
// typeof === 'function', decorated with '@api' will be | ||
// assumed as controller class. | ||
const controllersPath = path.join(__dirname, "api"); | ||
const controllersPath = path.join(__dirname, 'api'); | ||
new MvcMiddleware(app, container /* dependency injection */) | ||
.registerControllers(controllersPath); | ||
const expressApp = express(); | ||
expressApp.use(cors()).use(json()); | ||
// ... run your server ... | ||
const mvcMiddleware = new MvcMiddleware(expressApp); | ||
(async () => { | ||
await mvcMiddleware.registerControllers(controllersPath); | ||
http | ||
.createServer(expressApp) | ||
.listen(80, 'localhost'); | ||
})(); | ||
``` | ||
```ts | ||
// src/api/UsersApi.ts | ||
import { MvcController } from 'mvc-middleware'; | ||
You may pass any custom DI resolver that implements following contract: | ||
@api('/api') | ||
export default class UsersApi extends MvcController { | ||
@GET // api/users | ||
users() { | ||
return this.ok(['user-1', 'user-2', 'user-3']); | ||
} | ||
} | ||
``` | ||
```ts | ||
// src/api/ArticlesApi.ts | ||
import { MvcController } from 'mvc-middleware'; | ||
@api('/api') | ||
export default class ArticlesApi extends MvcController { | ||
@GET // articles | ||
async articles() { | ||
const articles = await Promise.resolve(['article-1', 'article-2']); | ||
return this.ok(articles); | ||
} | ||
} | ||
``` | ||
## <a name="dependency-injection"></a> Dependency injection | ||
We recommend to use <a href="https://github.com/tomas-light/cheap-di/tree/master/packages/cheap-di">cheap-di</a> package to handle your dependency injection: | ||
```ts | ||
import { container } from 'cheap-di'; | ||
import express from 'express'; | ||
const expressApp = express(); | ||
const mvcMiddleware = new MvcMiddleware(expressApp, container); | ||
// ... | ||
``` | ||
You may pass any custom DI resolver that implements the following contract: | ||
```ts | ||
type AbstractConstructor<T = any> = abstract new (...args: any[]) => T; | ||
@@ -46,2 +106,5 @@ type Constructor<T = any> = new (...args: any[]) => T; | ||
## <a name="decorators"></a> Decorators | ||
We have two sets of decorators: for stage 2 (legacy) and stage 3 proposals. You may choose one of them with imports: | ||
@@ -53,3 +116,35 @@ ```ts | ||
| decorator | description | variants of using | | ||
|-----------|----------------------------------------------------------------------------|-------------------------------------------------------------------------------| | ||
| api | collect method names and types (get/post/...) and concat prefix to is urls | `@api`<br>`@api('api')`<br>`@api('/api/domain')` | | ||
| GET | marks method as handler for GET request, can change handled url | `@GET`<br>`@GET('')`<br>`@GET('my-route')`<br>`@GET('/my-route')` | | ||
| POST | marks method as handler for GET request, can change handled url | `@POST`<br>`@POST('')`<br>`@POST('my-route')`<br>`@POST('/my-route')` | | ||
| PUT | marks method as handler for PUT request, can change handled url | `@PUT`<br>`@PUT('')`<br>`@PUT('my-route')`<br>`@PUT('/my-route')` | | ||
| PATCH | marks method as handler for PATCH request, can change handled url | `@PATCH`<br>`@PATCH('')`<br>`@PATCH('my-route')`<br>`@PATCH('/my-route')` | | ||
| DELETE | marks method as handler for DELETE request, can change handled url | `@DELETE`<br>`@DELETE('')`<br>`@DELETE('my-route')`<br>`@DELETE('/my-route')` | | ||
## <a name="methods"></a> MvcController methods | ||
| Method name | Response status code | Response type | Arguments | Description | | ||
|:-------------------:|:--------------------:|:-------------:|:---------------------------------:|------------------------------------------------------------| | ||
| `ok` | 200 | text or json | `model?: any` | returns 200 status code with data | | ||
| `created` | 201 | text or json | `model?: any` | returns 201 status code with data | | ||
| `accepted` | 202 | text or json | `model?: any` | returns 202 status code with data | | ||
| `noContent` | 204 | - | - | returns 204 status code | | ||
| | | | | | | ||
| `found` | 302 | text | `url: string` | returns 302 status code | | ||
| `permanentRedirect` | 308 | text | `url: string` | returns 308 status code | | ||
| `redirect` | 300 - 308 | text | `statusCode: number, url: string` | returns redirection status code | | ||
| | | | | | | ||
| `badRequest` | 400 | text or json | `model?: any` | returns 400 status code with data | | ||
| `unauthorized` | 401 | text or json | `model?: any` | returns 401 status code with data | | ||
| `forbid` | 403 | - | `model?: any` | returns 403 status code | | ||
| `notFound` | 404 | text or json | `model?: any` | returns 404 status code with data | | ||
| `conflict` | 409 | text or json | `model?: any` | returns 409 status code with data | | ||
| | | | | | | ||
| `serverError` | 500 | text | `message?: any` | returns 500 status code with error message | | ||
| `sendResponse` | any http status code | text | `model: any, statusCode?: number` | returns status code with data. Default status code is 200 | | ||
## <a name="more-examples"></a> More examples | ||
```ts | ||
@@ -60,5 +155,5 @@ // src/api/UsersController.ts | ||
@api('/api/user') | ||
export class UsersController extends MvcController { | ||
static users: Array<{ id: number, name: string }> = [{ | ||
@api('/api/users') | ||
export default class UsersController extends MvcController { | ||
static users = [{ | ||
id: 1, | ||
@@ -68,8 +163,4 @@ name: 'user 1', | ||
constructor(request: Request, response: Response) { | ||
super(request, response); | ||
} | ||
// GET: /api/users/list | ||
@GET | ||
// GET: /api/users | ||
@GET('') | ||
list() { | ||
@@ -81,4 +172,4 @@ return this.ok(UsersController.users); | ||
@GET(':userId') | ||
getById(userIdStr: string) { | ||
const userId = parseInt(userIdStr, 10); | ||
getById(stringId: string) { | ||
const userId = parseInt(stringId, 10); | ||
const user = UsersController.users.find(user => user.id === userId); | ||
@@ -88,4 +179,4 @@ return this.ok(user); | ||
// POST: /api/users/add | ||
@POST | ||
// POST: /api/users | ||
@POST('') | ||
add({ name }: { name: string }) { | ||
@@ -99,7 +190,5 @@ UsersController.users.push({ | ||
} | ||
export default UsersController; | ||
``` | ||
If you want to get query params and body content you have to connect another middlewares, that will handle requests before MvcMiddleware like bellow. | ||
If you want to get query params and body content, you have to connect another middleware that will handle requests before MvcMiddleware like below. | ||
@@ -112,29 +201,9 @@ ```js | ||
const app = express(); | ||
const expressApp = express(); | ||
app.use(bodyParser.json()); | ||
app.use(bodyParser.urlencoded()); | ||
expressApp.use(bodyParser.json()); | ||
expressApp.use(bodyParser.urlencoded()); | ||
const mvcMiddleware = new MvcMiddleware(expressApp, container); | ||
// ... | ||
``` | ||
### MvcController methods | ||
| Method name | Response status code | Response type | Arguments | Description | | ||
|:-------------------:|:--------------------:|:-------------:|:---------------------------------:|------------------------------------------------------------| | ||
| `ok` | 200 | text or json | `model?: any` | returns 200 status code with data | | ||
| `created` | 201 | text or json | `model?: any` | returns 201 status code with data | | ||
| `accepted` | 202 | text or json | `model?: any` | returns 202 status code with data | | ||
| `noContent` | 204 | - | - | returns 204 status code | | ||
| | | | | | | ||
| `found` | 302 | text | `url: string` | returns 302 status code | | ||
| `permanentRedirect` | 308 | text | `url: string` | returns 308 status code | | ||
| `redirect` | 300 - 308 | text | `statusCode: number, url: string` | returns redirection status code | | ||
| | | | | | | ||
| `badRequest` | 400 | text or json | `model?: any` | returns 400 status code with data | | ||
| `unauthorized` | 401 | text or json | `model?: any` | returns 401 status code with data | | ||
| `forbid` | 403 | - | `model?: any` | returns 403 status code | | ||
| `notFound` | 404 | text or json | `model?: any` | returns 404 status code with data | | ||
| `conflict` | 409 | text or json | `model?: any` | returns 409 status code with data | | ||
| | | | | | | ||
| `serverError` | 500 | text | `message?: any` | returns 500 status code with error message | | ||
| `sendResponse` | any http status code | text | `model: any, statusCode?: number` | returns status code with data. Default status code is 200 | | ||
``` |
@@ -6,6 +6,6 @@ import type { DependencyResolver } from 'cheap-di'; | ||
private readonly application; | ||
private readonly container; | ||
constructor(application: Pick<Application, 'get' | 'post' | 'put' | 'patch' | 'delete'>, container: { | ||
private readonly container?; | ||
constructor(application: Pick<Application, 'get' | 'post' | 'put' | 'patch' | 'delete'>, container?: { | ||
resolve: DependencyResolver['resolve']; | ||
}); | ||
} | undefined); | ||
/** Registers all classes with default exports in specified directory | ||
@@ -12,0 +12,0 @@ * as API controllers recursively. |
import { Constructor } from 'cheap-di'; | ||
interface Api { | ||
<T extends Constructor>(constructor: T): T; | ||
(urlPrefix: string): <T extends Constructor>(constructor: T) => T; | ||
<T, TConstructor extends Constructor<T>>(constructor: TConstructor): TConstructor; | ||
(urlPrefix: string): <T, TConstructor extends Constructor<T>>(constructor: TConstructor) => TConstructor; | ||
} | ||
@@ -6,0 +6,0 @@ /** |
import { ApiMethodsContainer } from './constants.js'; | ||
export declare const apiSymbol: unique symbol; | ||
export type PatchedConstructor = (abstract new (...args: any[]) => any) & { | ||
export type PatchedConstructor = (new (...args: any[]) => any) & { | ||
[apiSymbol]?: ApiMethodsContainer; | ||
}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
194724
2385
200