mirror of
https://github.com/DIYgod/RSSHub.git
synced 2025-12-19 06:38:55 +08:00
Add middlewares for API and various improvements (#685)
This commit is contained in:
@@ -6,29 +6,23 @@ router.get('/routes/:name?', (ctx) => {
|
||||
const allRoutes = Array.from(routes.stack);
|
||||
allRoutes.shift();
|
||||
const result = {};
|
||||
let counter = 0;
|
||||
|
||||
allRoutes.forEach((i) => {
|
||||
const path = i.path;
|
||||
const top = path.split('/')[1];
|
||||
|
||||
if (ctx.params.name === undefined) {
|
||||
if (ctx.params.name === undefined || top === ctx.params.name) {
|
||||
if (result[top]) {
|
||||
result[top].routes.push(path);
|
||||
} else {
|
||||
result[top] = { routes: [path] };
|
||||
}
|
||||
} else {
|
||||
if (top === ctx.params.name) {
|
||||
if (result[top]) {
|
||||
result[top].routes.push(path);
|
||||
} else {
|
||||
result[top] = { routes: [path] };
|
||||
}
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
});
|
||||
|
||||
ctx.body = result;
|
||||
ctx.body = { counter, result };
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -93,6 +93,10 @@ RSSHub 同时支持 RSS 2.0、Atom 和 [JSON Feed](https://jsonfeed.org/) 输出
|
||||
|
||||
## API 接口
|
||||
|
||||
::: warning 注意
|
||||
API 仍处于开发状态中, 并可能会有改动. 欢迎提供建议!
|
||||
:::
|
||||
|
||||
RSSHub 提供下列 API 接口:
|
||||
|
||||
### 可用公共路由列表
|
||||
@@ -109,12 +113,14 @@ RSSHub 提供下列 API 接口:
|
||||
|
||||
- name, 路由一级名称, 对应 [https://github.com/DIYgod/RSSHub/tree/master/routes](https://github.com/DIYgod/RSSHub/tree/master/routes) 中的文件夹名称. 可选, **缺省则返回所有可用路由**.
|
||||
|
||||
返回的 JSON 结果格式如下:
|
||||
成功请求将会返回 HTTP 状态码 `200 OK` 与 JSON 结果, 格式如下:
|
||||
|
||||
```js
|
||||
{
|
||||
"bilibili":{
|
||||
"routes":[
|
||||
"status": "success",
|
||||
"data": {
|
||||
"bilibili": {
|
||||
"routes": [
|
||||
"/bilibili/user/video/:uid",
|
||||
"/bilibili/user/article/:uid",
|
||||
"/bilibili/user/fav/:uid",
|
||||
@@ -139,9 +145,13 @@ RSSHub 提供下列 API 接口:
|
||||
"/bilibili/topic/:topic"
|
||||
]
|
||||
}
|
||||
},
|
||||
"message": "request returned 22 routes"
|
||||
}
|
||||
```
|
||||
|
||||
若无符合请求路由, 请求将会返回 HTTP 状态码 `204 No Content`.
|
||||
|
||||
## 社交媒体
|
||||
|
||||
### bilibili
|
||||
|
||||
@@ -91,6 +91,10 @@ For exmaple:
|
||||
|
||||
## API
|
||||
|
||||
::: warning Warning
|
||||
The API is under active development and is subject to change. All suggestions are welcome!
|
||||
:::
|
||||
|
||||
RSSHub provides the following APIs:
|
||||
|
||||
### List of Public Routes
|
||||
@@ -107,21 +111,27 @@ Parameters:
|
||||
|
||||
- name, route's top level name as in [https://github.com/DIYgod/RSSHub/tree/master/routes](https://github.com/DIYgod/RSSHub/tree/master/routes). Optional, **returns all public routes if not specified**.
|
||||
|
||||
The above example returns the following result in JSON:
|
||||
A successful request returns a HTTP status code `200 OK` with the result in JSON:
|
||||
|
||||
```js
|
||||
{
|
||||
"github":{
|
||||
"routes":[
|
||||
"status": "success",
|
||||
"data": {
|
||||
"github": {
|
||||
"routes": [
|
||||
"/github/trending/:since/:language?",
|
||||
"/github/issue/:user/:repo",
|
||||
"/github/user/followers/:user",
|
||||
"/github/stars/:user/:repo"
|
||||
]
|
||||
}
|
||||
},
|
||||
"message": "request returned 4 routes"
|
||||
}
|
||||
```
|
||||
|
||||
If no matching results were found, the server returns only a HTTP status code `204 No Content`.
|
||||
|
||||
## Application Updates
|
||||
|
||||
### RSSHub
|
||||
|
||||
11
index.js
11
index.js
@@ -17,9 +17,14 @@ const auth = require('./middleware/auth');
|
||||
|
||||
const router = require('./router');
|
||||
const protected_router = require('./protected_router');
|
||||
const api_router = require('./api_router');
|
||||
const mount = require('koa-mount');
|
||||
|
||||
// API related
|
||||
|
||||
const apiTemplate = require('./middleware/api-template');
|
||||
const api_router = require('./api_router');
|
||||
const apiResponseHandler = require('./middleware/api-response-handler');
|
||||
|
||||
process.on('uncaughtException', (e) => {
|
||||
logger.error('uncaughtException: ' + e);
|
||||
});
|
||||
@@ -55,9 +60,11 @@ app.use(debug);
|
||||
// 5 fix incorrect `utf-8` characters
|
||||
app.use(utf8);
|
||||
|
||||
app.use(apiTemplate);
|
||||
app.use(apiResponseHandler());
|
||||
|
||||
// 4 generate body
|
||||
app.use(template);
|
||||
|
||||
// 3 filter content
|
||||
app.use(parameter);
|
||||
|
||||
|
||||
165
middleware/api-response-handler.js
Normal file
165
middleware/api-response-handler.js
Normal file
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* HTTP Status codes
|
||||
*/
|
||||
const statusCodes = {
|
||||
CONTINUE: 100,
|
||||
OK: 200,
|
||||
CREATED: 201,
|
||||
ACCEPTED: 202,
|
||||
NO_CONTENT: 204,
|
||||
BAD_REQUEST: 400,
|
||||
UNAUTHORIZED: 401,
|
||||
FORBIDDEN: 403,
|
||||
NOT_FOUND: 404,
|
||||
REQUEST_TIMEOUT: 408,
|
||||
UNPROCESSABLE_ENTITY: 422,
|
||||
INTERNAL_SERVER_ERROR: 500,
|
||||
NOT_IMPLEMENTED: 501,
|
||||
BAD_GATEWAY: 502,
|
||||
SERVICE_UNAVAILABLE: 503,
|
||||
GATEWAY_TIME_OUT: 504,
|
||||
};
|
||||
|
||||
function responseHandler() {
|
||||
return async (ctx, next) => {
|
||||
ctx.res.statusCodes = statusCodes;
|
||||
ctx.statusCodes = ctx.res.statusCodes;
|
||||
|
||||
ctx.res.success = ({ statusCode, data = null, message = null }) => {
|
||||
const status = 'success';
|
||||
|
||||
if (!!statusCode && statusCode < 400) {
|
||||
ctx.status = statusCode;
|
||||
} else if (!(ctx.status < 400)) {
|
||||
ctx.status = statusCodes.OK;
|
||||
}
|
||||
|
||||
ctx.body = { status, data, message };
|
||||
};
|
||||
|
||||
ctx.res.fail = ({ statusCode, code, data = null, message = null }) => {
|
||||
const status = 'fail';
|
||||
|
||||
if (!!statusCode && (statusCode >= 400 && statusCode < 500)) {
|
||||
ctx.status = statusCode;
|
||||
} else if (!(ctx.status >= 400 && ctx.status < 500)) {
|
||||
ctx.status = statusCodes.BAD_REQUEST;
|
||||
}
|
||||
|
||||
ctx.body = { status, code, data, message };
|
||||
};
|
||||
|
||||
ctx.res.error = ({ statusCode, code, data = null, message = null }) => {
|
||||
const status = 'error';
|
||||
|
||||
if (!!statusCode && (statusCode >= 500 && statusCode < 600)) {
|
||||
ctx.status = statusCode;
|
||||
} else if (!(ctx.status >= 500 && ctx.status < 600)) {
|
||||
ctx.status = statusCodes.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
ctx.body = { status, code, data, message };
|
||||
};
|
||||
|
||||
ctx.res.ok = (params = {}) => {
|
||||
ctx.res.success({
|
||||
...params,
|
||||
statusCode: statusCodes.OK,
|
||||
});
|
||||
};
|
||||
|
||||
ctx.res.created = (params = {}) => {
|
||||
ctx.res.success({
|
||||
...params,
|
||||
statusCode: statusCodes.CREATED,
|
||||
});
|
||||
};
|
||||
|
||||
ctx.res.accepted = (params = {}) => {
|
||||
ctx.res.success({
|
||||
...params,
|
||||
statusCode: statusCodes.ACCEPTED,
|
||||
});
|
||||
};
|
||||
|
||||
ctx.res.noContent = (params = {}) => {
|
||||
ctx.res.success({
|
||||
...params,
|
||||
statusCode: statusCodes.NO_CONTENT,
|
||||
});
|
||||
};
|
||||
|
||||
ctx.res.badRequest = (params = {}) => {
|
||||
ctx.res.fail({
|
||||
...params,
|
||||
statusCode: statusCodes.BAD_REQUEST,
|
||||
});
|
||||
};
|
||||
|
||||
ctx.res.forbidden = (params = {}) => {
|
||||
ctx.res.fail({
|
||||
...params,
|
||||
statusCode: statusCodes.FORBIDDEN,
|
||||
});
|
||||
};
|
||||
|
||||
ctx.res.notFound = (params = {}) => {
|
||||
ctx.res.fail({
|
||||
...params,
|
||||
statusCode: statusCodes.NOT_FOUND,
|
||||
});
|
||||
};
|
||||
|
||||
ctx.res.requestTimeout = (params = {}) => {
|
||||
ctx.res.fail({
|
||||
...params,
|
||||
statusCode: statusCodes.REQUEST_TIMEOUT,
|
||||
});
|
||||
};
|
||||
|
||||
ctx.res.unprocessableEntity = (params = {}) => {
|
||||
ctx.res.fail({
|
||||
...params,
|
||||
statusCode: statusCodes.UNPROCESSABLE_ENTITY,
|
||||
});
|
||||
};
|
||||
|
||||
ctx.res.internalServerError = (params = {}) => {
|
||||
ctx.res.error({
|
||||
...params,
|
||||
statusCode: statusCodes.INTERNAL_SERVER_ERROR,
|
||||
});
|
||||
};
|
||||
|
||||
ctx.res.notImplemented = (params = {}) => {
|
||||
ctx.res.error({
|
||||
...params,
|
||||
statusCode: statusCodes.NOT_IMPLEMENTED,
|
||||
});
|
||||
};
|
||||
|
||||
ctx.res.badGateway = (params = {}) => {
|
||||
ctx.res.error({
|
||||
...params,
|
||||
statusCode: statusCodes.BAD_GATEWAY,
|
||||
});
|
||||
};
|
||||
|
||||
ctx.res.serviceUnavailable = (params = {}) => {
|
||||
ctx.res.error({
|
||||
...params,
|
||||
statusCode: statusCodes.SERVICE_UNAVAILABLE,
|
||||
});
|
||||
};
|
||||
|
||||
ctx.res.gatewayTimeOut = (params = {}) => {
|
||||
ctx.res.error({
|
||||
...params,
|
||||
statusCode: statusCodes.GATEWAY_TIME_OUT,
|
||||
});
|
||||
};
|
||||
await next();
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = responseHandler;
|
||||
13
middleware/api-template.js
Normal file
13
middleware/api-template.js
Normal file
@@ -0,0 +1,13 @@
|
||||
module.exports = async (ctx, next) => {
|
||||
await next();
|
||||
if (ctx.request.path.startsWith('/api/')) {
|
||||
if (ctx.body.counter > 0) {
|
||||
return ctx.res.ok({
|
||||
message: `request returned ${ctx.body.counter} ${ctx.body.counter > 1 ? 'routes' : 'route'}`,
|
||||
data: ctx.body.result,
|
||||
});
|
||||
} else {
|
||||
return ctx.res.noContent();
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user