hile-http
npx skills add https://github.com/cevio/hile --skill hile-http
Agent 安装分布
Skill 文档
@hile/http
@hile/http æ¯åºäº Koa + find-my-way ç HTTP æå¡æ¡æ¶ãæ¬ææ¡£æ¯é¢å AI ç¼ç 模åå人类å¼åè
ç 代ç çæè§èï¼é
读ååºè½æ£ç¡®å°ä½¿ç¨æ¬åºç¼åç¬¦åæ¶æè§åç HTTP æå¡ä»£ç ã
1. æ¶ææ»è§
@hile/http æä¾ä¸å±æ½è±¡ï¼
- Http ç±» â æå¡æ ¸å¿ï¼å°è£ Koa å®ä¾åè·¯ç±å¨ï¼æä¾ä¸é´ä»¶æ³¨åãè·¯ç±æ³¨ååæå¡å¯å
- defineController â æ§å¶å¨å®ä¹å½æ°ï¼å° HTTP æ¹æ³ + ä¸é´ä»¶ + å¤ç彿°æå 为æ åæ§å¶å¨å¯¹è±¡
- Loader â è·¯ç±å è½½å¨ï¼æ¯ææå¨ç¼è¯ç»å®ååºäºæä»¶ç³»ç»çèªå¨è·¯ç±å è½½
å
¸å工使µï¼å®ä¹æ§å¶å¨ â éè¿ Loader ç¼è¯ç»å®å° Http â å¯å¨æå¡
ä¾èµå ³ç³»
@hile/http
âââ koa â Web æ¡æ¶
âââ koa-compose â ä¸é´ä»¶ç»å
âââ find-my-way â 髿§è½è·¯ç±å¹é
âââ glob â æä»¶ç³»ç»è·¯ç±æ«æ
2. ç±»åç¾å
çæä»£ç æ¶ï¼å¿ é¡»ä¸¥æ ¼éµå¾ªä»¥ä¸ç±»åï¼
import { Context, Middleware } from 'koa'
import { HTTPMethod } from 'find-my-way'
// Http é
ç½®ï¼port å¿
å¡«ï¼å
¶ä½å¯éï¼
type HttpProps = {
port: number
keys?: string[]
ignoreDuplicateSlashes?: boolean // é»è®¤ true
ignoreTrailingSlash?: boolean // é»è®¤ true
maxParamLength?: number // é»è®¤ +Infinity
allowUnsafeRegex?: boolean // é»è®¤ true
caseSensitive?: boolean // é»è®¤ true
}
// æ§å¶å¨å¤ç彿°ï¼æ¥æ¶ Koa Contextï¼è¿åå¼èªå¨èµç» ctx.body
type ControllerFunction = (ctx: Context) => unknown | Promise<unknown>
// æ§å¶å¨æ³¨åä¿¡æ¯ï¼ç± defineController è¿å
interface ControllerRegisterProps {
id: number
method: HTTPMethod
middlewares: Middleware[]
data: Record<string, any> // ç¼è¯å data.url ä¼è¢«èªå¨è®¾ç½®
}
// Loader ç¼è¯é项
interface LoaderCompileOptions {
defaultSuffix?: string // é»è®¤ '/index'ï¼å¹é
æ¶å»é¤è¯¥åç¼
prefix?: string // è·¯ç±åç¼
}
// Loader æä»¶å è½½é项
type LoaderFromOptions = {
suffix?: string // æä»¶åç¼æ è®°ï¼é»è®¤ 'controller'
} & LoaderCompileOptions
3. 代ç çææ¨¡æ¿
3.1 å建 Http æå¡ï¼å¿ é¡»éµå¾ªç模å¼ï¼
模æ¿ï¼
import { Http } from '@hile/http'
const http = new Http({
port: 3000,
})
è§åï¼
- æé åæ°
portå¿ å¡« - ä¸éè¦æå¨ä¼
keysï¼æ¡æ¶ä¼èªå¨çæéæºå¯é¥ - è·¯ç±å¨é»è®¤é ç½®ï¼å¿½ç¥é夿æ ã忽ç¥å°¾é¨ææ ã大å°åææï¼é常æ éä¿®æ¹
3.2 注åå ¨å±ä¸é´ä»¶
模æ¿ï¼
http.use(async (ctx, next) => {
const start = Date.now()
await next()
ctx.set('X-Response-Time', `${Date.now() - start}ms`)
})
è§åï¼
use()å¿ é¡»å¨listen()ä¹åè°ç¨- ä¸é´ä»¶ç¾ååºå®ä¸º
(ctx: Context, next: Next) => Promise<void> use()è¿åthisï¼æ¯æé¾å¼è°ç¨- å ¨å±ä¸é´ä»¶å¯¹ ææè·¯ç± çæ
3.3 æå¨æ³¨åè·¯ç±
模æ¿ï¼
// 注åè·¯ç±ï¼è¿å注é彿°
const off = http.get('/api/users', async (ctx) => {
ctx.body = { users: [] }
})
// éè¦æ¶å¯æ³¨éè·¯ç±
off()
è§åï¼
- 使ç¨
http.get()/http.post()/http.put()/http.delete()/http.trace()å¿«æ·æ¹æ³ - æä½¿ç¨
http.route(method, url, ...middlewares)æå®ä»»æ HTTP æ¹æ³ - ææè·¯ç±æ³¨åæ¹æ³ é½è¿å注éåè°å½æ°ï¼è°ç¨åè·¯ç±ä¸åå¹é
- è·¯ç±æ¯æè·¯å¾åæ°ï¼
/users/:id - è·¯ç±æ¯æå¤ä¸ªä¸é´ä»¶ï¼æé¡ºåºæ§è¡
3.4 å®ä¹æ§å¶å¨ï¼æä»¶è·¯ç±æ¨¡å¼ï¼å¿ é¡»éµå¾ªç模å¼ï¼
模æ¿ï¼ç®æ´åæ³ â æ é¢å¤ä¸é´ä»¶ï¼ï¼
import { defineController } from '@hile/http'
export default defineController('GET', (ctx) => {
return { message: 'hello' }
})
模æ¿ï¼å¸¦ä¸é´ä»¶ï¼ï¼
import { defineController } from '@hile/http'
const authMiddleware = async (ctx, next) => {
if (!ctx.headers.authorization) {
ctx.throw(401)
}
await next()
}
export default defineController('POST', [authMiddleware], (ctx) => {
return { data: ctx.request.body }
})
è§åï¼
- 第ä¸ä¸ªåæ° å¿
é¡» æ¯ HTTP æ¹æ³å符串ï¼
'GET'|'POST'|'PUT'|'DELETE'ç - 第äºä¸ªåæ°å¯ä»¥æ¯ æ§å¶å¨å½æ°ï¼æ ä¸é´ä»¶æ¶ï¼æ ä¸é´ä»¶æ°ç»ï¼æä¸é´ä»¶æ¶ï¼
- æä¸é´ä»¶æ°ç»æ¶ï¼ç¬¬ä¸ä¸ªåæ° å¿ é¡» æ¯æ§å¶å¨å½æ°
- æ§å¶å¨å½æ°æ¥æ¶
ctx: Contextï¼è¿åå¼éundefinedæ¶èªå¨èµç»ctx.body - è¿å
undefinedæ¶ä¸ä¿®æ¹ctx.bodyï¼ç¨äºæµå¼ååºææå¨è®¾ç½®ï¼ - æ§å¶å¨æä»¶ å¿
é¡»
export default导åºdefineControllerçè¿åå¼ - ä¸ä¸ªæä»¶å¯ä»¥å¯¼åºå个æ§å¶å¨ææ§å¶å¨æ°ç»ï¼ç¨äºåä¸è·¯å¾æ³¨åå¤ä¸ª HTTP æ¹æ³ï¼
3.5 æä»¶ç³»ç»è·¯ç±ï¼èªå¨å è½½ï¼
模æ¿ï¼
import { Http } from '@hile/http'
const http = new Http({ port: 3000 })
const off = await http.load('./src/controllers', {
suffix: 'controller', // å¹é
*.controller.{ts,js} æä»¶
defaultSuffix: '/index', // index æä»¶æ å°å°ç¶è·¯å¾
prefix: '/api', // ææè·¯ç±æ·»å åç¼
})
è§åï¼
load()æ«æç®å½ä¸ææå¹é **/*.{suffix}.{ts,js}çæä»¶- æä»¶è·¯å¾èªå¨è½¬æ¢ä¸ºè·¯ç±è·¯å¾ï¼å»é¤åç¼æ è®°åæä»¶æ©å±åï¼
load()è¿åPromise<() => void>ï¼å¿ é¡»await- è¿åçæ³¨é彿°è°ç¨åç§»é¤ææå è½½çè·¯ç±
3.6 æä»¶è·¯ç±è·¯å¾è½¬æ¢è§å
æä»¶ç³»ç»è·¯å¾å°è·¯ç±è·¯å¾ç转æ¢éµå¾ªä»¥ä¸è§åï¼
| æä»¶è·¯å¾ | è·¯ç±è·¯å¾ | 说æ |
|---|---|---|
users/index.controller.ts |
/users æ / |
index 被 defaultSuffix å»é¤ |
users/list.controller.ts |
/users/list |
æ®éè·¯å¾ |
users/[id].controller.ts |
/users/:id |
[param] 转æ¢ä¸º :param |
[category]/[id].controller.ts |
/:category/:id |
å¤åæ° |
index.controller.ts |
/ |
æ ¹è·¯å¾ |
è½¬æ¢æµç¨ï¼
- å»é¤æä»¶åä¸ç
.{suffix}.{ts,js}åç¼ - è·¯å¾ä¸ä»¥
/å¼å¤´æ¶èªå¨è¡¥å - è·¯å¾ä»¥
defaultSuffixï¼é»è®¤/indexï¼ç»å°¾æ¶å»é¤è¯¥åç¼ - å»é¤å为空åé置为
/ - æ·»å
prefixåç¼ï¼å¦ææå®ï¼ - å°
[param]æ¿æ¢ä¸º:param
3.7 å¯å¨æå¡
模æ¿ï¼
const close = await http.listen((server) => {
console.log(`Server running on port ${http.port}`)
})
// å
³éæå¡
close()
è§åï¼
listen()è¿åPromise<() => void>ï¼å¿ é¡»await- å¯éä¼ å
¥
onListenåè°ï¼å¨æå¡ç«¯å£ç»å®åæ§è¡ï¼æ¥æ¶Server对象 - è¿åçå ³é彿°è°ç¨å忢æå¡
4. 强å¶è§åï¼çæä»£ç æ¶å¿ é¡»éµå®ï¼
| # | è§å | åå |
|---|---|---|
| 1 | å
¨å±ä¸é´ä»¶ å¿
é¡» å¨ listen() ä¹åéè¿ use() 注å |
listen() å
é¨å»ç»ä¸é´ä»¶æ |
| 2 | æ§å¶å¨æä»¶ å¿
é¡» export default å¯¼åº |
Loader éè¿ import(path).default å è½½ |
| 3 | æ§å¶å¨æä»¶å½å å¿
é¡» 以 {suffix}.ts ç»å°¾ |
é»è®¤ä¸º *.controller.tsï¼Loader éè¿æä»¶ååç¼å¹é
|
| 4 | defineController çæ§å¶å¨å½æ°åæ°æ¯ ctxï¼ä¸æ¯ ctx, nextï¼ |
æ¡æ¶èªå¨å
è£
为ä¸é´ä»¶ï¼ctx.body ç±è¿åå¼èªå¨è®¾ç½® |
| 5 | æ§å¶å¨å½æ°éè¦è¿åååºæ°æ®æ¶ ç´æ¥ return | ä¸è¦æå¨ ctx.body = ... ç¶åå return å¼ |
| 6 | è·¯ç±åæ°ä½¿ç¨ [param] æ¹æ¬å·è¯æ³ |
Loader ä¼èªå¨è½¬æ¢ä¸º find-my-way ç :param æ ¼å¼ |
| 7 | load() è¿å Promiseï¼å¿
é¡» await |
å 鍿弿¥æä»¶æ«æå卿 import |
| 8 | åä¸è·¯å¾çå¤ä¸ª HTTP æ¹æ³æ§å¶å¨ 导åºä¸ºæ°ç» | export default [getController, postController] |
| 9 | ä¸è¦ 卿§å¶å¨å½æ°ä¸è°ç¨ next() |
æ§å¶å¨æ¯ç»ç«¯å¤çå¨ï¼ä¸æ¯ä¸é´ä»¶ |
| 10 | éè¦ next() çé»è¾ æ¾å¨ä¸é´ä»¶æ°ç»ä¸ |
defineController('GET', [myMiddleware], handler) |
5. 宿´ç¤ºä¾ï¼é¡¹ç®ç»æ
src/
âââ controllers/
â âââ index.controller.ts # GET /api
â âââ users/
â â âââ index.controller.ts # GET /api/users
â â âââ [id].controller.ts # GET /api/users/:id, PUT /api/users/:id
â âââ posts/
â âââ index.controller.ts # GET /api/posts, POST /api/posts
â âââ [id].controller.ts # GET /api/posts/:id
âââ middlewares/
â âââ logger.ts
â âââ auth.ts
âââ main.ts
middlewares/logger.ts
import { Middleware } from 'koa'
export const logger: Middleware = async (ctx, next) => {
const start = Date.now()
await next()
const ms = Date.now() - start
console.log(`${ctx.method} ${ctx.url} - ${ctx.status} ${ms}ms`)
}
middlewares/auth.ts
import { Middleware } from 'koa'
export const auth: Middleware = async (ctx, next) => {
const token = ctx.headers.authorization
if (!token) {
ctx.throw(401, 'Unauthorized')
}
await next()
}
controllers/index.controller.ts
import { defineController } from '@hile/http'
export default defineController('GET', () => {
return { status: 'ok', timestamp: Date.now() }
})
controllers/users/index.controller.ts
import { defineController } from '@hile/http'
export default defineController('GET', async (ctx) => {
return { users: [] }
})
controllers/users/[id].controller.ts
import { defineController } from '@hile/http'
import { auth } from '../../middlewares/auth'
const getUser = defineController('GET', async (ctx) => {
const { id } = ctx.params
return { id, name: `User ${id}` }
})
const updateUser = defineController('PUT', [auth], async (ctx) => {
const { id } = ctx.params
return { id, updated: true }
})
export default [getUser, updateUser]
controllers/posts/index.controller.ts
import { defineController } from '@hile/http'
import { auth } from '../../middlewares/auth'
const getPosts = defineController('GET', async (ctx) => {
return { posts: [] }
})
const createPost = defineController('POST', [auth], async (ctx) => {
return { created: true }
})
export default [getPosts, createPost]
controllers/posts/[id].controller.ts
import { defineController } from '@hile/http'
export default defineController('GET', async (ctx) => {
const { id } = ctx.params
return { id, title: `Post ${id}` }
})
main.ts
import { Http } from '@hile/http'
import { logger } from './middlewares/logger'
async function main() {
const http = new Http({ port: 3000 })
http.use(logger)
await http.load('./src/controllers', {
suffix: 'controller',
prefix: '/api',
})
const close = await http.listen(() => {
console.log(`Server running on http://localhost:${http.port}`)
})
}
main()
çæçè·¯ç±è¡¨
| æ¹æ³ | è·¯å¾ | æ¥æºæä»¶ |
|---|---|---|
| GET | /api |
index.controller.ts |
| GET | /api/users |
users/index.controller.ts |
| GET | /api/users/:id |
users/[id].controller.ts |
| PUT | /api/users/:id |
users/[id].controller.ts |
| GET | /api/posts |
posts/index.controller.ts |
| POST | /api/posts |
posts/index.controller.ts |
| GET | /api/posts/:id |
posts/[id].controller.ts |
6. 忍¡å¼ï¼çæä»£ç æ¶å¿ é¡»é¿å ï¼
6.1 ä¸è¦å¨æ§å¶å¨å½æ°ä¸è°ç¨ next()
// â éè¯¯ï¼æ§å¶å¨å½æ°ä¸æ¯ä¸é´ä»¶ï¼ä¸åºè°ç¨ next
export default defineController('GET', async (ctx, next) => {
await next()
return { data: 'hello' }
})
// â æ£ç¡®ï¼ç´æ¥è¿åæ°æ®
export default defineController('GET', async (ctx) => {
return { data: 'hello' }
})
6.2 ä¸è¦åæ¶ return åæå¨è®¾ç½® ctx.body
// â é误ï¼éå¤è®¾ç½®ï¼return å¼ä¼è¦ç
export default defineController('GET', async (ctx) => {
ctx.body = { wrong: true }
return { right: true }
})
// â æ£ç¡®ï¼åªç¨ return
export default defineController('GET', async (ctx) => {
return { right: true }
})
// â 乿£ç¡®ï¼åªç¨ ctx.bodyï¼return undefined
export default defineController('GET', async (ctx) => {
ctx.body = fs.createReadStream('file.txt')
})
6.3 ä¸è¦å¨ listen() ä¹å注åå ¨å±ä¸é´ä»¶
// â é误ï¼listen åæ³¨åçä¸é´ä»¶ä¸ä¼çæ
const close = await http.listen()
http.use(lateMiddleware)
// â æ£ç¡®ï¼å¨ listen ä¹å注å
http.use(earlyMiddleware)
const close = await http.listen()
6.4 ä¸è¦å¿è®° export default
// â é误ï¼Loader æ æ³å è½½ï¼å 为没æ default 导åº
export const myController = defineController('GET', () => 'hello')
// â æ£ç¡®
export default defineController('GET', () => 'hello')
6.5 ä¸è¦æå¨åè·¯å¾åæ°ä¸º :param æ ¼å¼
// â éè¯¯ï¼æä»¶åä½¿ç¨ :paramï¼æä»¶ç³»ç»ä¸æ¯æåå·ï¼
// æä»¶å: users/:id.controller.ts
// â æ£ç¡®ï¼æä»¶åä½¿ç¨ [param]
// æä»¶å: users/[id].controller.ts
6.6 ä¸è¦æ load() ç await éæ¼
// â é误ï¼load æ¯å¼æ¥çï¼ä¸ await ä¼å¯¼è´è·¯ç±æªæ³¨åå°±å¯å¨æå¡
http.load('./controllers')
const close = await http.listen()
// â æ£ç¡®
await http.load('./controllers')
const close = await http.listen()
7. API 鿥
Http ç±»
| æ¹æ³ | ç¾å | 说æ |
|---|---|---|
constructor |
(props: HttpProps) |
å建 Http æå¡å®ä¾ |
port |
number (getter) |
è·å端å£å· |
use |
(middleware: Middleware) => this |
注åå ¨å±ä¸é´ä»¶ï¼æ¯æé¾å¼è°ç¨ |
listen |
(onListen?) => Promise<() => void> |
å¯å¨æå¡ï¼è¿åå ³é彿° |
get |
(url, ...middlewares) => () => void |
注å GET è·¯ç±ï¼è¿å注é彿° |
post |
(url, ...middlewares) => () => void |
注å POST è·¯ç±ï¼è¿å注é彿° |
put |
(url, ...middlewares) => () => void |
注å PUT è·¯ç±ï¼è¿å注é彿° |
delete |
(url, ...middlewares) => () => void |
注å DELETE è·¯ç±ï¼è¿å注é彿° |
trace |
(url, ...middlewares) => () => void |
注å TRACE è·¯ç±ï¼è¿å注é彿° |
route |
(method, url, ...middlewares) => () => void |
注åä»»ææ¹æ³è·¯ç±ï¼è¿å注é彿° |
load |
(directory, options?) => Promise<() => void> |
æä»¶ç³»ç»è·¯ç±å è½½ï¼è¿å注é彿° |
defineController 彿°
| è°ç¨å½¢å¼ | 说æ |
|---|---|
defineController(method, fn: ControllerFunction) |
æ ä¸é´ä»¶ï¼ç´æ¥ä¼ æ§å¶å¨å½æ° |
defineController(method, middlewares: Middleware[], fn: ControllerFunction) |
æä¸é´ä»¶æ°ç» + æ§å¶å¨å½æ° |
è¿åå¼ ControllerRegisterPropsï¼éæ³åï¼ï¼
| åæ®µ | ç±»å | 说æ |
|---|---|---|
id |
number |
å¯ä¸ ID |
method |
HTTPMethod |
HTTP æ¹æ³ |
middlewares |
Middleware[] |
ä¸é´ä»¶æ°ç»ï¼å«æ§å¶å¨å è£ åçå°¾é¨ä¸é´ä»¶ï¼ |
data |
Record<string, any> |
ç¼è¯å data.url 为å®é
è·¯ç±è·¯å¾ |
Loader ç±»
| æ¹æ³ | ç¾å | 说æ |
|---|---|---|
compile |
(path, controllers: ControllerRegisterProps | ControllerRegisterProps[], options?) => () => void |
æå¨ç¼è¯ç»å®è·¯ç± |
from |
(directory, options?) => Promise<() => void> |
æä»¶ç³»ç»æ¹éå è½½ |
8. å 鍿ºå¶ï¼ä¾çè§£ï¼ä¸ä¾ç´æ¥è°ç¨ï¼
Http å é¨ç»æ
| ç»ä»¶ | ç±»å | 说æ |
|---|---|---|
koa |
Koa |
Koa å®ä¾ï¼å¤çä¸é´ä»¶ç®¡çº¿ |
router |
Instance |
find-my-way å è£ å®ä¾ï¼é«æ§è½è·¯ç±å¹é |
loader |
Loader |
è·¯ç±å è½½å¨å®ä¾ |
server |
Server? |
Node.js HTTP Serverï¼listen åå建 |
è·¯ç±å¹é æµç¨
HTTP 请æ±å°è¾¾
â
ââ Koa ä¸é´ä»¶ç®¡çº¿ï¼use 注åçå
¨å±ä¸é´ä»¶æé¡ºåºæ§è¡ï¼
â
ââ router.routes()ï¼æåä¸ä¸ªä¸é´ä»¶ï¼
â
ââ find-my-way.find(method, path)
â ââ å¹é
æå â 设置 ctx.params, ctx.store â æ§è¡è·¯ç±ä¸é´ä»¶
â ââ å¹é
失败 â defaultRoute â è°ç¨ next()ï¼è¿å 404ï¼
â
ââ è·¯ç±ä¸é´ä»¶ï¼koa-compose ç»åï¼
ââ ç¨æ·å®ä¹çä¸é´ä»¶ï¼defineController ç middlewares åæ°ï¼
ââ æ§å¶å¨å
è£
ä¸é´ä»¶ï¼æ§è¡ fn(ctx)ï¼è¿åå¼èµç» ctx.bodyï¼
Loader æä»¶è·¯å¾è½¬æ¢æµç¨
æä»¶: users/[id].controller.ts
â
ââ å»é¤åç¼ â users/[id].
ââ å»é¤æ©å±ååç¹ â users/[id]
ââ è¡¥å
åå¯¼ææ â /users/[id]
ââ æ£æ¥ defaultSuffix â ä¸å¹é
ï¼ä¿æ
ââ æ·»å prefix â /api/users/[id]
ââ æ¿æ¢åæ° â /api/users/:id
æ§å¶å¨å è£ æºå¶
defineController å°æ§å¶å¨å½æ°å
è£
为 Koa ä¸é´ä»¶å¹¶è¿½å å° middlewares æ«å°¾ï¼
defineController('GET', [mw1, mw2], fn)
â middlewares = [mw1, mw2, async (ctx) => {
const result = await fn(ctx)
if (result !== undefined) ctx.body = result
}]
æ§å¶å¨å½æ° fn çè¿åå¼ç±»å为 unknown | Promise<unknown>ï¼
- è¿åé
undefinedå¼ â èªå¨è®¾ç½®ctx.body - è¿å
undefinedâ ä¸ä¿®æ¹ctx.bodyï¼ç¨äºæµãæå¨è®¾ç½®çåºæ¯ï¼
9. ä¸ hileï¼coreï¼éæ
@hile/http é常éè¿ hile æå¡å®¹å¨è¿è¡ç®¡çï¼
import { defineService, loadService } from '@hile/core'
import { Http } from '@hile/http'
export const httpService = defineService(async (shutdown) => {
const http = new Http({ port: 3000 })
http.use(logger)
await http.load('./src/controllers', { suffix: 'controller', prefix: '/api' })
const close = await http.listen(() => {
console.log(`Server running on port ${http.port}`)
})
shutdown(close)
return http
})
è§åï¼
- å°
listen()è¿åçclose彿°æ³¨å为shutdownåè° - éè¿
loadService(httpService)å¨å ¶ä»å°æ¹è·å Http å®ä¾
10. å¼å
pnpm --filter @hile/http install
pnpm --filter @hile/http build
pnpm --filter @hile/http test
pnpm --filter @hile/http dev
ææ¯æ ï¼ TypeScript, Koa, find-my-way, Vitest
License
MIT