# webframe | # webframe | ||||
Web server. | |||||
Webframe is a small and dependency free web application framework. | |||||
## Usage | |||||
Install: | |||||
npm install --save webframe | |||||
Use: | |||||
var webframe = require("webframe"); | |||||
var app = new webframe.App(); | |||||
The file example.js contains an example of a small web application. | |||||
## Why a new web framework? | |||||
A new installation of express is 2.3 megabytes. You end up with 42 dependencies | |||||
in your node\_modules directory. The Koa framework is 2.5 megabytes, with 36 | |||||
dependencies. This isn't bad for a big web application, but I write a lot of | |||||
tiny web applications where megabytes of dependencies just feels wrong. As a | |||||
result, I have in the past often just used the http module directly. This | |||||
framework is a nice middle ground; it provides some of the utility of big web | |||||
frameworks, but without adding a ton of extra dependencies to the application. | |||||
## Documentation | |||||
### webframe.App([options]) | |||||
Creates a new instance, with an optional options argument. | |||||
Options: | |||||
* `server`: Your own HTTP (like the one created by http.createHttpServer). | |||||
* `port`: The port to listen on. Defaults to the PORT environment variable, or | |||||
8080. | |||||
* `host`: The host to listen on. Defaults to "127.0.0.1". For the server to be | |||||
accessible to the outside world, listen on "0.0.0.0". | |||||
* `client_utils`: Whether to add some utility functions to /webframe.js. | |||||
Defaults to false. | |||||
* `res404`: The string to return for a 404 error. "{{pathname}}" will be | |||||
replaced with the pathname. | |||||
* `res403`: The string to return for a 403 error. "{{pathname}}" will be | |||||
replaced with the pathname. | |||||
var app = webframe.App({ client_utils: true }); | |||||
### app.route(method, path [, middleware], func) | |||||
Add a route. | |||||
* `method`: What HTTP method the route should be available at. | |||||
Valid options: "GET", "POST", "PUT", "DELETE", or "ALL". | |||||
* `path`: The path. If it starts with a "^", it will be interpreted as a | |||||
regular expression (with `new RegExp(path)`), and the route will be ran on | |||||
all matching paths. Routes without a pattern are prioritized over those with | |||||
a pattern. | |||||
* `middleware`: Optional. An array of middleware functions which will be ran | |||||
before the route's function. | |||||
* `func`: function(request, response). The function which handles the request. | |||||
### app.get, app.post, app.put, app.delete, app.all | |||||
Utility functions which just call app.route() with a predefined method value. | |||||
E.g `app.get("/foo", handler)` is the same as | |||||
`app.route("GET", "/foo", handler)`. | |||||
### app.info(str), app.notice(str), app.warning(str), app.panic(str) | |||||
Logging functions. They all print a message. `app.panic` also exits, and is | |||||
intended for errors so bad you have to crash. | |||||
In the future, it will be possible to control the logging level. | |||||
### Middleware | |||||
`webframe.middleware` contains middleware for the route functions. | |||||
* `webframe.middleware.cookies`: Adds `request.cookies`, parsed COOKIE header. | |||||
* `webframe.middleware.params`: Adds `request.params`, parsed URL parameters. | |||||
In the future, there will be middleware for POST request too. | |||||
### webframe.static(root[, before]) | |||||
Serve static files. | |||||
* `app.get("^.*", webframe.static("web"))` will serve all the files in the | |||||
`web` directory, so `/index.html` will serve `web/index.html`. | |||||
* `app.get("^/static/.*", webframe.static("web", "/static"))` will serve all | |||||
files in the `web` directory under `/static`, so `/static/script.js` will | |||||
serve `web/script.js`. |
// Replace with require("webframe") | |||||
var webframe = require("./index.js"); | |||||
var mw = webframe.middleware; | |||||
var app = new webframe.App(); | |||||
// Endpoint which uses the "params" middleware to parse URL parameters | |||||
app.get("/endpoint", [ mw.params ], (req, res) => { | |||||
res.end("Hello, "+req.params.name); | |||||
}); | |||||
// When a an endpoint path starts with a ^, it's interpreted as a | |||||
// regular expression pattern. "^.*" matches everything (though endpoints | |||||
// with explicit paths, like the "/endpoint" above, are prioritized). | |||||
// | |||||
// webframe.staic("web") returns a function which serves the file in "web". | |||||
app.get("^.*", webframe.static("web")); |
return; | return; | ||||
} | } | ||||
// Run all the before stuff if applicable | |||||
// Run all the middleware stuff if applicable | |||||
var self = this; | var self = this; | ||||
if (route.before) { | |||||
var cbs = route.before.length; | |||||
if (route.middleware) { | |||||
var cbs = route.middleware.length; | |||||
function cb() { | function cb() { | ||||
if (--cbs === 0) | if (--cbs === 0) | ||||
route.func(req, res, self); | route.func(req, res, self); | ||||
} | } | ||||
for (var i in route.before) { | |||||
route.before[i](req, res, cb); | |||||
for (var i in route.middleware) { | |||||
route.middleware[i](req, res, cb); | |||||
} | } | ||||
// Just run the function if there's no before | |||||
// Just run the function if there's no middleware | |||||
} else { | } else { | ||||
route.func(req, res, this); | route.func(req, res, this); | ||||
} | } | ||||
* path: path, | * path: path, | ||||
* func: function | * func: function | ||||
*/ | */ | ||||
route(method, path, before, func) { | |||||
route(method, path, middleware, func) { | |||||
if (method !== "GET" && method !== "POST" && method !== "PUT" && | if (method !== "GET" && method !== "POST" && method !== "PUT" && | ||||
method !== "DELETE" && method !== "ALL") { | method !== "DELETE" && method !== "ALL") { | ||||
throw new Error("Invalid method."); | throw new Error("Invalid method."); | ||||
// Before is optional | // Before is optional | ||||
if (func === undefined) { | if (func === undefined) { | ||||
func = before; | |||||
before = undefined; | |||||
func = middleware; | |||||
middleware = undefined; | |||||
} | } | ||||
// All necessary arguments must exist | // All necessary arguments must exist | ||||
method: method, | method: method, | ||||
func: func, | func: func, | ||||
pattern: pat, | pattern: pat, | ||||
before: before | |||||
middleware: middleware | |||||
}); | }); | ||||
} else { | } else { | ||||
this._routeMap[path] = { | this._routeMap[path] = { | ||||
method: method, | method: method, | ||||
func: func, | func: func, | ||||
before: before | |||||
middleware: middleware | |||||
}; | }; | ||||
} | } | ||||
} | } | ||||
*/ | */ | ||||
template(tmpl, args) { return template(tmpl, args); } | template(tmpl, args) { return template(tmpl, args); } | ||||
get(path, before, func) { this.route("GET", path, before, func); } | |||||
post(path, before, func) { this.route("POST", path, before, func); } | |||||
put(path, before, func) { this.route("PUT", path, before, func); } | |||||
delete(path, before, func) { this.route("DELETE", path, before, func); } | |||||
all(path, before, func) { this.route("ALL", path, before, func); } | |||||
get(path, middleware, func) { this.route("GET", path, middleware, func); } | |||||
post(path, middleware, func) { this.route("POST", path, middleware, func); } | |||||
put(path, middleware, func) { this.route("PUT", path, middleware, func); } | |||||
delete(path, middleware, func) { this.route("DELETE", path, middleware, func); } | |||||
all(path, middleware, func) { this.route("ALL", path, middleware, func); } | |||||
info(str) { | info(str) { | ||||
console.log("Info: "+str); | console.log("Info: "+str); |
zip: "application/zip", | zip: "application/zip", | ||||
pdf: "application/pdf", | pdf: "application/pdf", | ||||
gif: "image/gif", | |||||
png: "image/png", | png: "image/png", | ||||
jpeg: "image/jpeg", | jpeg: "image/jpeg", | ||||
jpg: "image/jpeg", | jpg: "image/jpeg", | ||||
gif: "image/gif", | |||||
svg: "image/svg", | |||||
svg: "image/svg+xml", | |||||
bmp: "image/bmp", | |||||
webp: "image/webp", | |||||
midi: "audio/midi", | |||||
mp3: "audio/mpeg", | |||||
ogg: "audio/ogg", | |||||
webm: "video/webm", | |||||
mkv: "video/mkv", | |||||
ogv: "video/ogg", | |||||
} | } | ||||
function mimetype(path) { | function mimetype(path) { | ||||
} | } | ||||
module.exports = function(root, before) { | module.exports = function(root, before) { | ||||
return function(req, res, app) { | return function(req, res, app) { | ||||
var pn = req.urlobj.pathname; | var pn = req.urlobj.pathname; | ||||
{ | { | ||||
"name": "webframe", | "name": "webframe", | ||||
"version": "0.1.0", | |||||
"version": "0.1.1", | |||||
"description": "Web server.", | "description": "Web server.", | ||||
"main": "index.js", | "main": "index.js", | ||||
"scripts": { | "scripts": { |
<!DOCTYPE html> | |||||
<html> | |||||
<head> | |||||
<meta charset="utf-8"> | |||||
<title>Hello World</title> | |||||
</head> | |||||
<body> | |||||
<p> Hello World! </p> | |||||
<form action="/endpoint" method="get"> | |||||
<label>What's your name? <input type="text" name="name"></label> | |||||
</form> | |||||
</body> | |||||
</html> |