-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 9a36198
Showing
9 changed files
with
266 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { serve } from './deps.ts'; | ||
import { Context } from './context.ts'; | ||
import { Middleware, compose } from './middleware.ts'; | ||
|
||
export class Application<S extends object = { [key: string]: any }> { | ||
/** | ||
* 注册的中间件 | ||
* | ||
* @private | ||
* @type {Middleware[]} | ||
* @memberof Application | ||
*/ | ||
private _middleware: Middleware[] = [] | ||
/** | ||
* 将状态传递给前端视图的对象。 这可以通过在创建新应用程序时提供通用状态参数来键入。 | ||
* | ||
* @example | ||
* const app = new Application<{foo: string}>(); | ||
* @type {S} | ||
* @memberof Application | ||
*/ | ||
state: S; | ||
|
||
async listen(addr: string): Promise<void> { | ||
const middleware = compose(this._middleware); | ||
const server = serve(addr); | ||
for await(const req of server) { | ||
const context = new Context(this, req); | ||
await middleware(context); | ||
await req.respond(context.response.toServerResponse()); | ||
} | ||
} | ||
/** | ||
* 注册中间件 | ||
* @param middleware | ||
*/ | ||
use(middleware: Middleware): this { | ||
this._middleware.push(middleware); | ||
return this; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { Application } from './application.ts'; | ||
import { ServerRequest } from './deps.ts'; | ||
import { Response } from './response.ts'; | ||
|
||
export class Context<S extends object = { [key: string]: any }> { | ||
/** | ||
* 当期应用的引用 | ||
* | ||
* @type {Application<any>} | ||
* @memberof Context | ||
*/ | ||
app: Application<any>; | ||
/** | ||
* 请求对象 | ||
* | ||
* @type {*} | ||
* @memberof Context | ||
*/ | ||
request: any; | ||
/** | ||
* 响应对象 | ||
* | ||
* @type {*} | ||
* @memberof Context | ||
*/ | ||
response = new Response(); | ||
/** | ||
* 将状态传递给前端视图的对象。 这可以通过在创建新应用程序时提供通用状态参数来键入。 | ||
* | ||
* @example | ||
* const app = new Application<{foo: string}>(); | ||
* @type {S} | ||
* @memberof Context | ||
*/ | ||
state: S; | ||
constructor(app: Application<S>, serverRequest: ServerRequest) { | ||
this.app = app; | ||
this.state = app.state; | ||
this.request = serverRequest; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export { serve, ServerRequest, Response } from 'https://deno.land/[email protected]/http/server.ts'; | ||
export { Status, STATUS_TEXT } from 'https://deno.land/[email protected]/http/http_status.ts'; | ||
export { contentType } from 'https://deno.land/[email protected]/media_types/mod.ts'; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { | ||
green, | ||
cyan, | ||
bold, | ||
yellow | ||
} from 'https://deno.land/x/[email protected]/colors/mod.ts'; | ||
|
||
import { Application } from '../mod.ts'; | ||
(async() => { | ||
const app = new Application(); | ||
|
||
// 注册中间件 | ||
app.use(async (ctx, next) => { | ||
await next(); | ||
const rt = ctx.response.headers.get('X-Response-Time'); | ||
console.log( | ||
`${green(ctx.request.method)} ${cyan(ctx.request.url)} - ${bold( | ||
String(rt) | ||
)}` | ||
); | ||
}); | ||
app.use(async (ctx, next) => { | ||
const start = Date.now(); | ||
await next(); | ||
const ms = Date.now() - start; | ||
ctx.response.headers.set('X-Response-Time', `${ms}ms`); | ||
}); | ||
app.use(ctx => { | ||
ctx.response.body = 'Hellow World!'; | ||
}); | ||
|
||
const address = '0.0.0.0:8000'; | ||
console.log(bold('Start listening on ') + yellow(address)); | ||
await app.listen(address); | ||
console.log(bold('Finished.')) | ||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { Context } from './context.ts'; | ||
|
||
export interface Middleware { | ||
(context: Context, next: () => Promise<void>): Promise<void> | void | ||
} | ||
|
||
/** | ||
* 将多个中间件功能合成成一个中间件功能 | ||
* @param middleware | ||
*/ | ||
export function compose( | ||
middleware: Middleware[] | ||
): (context: Context) => Promise<void> { | ||
return function (context: Context, next?: () => Promise<void>) { | ||
let index = -1; | ||
/** | ||
* 调度 | ||
* @param i | ||
*/ | ||
async function dispatch(i: number) { | ||
if (i <= index) { | ||
throw new Error('next() called multiple times'); | ||
} | ||
index = i; | ||
let fn: Middleware | undefined = middleware[i]; | ||
if (i === middleware.length) { | ||
fn = next; | ||
} | ||
if (!fn) { | ||
return; | ||
} | ||
return fn(context, dispatch.bind(null, i + 1)); | ||
} | ||
return dispatch(0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { Application } from './application.ts' | ||
export { Context } from './context.ts' |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { contentType, Status } from './deps.ts'; | ||
import { isHtml } from './util.ts'; | ||
/** | ||
* 服务响应对象 | ||
*/ | ||
interface ServerResponse { | ||
status?: number, | ||
headers?: Headers; | ||
body?:Uint8Array; | ||
} | ||
|
||
/** | ||
* body 类型 | ||
*/ | ||
const BODY_TYPES = ['string', 'number', 'bigint', 'boolean', 'symbol']; | ||
|
||
/** | ||
* body 编码对象 | ||
*/ | ||
const encoder = new TextEncoder(); | ||
|
||
export class Response { | ||
/** | ||
* 响应对象 body | ||
* | ||
* @type {*} | ||
* @memberof Response | ||
*/ | ||
body?: any; | ||
/** | ||
* 响应头 | ||
*/ | ||
headers = new Headers(); | ||
/** | ||
* 响应状态码 | ||
* | ||
* @type {Status} | ||
* @memberof Response | ||
*/ | ||
status?: Status; | ||
/** | ||
* 响应对象 mime 类型 | ||
* | ||
* @type {string} | ||
* @memberof Response | ||
*/ | ||
type: string; | ||
|
||
toServerResponse(): ServerResponse { | ||
const body = this._getBody(); | ||
this._setContentType(); | ||
// 如果没有正文且没有内容类型且没有设置长度,则设置内容长度为0 | ||
if(!( | ||
body || | ||
this.headers.has('Content-Type') || | ||
this.headers.has('Content-Length') | ||
)) { | ||
this.headers.append('Content-Length', '0'); | ||
} | ||
return { | ||
status: this.status || (body ? Status.OK : Status.NotFound), | ||
body, | ||
headers: this.headers | ||
} | ||
} | ||
/** | ||
* 获取响应对象 | ||
* @private | ||
* @memberof Response | ||
*/ | ||
private _getBody(): Uint8Array | undefined { | ||
const typeofBody = typeof this.body; | ||
let result: Uint8Array | undefined; | ||
if (BODY_TYPES.includes(typeofBody)) { | ||
const bodyText = String(this.body); | ||
result = encoder.encode(bodyText); | ||
this.type = this.type || isHtml(bodyText) ? "html" : "text/plain"; | ||
} else if(this.body instanceof Uint8Array) { | ||
result = this.body; | ||
} else if(typeofBody === 'object' && this.body !== null) { | ||
result = encoder.encode(JSON.stringify(this.body)); | ||
this.type = this.type || 'json'; | ||
} | ||
return result; | ||
} | ||
/** | ||
* 设置响应对象内容类型字段 | ||
* @private | ||
* @memberof Response | ||
*/ | ||
private _setContentType() { | ||
if(this.type) { | ||
const contentTypeString = contentType(this.type); | ||
if(contentTypeString && !this.headers.has("Content-Type")) { | ||
this.headers.append("Content-Type", contentTypeString); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/** | ||
* 判断是否为 html | ||
* @param value | ||
*/ | ||
export function isHtml(value: string): boolean { | ||
return /^\s*<(?:!DOCTYPE|html|body)/i.test(value); | ||
} |