feat: 更新cookie模块和添加res和req在app的请求当中
This commit is contained in:
parent
c99d03550e
commit
dc2f282f4b
@ -14,6 +14,7 @@
|
|||||||
"@kevisual/router": "link:../.."
|
"@kevisual/router": "link:../.."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"cookie": "^1.0.2",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.5.4"
|
"typescript": "^5.5.4"
|
||||||
}
|
}
|
||||||
|
78
demo/simple/src/check-handle-context/app.ts
Normal file
78
demo/simple/src/check-handle-context/app.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import { App } from '@kevisual/router';
|
||||||
|
import { QueryRouterServer } from '@kevisual/router';
|
||||||
|
const app = new App();
|
||||||
|
const queryApp = new QueryRouterServer();
|
||||||
|
|
||||||
|
// queryApp
|
||||||
|
// .route({
|
||||||
|
// path: 'api',
|
||||||
|
// })
|
||||||
|
// .define(async (ctx) => {
|
||||||
|
// ctx.throw(404, 'Not Found');
|
||||||
|
// ctx.throw(500, 'Internal Server Error');
|
||||||
|
// })
|
||||||
|
// .addTo(app);
|
||||||
|
|
||||||
|
app
|
||||||
|
.route({
|
||||||
|
path: 'hello',
|
||||||
|
})
|
||||||
|
.define(async (ctx) => {
|
||||||
|
// console.log('hello', ctx);
|
||||||
|
// console.log('hello', ctx.res);
|
||||||
|
console.log('hello', ctx.query.cookies);
|
||||||
|
// ctx.res?.cookie?.('token', 'abc', {
|
||||||
|
// domain: '*', // 设置为顶级域名,允许跨子域共享
|
||||||
|
// // httpOnly: true,
|
||||||
|
// // secure: true,
|
||||||
|
// // sameSite: 'Lax',
|
||||||
|
// });
|
||||||
|
ctx.res.cookie('token', 'abc', {
|
||||||
|
// domain: '*', // 设置为顶级域名,允许跨子域共享
|
||||||
|
// httpOnly: true,
|
||||||
|
// secure: true,
|
||||||
|
// sameSite: 'Lax',
|
||||||
|
});
|
||||||
|
ctx.res.cookie('test_cookie', 'abc', {
|
||||||
|
maxAge: 0,
|
||||||
|
path: '/api/router',
|
||||||
|
});
|
||||||
|
ctx.res.cookie('test_cookie', 'abc', {
|
||||||
|
maxAge: 0,
|
||||||
|
path: '/',
|
||||||
|
});
|
||||||
|
ctx.res.cookie('user', 'abc', {
|
||||||
|
maxAge: 0,
|
||||||
|
});
|
||||||
|
ctx.res.cookie('session', 'abc', {
|
||||||
|
maxAge: 0,
|
||||||
|
});
|
||||||
|
ctx.res.cookie('preferences', 'abc', {
|
||||||
|
maxAge: 0,
|
||||||
|
});
|
||||||
|
// const cookies = [
|
||||||
|
// cookie.serialize('user', 'john_doe', {
|
||||||
|
// httpOnly: true,
|
||||||
|
// maxAge: 60 * 60 * 24 * 7, // 1 week
|
||||||
|
// sameSite: 'lax',
|
||||||
|
// }),
|
||||||
|
// cookie.serialize('session', 'xyz123', {
|
||||||
|
// httpOnly: true,
|
||||||
|
// maxAge: 60 * 60 * 24, // 1 day
|
||||||
|
// }),
|
||||||
|
// cookie.serialize('preferences', JSON.stringify({ theme: 'dark' }), {
|
||||||
|
// httpOnly: false, // Accessible via JavaScript
|
||||||
|
// maxAge: 60 * 60 * 24 * 30, // 1 month
|
||||||
|
// }),
|
||||||
|
// ];
|
||||||
|
// ctx.res.setHeader('Set-Cookie', cookies);
|
||||||
|
ctx.res.end('hello' + Math.random().toString(32).slice(2));
|
||||||
|
|
||||||
|
ctx.end = true;
|
||||||
|
return;
|
||||||
|
ctx.body = 'world';
|
||||||
|
})
|
||||||
|
.addTo(app);
|
||||||
|
app.listen(3100, () => {
|
||||||
|
console.log('listening on port http://localhost:3100');
|
||||||
|
});
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package",
|
"$schema": "https://json.schemastore.org/package",
|
||||||
"name": "@kevisual/router",
|
"name": "@kevisual/router",
|
||||||
"version": "0.0.6-alpha-2",
|
"version": "0.0.6-alpha-3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"module": "dist/index.js",
|
"module": "dist/index.js",
|
||||||
@ -26,6 +26,7 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/node": "^22.8.6",
|
"@types/node": "^22.8.6",
|
||||||
"@types/ws": "^8.5.13",
|
"@types/ws": "^8.5.13",
|
||||||
|
"cookie": "^1.0.2",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"nanoid": "^5.0.8",
|
"nanoid": "^5.0.8",
|
||||||
"rollup": "^4.24.3",
|
"rollup": "^4.24.3",
|
||||||
|
@ -16,7 +16,7 @@ export default [
|
|||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
resolve(), // 使用 @rollup/plugin-node-resolve 解析 node_modules 中的模块
|
resolve(), // 使用 @rollup/plugin-node-resolve 解析 node_modules 中的模块
|
||||||
// commonjs(),
|
commonjs(),
|
||||||
typescript(), // 使用 @rollup/plugin-typescript 处理 TypeScript 文件
|
typescript(), // 使用 @rollup/plugin-typescript 处理 TypeScript 文件
|
||||||
],
|
],
|
||||||
external: ['ws'],
|
external: ['ws'],
|
||||||
|
11
src/app.ts
11
src/app.ts
@ -1,5 +1,5 @@
|
|||||||
import { QueryRouter, Route, RouteContext, RouteOpts } from './route.ts';
|
import { QueryRouter, Route, RouteContext, RouteOpts } from './route.ts';
|
||||||
import { Server, Cors, ServerOpts } from './server/server.ts';
|
import { Server, Cors, ServerOpts, HandleCtx } from './server/server.ts';
|
||||||
import { WsServer } from './server/ws-server.ts';
|
import { WsServer } from './server/ws-server.ts';
|
||||||
import { CustomError } from './result/error.ts';
|
import { CustomError } from './result/error.ts';
|
||||||
|
|
||||||
@ -14,7 +14,12 @@ type AppOptions<T = {}> = {
|
|||||||
io?: boolean;
|
io?: boolean;
|
||||||
ioOpts?: { routerHandle?: RouterHandle; routerContext?: RouteContext<T>; path?: string };
|
ioOpts?: { routerHandle?: RouterHandle; routerContext?: RouteContext<T>; path?: string };
|
||||||
};
|
};
|
||||||
export class App<T = {}> {
|
export type AppReqRes = HandleCtx;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 封装了 Router 和 Server 的 App 模块,处理http的请求和响应,内置了 Cookie 和 Token 和 res 的处理
|
||||||
|
*/
|
||||||
|
export class App<T = {}, U = AppReqRes> {
|
||||||
router: QueryRouter;
|
router: QueryRouter;
|
||||||
server: Server;
|
server: Server;
|
||||||
io: WsServer;
|
io: WsServer;
|
||||||
@ -55,7 +60,7 @@ export class App<T = {}> {
|
|||||||
add = this.addRoute;
|
add = this.addRoute;
|
||||||
|
|
||||||
Route = Route;
|
Route = Route;
|
||||||
route(opts: RouteOpts): Route;
|
route(opts: RouteOpts): Route<U>;
|
||||||
route(path: string, key?: string): Route;
|
route(path: string, key?: string): Route;
|
||||||
route(path: string, opts?: RouteOpts): Route;
|
route(path: string, opts?: RouteOpts): Route;
|
||||||
route(path: string, key?: string, opts?: RouteOpts): Route;
|
route(path: string, key?: string, opts?: RouteOpts): Route;
|
||||||
|
28
src/route.ts
28
src/route.ts
@ -69,7 +69,7 @@ export type RouteOpts = {
|
|||||||
export type DefineRouteOpts = Omit<RouteOpts, 'idUsePath' | 'verify' | 'verifyKey' | 'nextRoute'>;
|
export type DefineRouteOpts = Omit<RouteOpts, 'idUsePath' | 'verify' | 'verifyKey' | 'nextRoute'>;
|
||||||
const pickValue = ['path', 'key', 'id', 'description', 'type', 'validator', 'middleware'] as const;
|
const pickValue = ['path', 'key', 'id', 'description', 'type', 'validator', 'middleware'] as const;
|
||||||
export type RouteInfo = Pick<Route, (typeof pickValue)[number]>;
|
export type RouteInfo = Pick<Route, (typeof pickValue)[number]>;
|
||||||
export class Route {
|
export class Route<U = { [key: string]: any }> {
|
||||||
path?: string;
|
path?: string;
|
||||||
key?: string;
|
key?: string;
|
||||||
id?: string;
|
id?: string;
|
||||||
@ -207,9 +207,9 @@ export class Route {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
define<T extends { [key: string]: any } = RouterContextT>(opts: DefineRouteOpts): this;
|
define<T extends { [key: string]: any } = RouterContextT>(opts: DefineRouteOpts): this;
|
||||||
define<T extends { [key: string]: any } = RouterContextT>(fn: Run<T>): this;
|
define<T extends { [key: string]: any } = RouterContextT>(fn: Run<T & U>): this;
|
||||||
define<T extends { [key: string]: any } = RouterContextT>(key: string, fn: Run<T>): this;
|
define<T extends { [key: string]: any } = RouterContextT>(key: string, fn: Run<T & U>): this;
|
||||||
define<T extends { [key: string]: any } = RouterContextT>(path: string, key: string, fn: Run<T>): this;
|
define<T extends { [key: string]: any } = RouterContextT>(path: string, key: string, fn: Run<T & U>): this;
|
||||||
define(...args: any[]) {
|
define(...args: any[]) {
|
||||||
const [path, key, opts] = args;
|
const [path, key, opts] = args;
|
||||||
// 全覆盖,所以opts需要准确,不能由idUsePath 需要check的变量
|
// 全覆盖,所以opts需要准确,不能由idUsePath 需要check的变量
|
||||||
@ -516,15 +516,19 @@ export class QueryRouter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
getHandle<T = any>(router: QueryRouter, wrapperFn?: HandleFn<T>, ctx?: RouteContext) {
|
getHandle<T = any>(router: QueryRouter, wrapperFn?: HandleFn<T>, ctx?: RouteContext) {
|
||||||
return async (msg: { path: string; key?: string; [key: string]: any }) => {
|
return async (msg: { path: string; key?: string; [key: string]: any }, handleContext?: RouteContext) => {
|
||||||
const context = { ...ctx };
|
try {
|
||||||
const res = await router.parse(msg, context);
|
const context = { ...ctx, ...handleContext };
|
||||||
if (wrapperFn) {
|
const res = await router.parse(msg, context);
|
||||||
res.data = res.body;
|
if (wrapperFn) {
|
||||||
return wrapperFn(res, context);
|
res.data = res.body;
|
||||||
|
return wrapperFn(res, context);
|
||||||
|
}
|
||||||
|
const { code, body, message } = res;
|
||||||
|
return { code, data: body, message };
|
||||||
|
} catch (e) {
|
||||||
|
return { code: 500, message: e.message };
|
||||||
}
|
}
|
||||||
const { code, body, message } = res;
|
|
||||||
return { code, data: body, message };
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
exportRoutes() {
|
exportRoutes() {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import http, { IncomingMessage, Server, ServerResponse } from 'http';
|
import http, { IncomingMessage, Server, ServerResponse } from 'http';
|
||||||
import { parseBody } from './parse-body.ts';
|
import { parseBody } from './parse-body.ts';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
|
import { createHandleCtx } from './server.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get params and body
|
* get params and body
|
||||||
@ -20,9 +21,16 @@ export const handleServer = async (req: IncomingMessage, res: ServerResponse) =>
|
|||||||
const parsedUrl = url.parse(req.url, true);
|
const parsedUrl = url.parse(req.url, true);
|
||||||
// 获取token
|
// 获取token
|
||||||
let token = req.headers['authorization'] || '';
|
let token = req.headers['authorization'] || '';
|
||||||
|
const handle = createHandleCtx(req, res);
|
||||||
|
const cookies = handle.req.cookies;
|
||||||
|
if (!token) {
|
||||||
|
token = cookies.token;
|
||||||
|
}
|
||||||
if (token) {
|
if (token) {
|
||||||
token = token.replace('Bearer ', '');
|
token = token.replace('Bearer ', '');
|
||||||
}
|
}
|
||||||
|
//@ts-ignore
|
||||||
|
console.log('token', req.cookies, res.cookie);
|
||||||
// 获取查询参数
|
// 获取查询参数
|
||||||
const param = parsedUrl.query;
|
const param = parsedUrl.query;
|
||||||
let body: Record<any, any>;
|
let body: Record<any, any>;
|
||||||
@ -41,6 +49,7 @@ export const handleServer = async (req: IncomingMessage, res: ServerResponse) =>
|
|||||||
token,
|
token,
|
||||||
...param,
|
...param,
|
||||||
...body,
|
...body,
|
||||||
|
cookies,
|
||||||
};
|
};
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
@ -2,8 +2,46 @@ import http, { IncomingMessage, ServerResponse } from 'http';
|
|||||||
import https from 'https';
|
import https from 'https';
|
||||||
import http2 from 'http2';
|
import http2 from 'http2';
|
||||||
import { handleServer } from './handle-server.ts';
|
import { handleServer } from './handle-server.ts';
|
||||||
|
import * as cookie from 'cookie';
|
||||||
export type Listener = (...args: any[]) => void;
|
export type Listener = (...args: any[]) => void;
|
||||||
|
|
||||||
|
type CookieFn = (name: string, value: string, options?: cookie.SerializeOptions, end?: boolean) => void;
|
||||||
|
|
||||||
|
export type HandleCtx = {
|
||||||
|
req: IncomingMessage & { cookies: Record<string, string> };
|
||||||
|
res: ServerResponse & {
|
||||||
|
/**
|
||||||
|
* cookie 函数, end 参数用于设置是否立即设置到响应头,设置了后面的cookie再设置会覆盖前面的
|
||||||
|
*/
|
||||||
|
cookie: CookieFn; //
|
||||||
|
};
|
||||||
|
};
|
||||||
|
// 实现函数
|
||||||
|
export function createHandleCtx(req: IncomingMessage, res: ServerResponse): HandleCtx {
|
||||||
|
// 用于存储所有的 Set-Cookie 字符串
|
||||||
|
const cookies: string[] = [];
|
||||||
|
let handReq = req as HandleCtx['req'];
|
||||||
|
let handRes = res as HandleCtx['res'];
|
||||||
|
// 扩展 res.cookie 方法
|
||||||
|
const cookieFn: CookieFn = (name, value, options = {}, end = true) => {
|
||||||
|
// 序列化新的 Cookie
|
||||||
|
const serializedCookie = cookie.serialize(name, value, options);
|
||||||
|
cookies.push(serializedCookie); // 将新的 Cookie 添加到数组
|
||||||
|
if (end) {
|
||||||
|
// 如果设置了 end 参数,则立即设置到响应头
|
||||||
|
res.setHeader('Set-Cookie', cookies);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 解析请求中的现有 Cookie
|
||||||
|
const parsedCookies = cookie.parse(req.headers.cookie || '');
|
||||||
|
handReq.cookies = parsedCookies;
|
||||||
|
handRes.cookie = cookieFn;
|
||||||
|
// 返回扩展的上下文
|
||||||
|
return {
|
||||||
|
req: handReq,
|
||||||
|
res: handRes,
|
||||||
|
};
|
||||||
|
}
|
||||||
export type Cors = {
|
export type Cors = {
|
||||||
/**
|
/**
|
||||||
* @default '*''
|
* @default '*''
|
||||||
@ -14,7 +52,7 @@ export type ServerOpts = {
|
|||||||
/**path default `/api/router` */
|
/**path default `/api/router` */
|
||||||
path?: string;
|
path?: string;
|
||||||
/**handle Fn */
|
/**handle Fn */
|
||||||
handle?: (msg?: { path: string; key?: string; [key: string]: any }) => any;
|
handle?: (msg?: { path: string; key?: string; [key: string]: any }, ctx?: { req: http.IncomingMessage; res: http.ServerResponse }) => any;
|
||||||
cors?: Cors;
|
cors?: Cors;
|
||||||
httpType?: 'http' | 'https' | 'http2';
|
httpType?: 'http' | 'https' | 'http2';
|
||||||
httpsKey?: string;
|
httpsKey?: string;
|
||||||
@ -131,7 +169,7 @@ export class Server {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.writeHead(200); // 设置响应头,给予其他任何listen 知道headersSent,它已经被响应了
|
// res.writeHead(200); // 设置响应头,给予其他任何listen 知道headersSent,它已经被响应了
|
||||||
|
|
||||||
const url = req.url;
|
const url = req.url;
|
||||||
if (!url.startsWith(path)) {
|
if (!url.startsWith(path)) {
|
||||||
@ -144,7 +182,10 @@ export class Server {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const end = await handle(messages as any);
|
const end = await handle(messages as any, { req, res });
|
||||||
|
if (res.writableEnded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (typeof end === 'string') {
|
if (typeof end === 'string') {
|
||||||
res.end(end);
|
res.end(end);
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user