webium v0.6.6
Webium
A minimal web framework for microservice with middleware and routes. 中文
This module adds additional properties and methods to the corresponding req
and res objects in a http server, and enhance abilities of the program.
The program for enhancement has been separated as an individual module
enhance-req-res, which will work
with other frameworks or the internal Node.js http/https/http2 server, you can
check it out if you want.
This module has both express style and koa style, but only keeps very few and useful methods. It is compatible to most well-known connect and express middleware, so you can use them instead.
Since version 0.3.5, Webium is compatible to Node.js internal HTTP2 server.
Since version 0.4.2, Webium supports implementing hot-reloading.
Since version 0.5.0, Webium supports Flask (a Python framework) style of
binding routes, that means no next() needed, and directly returning values
from the handler function to the client is allowed.
Install
npm install webiumExample
const { App, Router } = require("./");
var app = new App();
// Webium supports dynamic routing, you can start listening before binding
// routes.
app.listen(80);
// Typical Express style
app.get("/", (req, res) => {
res.send("<h1>Welcome to your first webium app!</h1>");
}).get("/user/:id", (req, res) => {
console.log("UID:", req.params.id);
res.send({
id: req.params.id,
name: "Luna",
origin: "Webium"
});
}).post("/user", (req, res) => {
console.log("Body:", req.body);
res.send(req.body);
});
// Koa style: The route handler contains many features: using async/await,
// calling next() before doing stuffs, returning value from a handler function
// and catching errors across the request life cycle.
app.get("/async", async (req, res, next) => {
try {
var result = await next();
res.send(result); // Hello, Webium!
} catch (e) {
res.send(e instanceof Error ? e.message : e);
}
}).get("/async", async (req, res) => {
return "Hello, Webium!";
});
// Flask (Python) style: without next(). If the function returns something, it
// will be sent automatically. If nothing returned, the next function will be
// automatically invoked until the last one is called. This rule applys to both
// ordinary function and async functions (or any function returns promise).
app.get("/send-returning", async () => {
return "Hello, Webium!"; // the returning value will be sent automatically
});
app.get("/no-next", async (req) => {
req.myVar = "Hello, Webium";
}).get("/no-next", (req) => {
return req.myVar;
});
// Combine rules with an individual router.
var router = new Router();
router.get("/another-router", (req, res)=>{
res.send("This is another router.");
});
app.use(router);
// Bind regular expression directly
app.get(/\S+\.html$/, () => {
return "request an HTML file.";
});
// This route will match any URL
app.get("*", () => {
return "Unknown route.";
});API
webium
A namespace that contains classes App, Router and Cookie, while the
Cookie comes from sfn-cookie,
and the App inherited Router.
const webium = require("webium");
// recommended:
const { App, Router, Cookie } = require("webium");Cookie
new Cookie(name: string, value: string, options?: object)Alloptionsinclude:maxAge: numberHow many seconds that this cookie should last.expires: number|string|Date: Keep alive to a specified date or time.sameSite: Honor same-site principle, could be eitherStrictorLax.domain: Set cookie for a specified domain name.path: Set cookie for a specified pathname.httpOnly: Only HTTP, not JavaScript, can access this cookie.secure: This cookie won't be sent if not using HTTPS protocol.
new Cookie(cookieStr: string)new Cookie(options: object)cookie.toString()Gets the serialized cookie string of the current instance.
var cookie1 = new Cookie("username=Luna"),
cookie2 = new Cookie("username=Luna; Max-Age=120; HttpOnly"),
cookie3 = new Cookie("username", "Luna"),
cookie4 = new Cookie("username", "Luna", { maxAge: 120, httpOnly: true }),
cookie4 = new Cookie({ name: "username", value: "Luna", maxAge: 120, httpOnly: true });Router
new Router(caseSensitive?: boolean)
Creates a new router that can be used by the App. If caseSensitive is
true, when analyzing URL, the program will check it case-sensitively.
const { App, Router } = require("webium");
var app = new App,
router = new Router;
// ...
app.use(router);router.use()
Adds a handler function as a middleware, or concatenate another router.
signatures:
use(handler: RouteHandler): thisuse(router: Router): this
The type RouteHandler is a function with this signature:
(req: Request, res: Response: next(thisObj: any) => Promise<any>) => any
router.use((req, res, next) => {
// ...
next();
});
var router2 = new Router;
router2.use(router);Be aware, if you use another router when the current one has same routes,
only their handlers will be merged. If the routes in that router don't exist
in the current one, then references will be created, that means if you
modified the routes of that router, the current one will also be affected.
Middleware are called before all routes, and when concatenating two routes, middleware are always merged.
method(name: string, path: string, handler: RouteHandler, unique?: boolean): this
Adds a handler function to a specified method and path.
router.method("GET", "/", (req, res, next) => {
// ...
next();
}).method("GET", "/user/:name", (req, res, next) => {
// GET /user/luna
console.log(req.params); // { name: 'luna' }
// ...
});The path in express style, will be parsed by
path-to-regexp module, you can
learn more details in its documentation.
If the argument unique is provided, that means the route should contain only
one handler, and the new one will replace the old one.
delete(path: string, handler: RouteHandler, unique?: boolean): this
Short-hand for router.method("DELETE", path, handler, unique).
get(path: string, handler: RouteHandler, unique?: boolean): this
Short-hand for router.method("GET", path, handler, unique).
head(path: string, handler: RouteHandler, unique?: boolean): this
Short-hand for router.method("HEAD", path, handler, unique).
patch(path: string, handler: RouteHandler, unique?: boolean): this
Short-hand for router.method("PATCH", path, handler, unique).
post(path: string, handler: RouteHandler, unique?: boolean): this
Short-hand for router.method("POST", path, handler, unique).
put(path: string, handler: RouteHandler, unique?: boolean): this
Short-hand for router.method("PUT", path, handler, unique).
all(path: string, handler: RouteHandler, unique?: boolean): this
Adds a handler function to the all methods.
alias:
any()
contains(method: string, path: string, handler?: RouteHandler): boolean
Checks if the router contains corresponding route and handler.
methods(path: string): string[]
Returns all methods bound to the path.
App
The App class inherited from Router.
new App(options?: AppOptions)
Interface AppOptions includes:
domainSet a domain name (or multiple ones in an array) for the program to find out the subdomain.useProxyIftrue, when access properties likereq.ipandreq.host, will firstly try to get info from proxy, default:false.capitalizeAuto-capitalize response headers when setting, default:true.cookieSecretA secret key to sign/unsign cookie values.jsonpSet a query name for jsonp callback if needed. Iftrueis set, then the query name will bejsonp. In the query string, using the stylejsonp=callbackto request jsonp response.caseSensitiveSet the routes to be case-sensitive.
This module also automatically parses request body if the Content-Type is
application/x-www-form-urlencoded or application/json by using
body-parser module, so other
options worked with body-parser can also be set to options.
var app = new App();
// Or:
var app = new App({
domain: "example.com",
useProxy: true
});
app.get("/", (req, res, next) => {
// ...
next();
});listen()
Please check http.listen().
close()
Closes the server started by app.listen().
handler or listener
Returns the handler function for http/https/http2 server, you can use it with
https and/or http2.
HTTPS
const { createServer } = require("https");
createServer(app.listener).listen(443);HTTP/2
const { createSecureServer } = require("http2");
createSecureServer(tlsOptions, app.listener).listen(443);onerror(err: any, req: Request, res: Response)
If any error occurred, this method will be called, you can override this method to make it satisfied to your needs.
var app = new App();
// If the default function doesn't fulfill your needs, you can simply just
// overwrite it.
app.onerror = function (err, req, res) {
// ...
};Request
The Request interface extends IncomingMessage/Http2ServerRequest with
more properties and methods.
Some of these properties are read-only for security reasons, that means you won't be able to modified them.
streamTheHttp2Streamobject backing the request (only for http2).urlObjAn object parsed by url6 module that contains URL information. Be aware ofurlObj.auth, which is actually sent by httpBasic Authendication.timeRequest time, not really connection time, but the moment this module performs actions.proxyIf the client requested via a proxy server, this property will be set, otherwise it'snull. If available, it may contain these properties:protocolThe client's real request protocol (x-forwarded-proto).hostThe real host that client trying to request (x-forwarded-host).ipThe real IP of client (ips[0]).ipsAn array carries all IP addresses, includes client IP and proxy server IPs (x-forwarded-for).
authAuthentication of the client, it could benull, or an object carries{ username, password }.protocolEitherhttporhttps, ifuseProxyis true, then trying to useproxy'sprotocolfirst.secureIfprotocolishttps, thentrue, otherwisefalse.hostThe requested host address (includinghostnameandport), ifuseProxyis true, then try to useproxy'shostfirst.hostnameThe requested host name (withoutport).portThe requested port.domainNameThe request domain name.subdomainUnlike express or koa'ssubdomains, this property is calculated by setting thedomainoption.pathFull requested path (withsearch).pathnameDirectory part of requested path (withoutsearch).searchThe requested URLsearchstring, with a leading?.queryParsed URL query object.hrefFull requested URL string (withouthash, which is not sent by the client).refererEquivalent toheaders.referer.originReference toheaders.originorurlObj.origin.typeTheContent-Typeof requested body (withoutcharset).charsetThe requested body'scharset, or the first accepted charset (charsets[0]), assume they both use a same charset. Unlike other properties, If you set this one to a valid charset, it will be used to decode request body.charsetsAn array carries allAccept-Charsets, ordered byqualities.lengthTheContent-Lengthof requested body.xhrWhether the request fires withX-Requested-With: XMLHttpRequest.cookiesAn object carries all parsed cookies sent by the client.paramsThe URL parameters.bodyAn object carries requested body parsed by body-parser. Remember, onlyjsonandx-www-form-urlencodedare parsed by default.ipThe real client IP, ifuseProxyistrue, then trying to useproxy'sipfirst.ipsAn array carries all IP addresses, includes client IP and proxy server IPs. Unlikeproxy.ips, which may beundefined, while this will always be available.acceptThe first accepted response content type (accepts[0]).acceptsAn array carries allAccepts types, ordered byqualities.langThe first accepted response language (accepts[0]).langsAn array carries allAccept-Languages, ordered byqualities.encodingThe first accepted response encoding (encodings[0]).encodingsAn array carries allAccept-Encodings, ordered by sequence.cacheCache-Controlsent by the client, it could benull(no-cache), anumberof seconds (max-age), or a string likeprivate,public, etc.keepAliveWhether the request fires withConnection: keep-alive.get(field)Gets a request header field's (case insensitive) value.is(...types)Checks if the requestContent-Typematches the given types, available of using short-hand words, likehtmlindicatestext/html. If pass, returns the first matched type.
console.log(req.urlObj);
console.log(req.ip);
console.log(req.host);
console.log(req.subdomain);
console.log(req.query);
console.log(req.lang);
// ...Response
The Response interface extends ServerResponse/Http2ServerResponse with
more properties and methods.
Most of its properties are setters/getters, if you assign a new value to them, that will actually mean something.
stream - The Http2Stream object backing the response (only for http2)
This property is read-only.
res.stream.push("some thing");code - Sets/Gets status code.
res.code = 200;
console.log(res.code); // => 200message - Sets/Gets status message.
res.message = "OK";
console.log(res.message); // => OKstatus - Sets/Gets both status code and message.
res.status = 200;
console.log(res.status); // => 200 OK
res.status = "200 Everything works fine.";
console.log(res.status); // => 200 Everything works fine.
console.log(res.code); // => 200
console.log(res.message); // => Everything works fine.type - Sets/Gets Content-Type without charset part.
res.type = "text/html";
res.type = "html"; // Will auto lookup to text/html.
console.log(res.type); // => text/htmlcharset - Sets/Gets Content-Type only with charset part.
res.charset = "UTF-8";
console.log(res.charset); // => UTF-8length Sets/Gets Content-Length.
res.length = 12;
console.log(res.length); // => 12encoding Sets/Gets Content-Encoding.
res.encoding = "gzip";
console.log(res.encoding); // => gzipdate - Sets/Gets Date.
res.date = new Date(); // You can set a date string or Date instance.
console.log(res.date); // => Fri, 15 Dec 2017 04:13:17 GMTetag Sets/Gets - Etag.
This properties is internally used when calling res.send(), if you don't use
res.send(), you can call it manually.
const etag = require("etag");
var body = "Hello, World!";
res.etag = etag(body);
console.log(res.etag); // => d-CgqfKmdylCVXq1NV12r0Qvj2XgElastModified - Sets/Gets Last-Modified.
res.lastModified = new Date(2017); // You can set a date string or Date instance.
console.log(res.lastModified); // => Thu, 01 Jan 1970 00:00:02 GMTlocation - Sets/Gets Location.
res.location = "/login";
console.log(res.location); // => /loginrefresh - Sets/Gets Refresh in a number of seconds.
res.refresh = 3; // The page will auto-refresh in 3 seconds.
res.refresh = "3; URL=/logout"; // Auto-redirect to /logout in 3 seconds.
console.log(res.refresh); // => 3; URL=/logoutattachment - Sets/Gets Content-Disposition with a filename.
res.attachment = "example.txt";
console.log(res.attchment); // => attachment; filename="example.txt"cahce - Sets/Gets Cache-Control.
res.cache = null; // no-cache
res.cache = 0; // max-age=0
res.cache = 3600; // max-age=3600
res.cache = "private";
console.log(res.cache); // privatevary - Sets/Gets Vary.
res.vary = "Content-Type";
res.vary = ["Content-Type", "Content-Length"]; // Set multiple fields.
console.log(res.vary); // => [Content-Type, Content-Length]keepAlive - Sets/Gets Connection.
res.keepAlive = true; // Connection: keep-alive
console.log(res.keepAlive); // => truemodified - Whether the response has been modified.
This property is read-only, and only works after res.atag and
res.lastModified are set (whether explicitly or implicitly).
res.send("Hello, World!");
if (res.modified) {
console.log("A new response has been sent to the client.");
} else {
console.log("A '304 Not Modified' response has been sent to the client");
}headers - Sets/Gets response headers.
This property is a Proxy instance, you can only manipulate its properties to
set headers.
res.headers["x-powered-by"] = "Node.js/8.9.3";
console.log(res.headers); // => { "x-powered-by": "Node.js/8.9.3" }
// If you want to delete a heder, just call:
delete res.headers["x-powered-by"];cookies - Sets/Gets response cookies.
This property is a Proxy instance, you can only manipulate its properties to set cookies.
res.cookies.username = "Luna";
res.cookies.username = "Luna; Max-Age=3600"; // Set both value and max-age
// Another way to set a cookie is using the Cookie class:
const { Cookie } = require("webium");
res.cookies.username = new Cookie({ value: "Luna", maxAge: 3600 });
console.log(res.cookies); // => { username: "Luna" }
// If you want to delete a cookie, just call:
delete res.cookies.username;
// Or this may be more convinient if you just wnat it to expire:
res.cookies.username = null;get(field) - Gets a response header field's value.
var type = res.get("Content-Type");
// equivalent to
var type = req.headers["content-type"];set(field, value) - Sets a response header field's value.
res.set("Content-Type", "text/html");
// equivalent to:
res.headers["content-type"] = "text/html";append(field, value) - Appends a value to a response header field.
res.append("Set-Cookie", "username=Luna");
res.append("Set-Cookie", "email=luna@example.com");
// equivalent to:
res.set("Set-Cookie", ["username=Luna", "email=luna@example.com"]);remove(field) - Removes a response header field.
res.remove("Set-Cookie");
// equivalent to:
delete res.headers["set-cookie"];cookie(name) - Gets a response cookie.
var name = res.cookie("username");
// equivalent to:
var name = res.cookies.username;cookie(name, value, options?: object) - Sets a response cookie.
res.cookie("username", "Luna");
// equivalent to:
res.cookies.username = "Luna";
// you can set additinal options:
res.cookie("username", "Luna", { maxAge: 3600 });
// equivalent to:
res.cookies.username = new Cookie({ value: "Luna" , maxAge: 3600 });Be aware, you cannot set value as Luna; Max-Age=3600 with res.cookie(), it
will always be treated as cookie value.
auth() - Makes an HTTP basic authentication.
if(!req.auth){ // Require authendication if haven't.
res.auth();
}else{
// ...
}unauth() - Clears authentication.
Since browsers clear authentication while respond 401 Unauthorized, so
this method is exactly the same as res.auth(), only more readable.
redirect(url, code?: 301 | 302) - Redirects the request to a specified URL.
res.redirect("/login"); // code is 302 by default.
// If you want to go back to the previous page, just pass url -1.
res.redirect(-1);send(data) - Sends contents to the client.
This method will automatically perform type checking, If data is a buffer,
the res.type will be set to application/octet-stream; if data is an
object (or array), res.type will be set to application/json; if data is
a string, the program will detect if it's text/plain, text/html,
application/xml, or application/json.
This method also check if a response body has been modified since the last
time, if res.modified is false, a 304 Not Modified with no body will be
sent.
res.send("Hello, World!"); // text/plain
res.send("<p>Hello, World!</p>"); // text/html
res.send("<Text>Hello, World!</Text>"); // application/xml
res.send(`["Hello", "World!"]`); // application/json
res.send(["Hello", "World!"]); // application/json
res.send(Buffer.from("Hello, World!")); // application/octet-streamThis method could send jsonp response as well, if res.jsonp is set, or
options.jsonp for the application is set and the query matches, a jsonp
response will be sent, and the res.type will be set to
application/javascript.
res.jsonp = "callback";
res.send(["Hello", "World!"]); // will result as callback(["Hello", "World!"])sendFile(filename, cb?: (err)=>void) - Sends a file as response body.
This method also performs type checking.
res.sendFile("example.txt");
// if you provide a callback function, then it will be called after the
// response has been sent, or failed.
res.sendFile("example.txt", (err)=>{
console.log(err ? `Fail due to: ${err.message}`: "Success!");
});download(filename, newName?: string) Performs a file download function.
This method uses res.sendFile() to transfer the file, but instead of
displaying on the page, the browser will download it to disk.
res.download("example.txt");
// You can set a new name if the original one is inconvenient.
res.download("1a79a4d60de6718e8e5b326e338ae533.txt", "example.txt");Other forms:
download(filename, cb:? (err)=>void)download(filename, newName, cb:? (err)=>void)
The callback function, will be called after the response has been sent, or failed.
Other than downloading a real file, you can perform downloading a string as a
text file by using res.attachment and res.send().
// This content will be downloaded using the name 'example.html':
res.attachment = "example.html";
res.send("<p>Hello, World!</p>");Worth mentioned, if you use res.send() to send a buffer, most browsers will
download the buffer as a file, so it's always better to set res.attachment
when you are sending buffers.
About the next()
The function next returns a wrapper of the next handler, when it is called,
the returning value (if any) of the handler will be returned (in a resolved
promise since v0.6). This module will try to match as many routes as it can as
long as you continue calling the next() function, so you must not call it
unless you know what you're doing.
If you pass the thisObj to next(), then in the next handler function scope,
the pseudo-variable this will be pointed to thisObj, except in an arrow
function, which always uses the origin this at where the function is defined.
If no thisObj passed, then the default this is the App instance.
Allowing you pass thisObj is meant to allow you bind a class method to the
route, which always requires the this context available. Please see the
following example:
class Controller {
constructor() {
this.text = "Hello, World!";
}
index(req, res) {
res.send(this.text);
}
}
var app = new App;
app.get("/", (req, res, next) => {
var ctrl = new Controller;
ctrl.text = "<h1>Response comes from a controller.</h1>";
next(ctrl);
}).get("/", Controller.prototype.index);If you calling Controller.prototype.index(...args) directly, nothing will be
sent because there is no this.text at all. But using the mechanism of
Controller.prototype.index.call(new Controller, ...args), it will work fine.
The next(new Controller) is just doing the same thing for you internally.
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago