@@ -1,3 +1,95 @@ | |||
# 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`. |
@@ -0,0 +1,17 @@ | |||
// 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")); |
@@ -84,20 +84,20 @@ class App { | |||
return; | |||
} | |||
// Run all the before stuff if applicable | |||
// Run all the middleware stuff if applicable | |||
var self = this; | |||
if (route.before) { | |||
var cbs = route.before.length; | |||
if (route.middleware) { | |||
var cbs = route.middleware.length; | |||
function cb() { | |||
if (--cbs === 0) | |||
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 { | |||
route.func(req, res, this); | |||
} | |||
@@ -111,7 +111,7 @@ class App { | |||
* path: path, | |||
* func: function | |||
*/ | |||
route(method, path, before, func) { | |||
route(method, path, middleware, func) { | |||
if (method !== "GET" && method !== "POST" && method !== "PUT" && | |||
method !== "DELETE" && method !== "ALL") { | |||
throw new Error("Invalid method."); | |||
@@ -119,8 +119,8 @@ class App { | |||
// Before is optional | |||
if (func === undefined) { | |||
func = before; | |||
before = undefined; | |||
func = middleware; | |||
middleware = undefined; | |||
} | |||
// All necessary arguments must exist | |||
@@ -136,13 +136,13 @@ class App { | |||
method: method, | |||
func: func, | |||
pattern: pat, | |||
before: before | |||
middleware: middleware | |||
}); | |||
} else { | |||
this._routeMap[path] = { | |||
method: method, | |||
func: func, | |||
before: before | |||
middleware: middleware | |||
}; | |||
} | |||
} | |||
@@ -163,11 +163,11 @@ class App { | |||
*/ | |||
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) { | |||
console.log("Info: "+str); |
@@ -12,11 +12,21 @@ var mimes = { | |||
zip: "application/zip", | |||
pdf: "application/pdf", | |||
gif: "image/gif", | |||
png: "image/png", | |||
jpeg: "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) { | |||
@@ -52,7 +62,6 @@ function sendfile(path, app, pathname, res) { | |||
} | |||
module.exports = function(root, before) { | |||
return function(req, res, app) { | |||
var pn = req.urlobj.pathname; | |||
@@ -1,6 +1,6 @@ | |||
{ | |||
"name": "webframe", | |||
"version": "0.1.0", | |||
"version": "0.1.1", | |||
"description": "Web server.", | |||
"main": "index.js", | |||
"scripts": { |
@@ -0,0 +1,14 @@ | |||
<!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> |