Skip to content

Commit

Permalink
added guide for 2x to 3x
Browse files Browse the repository at this point in the history
  • Loading branch information
IamLizu committed Aug 13, 2024
1 parent 0c923d2 commit 8179986
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 0 deletions.
5 changes: 5 additions & 0 deletions en/3x/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ redirect_from: "/3x/api.html"

<h1>3.x API</h1>

See also:

* [New features in 3.x.](../guide/new-features-in-3x.md)
* [Migrating from 2.x to 3.x.](../guide/migrating-from-2x-to-3x.md)

{% include api/{{ page.lang }}/3x/express.md %}
{% include api/{{ page.lang }}/3x/app.md %}
{% include api/{{ page.lang }}/3x/req.md %}
Expand Down
154 changes: 154 additions & 0 deletions en/guide/migrating-from-2x-to-3x.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
---
layout: page
title: Migrating From 2.x to 3.x
menu: guide
lang: en
redirect_from: "/guide/migrating-from-2x-to-3x.html"
---
# Migrating From 2.x to 3.x
## Removed

- `res.render()` "status" option (use node's `res.statusCode=` or `res.status(code).render(...)`)
- `res.render()` "charset" option (use `res.charset=`)
- `res.local(foo, bar)` (use `res.locals.foo = bar` or `res.locals({ foo: bar })` instead)
- `app.dynamicHelpers()` (use middleware + `res.locals`)
- `app.helpers()` (use `app.locals`)
- the concept of a "layout" (template engine specific now)
- `partial()` (template engine specific)
- `res.partial()`
- "view options" setting, use `app.locals`
- "hints" setting
- `req.isXMLHttpRequest` (use `req.xhr`)
- `app.error()` (use middleware with (err, req, res, next))
- `req.flash()` (just use sessions: `req.session.messages = ['foo']` or similar)
- [connect-flash](https://github.com/jaredhanson/connect-flash) can be used as middleware to provide req.flash()
- the `jsonp callback` setting was removed (use `res.jsonp()`)

## Changed

- `req.header(field[, defaultValue])` replaced by `req.get(field)` (remains for backwards compatibility)
- `res.header(field[, value])` replaced by `res.set(field, value)` / `res.get(field)` (remains for backwards compatibility)
- renamed `app.register()` to `app.engine()`
- template engine compliance from `engine.compile(str, options) => Function` to `engine.__express(filename, options, callback)`
- `express.createServer()` is now simply `express()` (but remains for BC).
- Keep in mind that the return value of `express()` is **no longer an `http.Server` instance**. (See the **Application function** section below for more details)

## View options

The "view options" setting is no longer necessary, `app.locals` are the local variables merged with `res.render()`'s, so `app.locals.pretty = true` is the same as passing `res.render(view, { pretty: true })`.

## Application function

The return value of `express()` is a JavaScript `Function`, encapsulating everything that makes an Express app tick. This means you can easily setup HTTP and HTTPS versions of your application by passing it to node's `http.createServer()` and `https.createServer()`:

```js
var app = express();
http.createServer(app).listen(80);
https.createServer(options, app).listen(443);
```

For convenience, and smaller applications the `app.listen()` method takes the same arguments, wrapping in an HTTP server. The following are equivalent:

```js
var app = express();
app.listen(3000);
```

and

```js
var app = express()
, http = require('http');

http.createServer(app).listen(3000);
```

This however means that methods that are on node's `http.Server.prototype` are no longer present on `app`, for example `app.address()` must now be called on the server returned by `app.listen()` or the one you have wrapped with `http.createServer(app)`.

## Socket.IO compatibility

Socket.IO's `.listen()` method takes an `http.Server` instance as an argument. As of 3.x, the return value of `express()` is not an `http.Server` instance. (See the **Application function** section above.) To get Socket.IO working with Express 3.x, make sure you manually create and pass your `http.Server` instance to Socket.IO's `.listen()` method.

```js
var app = express()
, http = require('http')
, server = http.createServer(app)
, io = require('socket.io').listen(server);

server.listen(3000);
```

## Template engine integration

Express 2x template engine compatibility required the following module export:

```js
exports.compile = function(templateString, options) {
return a Function;
};
```

Express 3x template engines should export the following:

```js
exports.\_\_express = function(filename, options, callback) {
callback(err, string);
};
```

If a template engine does not expose this method, you're not out of luck, the `app.engine()` method allows you to map any function to an extension. Suppose you had a markdown library and wanted to render `.md` files, but this library did not support Express, your `app.engine()` call may look something like this:

```js
var markdown = require('some-markdown-library');

app.engine('md', function(path, options, fn){
fs.readFile(path, 'utf8', function(err, str){
if (err) return fn(err);
str = markdown.parse(str).toString();
fn(null, str);
});
});
```

## View system changes

By removing the concept of a "layout" & partials in Express 3.x template engines will have greater control over file I/O. This means integration with template engines much easier, and greatly simplify the view system's internals.

This also enables template engines to supply their own means of inheritance, for example later releases of Jade provide Django-inspired template inheritance, where the view being rendered specifies the layout it wants to extend. For an example of this using the Jade engine visit [http://www.devthought.com/code/use-jade-blocks-not-layouts/](http://www.devthought.com/code/use-jade-blocks-not-layouts/)

Post-release we may end up building an Express extension to support the old `partial()` concept.

To get back layout functionality with EJS you can use [express-partials](https://github.com/publicclass/express-partials) or [ejs-locals](https://github.com/RandomEtc/ejs-locals).

## Error handling middleware

The `app.error(callback)` method in 2.x was effectively the same as the following:

```js
app.error = function(fn){
this.use(function(err, req, res, next){
fn.apply(this, arguments);
});
};
```

The reason for this is that Connect differentiates between "regular" middleware, and "error-handling" middleware via the `fn.length`. A regular middleware has a `fn.length` of `<= 3`, aka `(req, res, next)`, whereas error-handling middleware must have exactly `4` `(err, req, res, next)`. So the reason 2.x wrapped this functionality was to simply provide a bit of sugar on-top of this API making the parameters optional.

In short all you need to do to "catch" these errors that are passed along is to define another middleware, but with `4` arguments. Note that this middleware should be defined _below_ all the others, so that they may invoke `next(err)` in order to pass an error to it like so:

```js
app.use(express.bodyParser())
app.use(express.cookieParser())
app.use(express.session())
app.use(app.router) // the router itself (app.get(), app.put() etc)
app.use(function(err, req, res, next){
// if an error occurs Connect will pass it down
// through these "error-handling" middleware
// allowing you to respond however you like
res.send(500, { error: 'Sorry something bad happened!' });
})
```

## App- & Request-level local variables

Do not use `name` as a key in app.locals or res.locals because those objects are Function object instances. Any attempt to set them will be silently ignored. Other unstable or unusable top level keys are listed here: [Function Instance Properties](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function#Function_instance-Properties).
63 changes: 63 additions & 0 deletions en/guide/new-features-in-3x.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
layout: page
title: New Features in 3.x
menu: guide
lang: en
redirect_from: "/guide/new-features-in-3x.html"
---
# New Features in 3.x
## Express

- `express.application` prototype
- `express.request` prototype
- `express.response` prototype

## Application

- `app.head()` support
- `app.locals` object (`app.locals.foo = 'bar'`)
- `app.locals(obj)` (`app.locals({ foo: 'bar', bar: 'baz'})`)
- `app.render(name[, options], callback)` to render app-level views
- `app.engine(ext, callback)` to map template engines

## Settings

- `trust proxy` setting enables the use of "X-Forwarded-Proto" for `req.protocol` and `res.redirect` url construction
- `json spaces` setting enables the developer to decide if `res.json()` responds with nicely formatted JSON or compact json (this value is passed to `JSON.stringify()`)
- `json replacer` setting is a callback which is passed to `JSON.stringify()` in `res.json()` allowing you to manipulate and filter the JSON response

## Request

- `req.path` the request pathname
- `req.protocol` returns the protocol string "http" or "https" (supports X-Forwarded-Proto via `trust proxy` setting)
- `req.get(field)` gets a request header field value (`req.get('Host')`)
- `req.accepts(type)` improved
- `req.accepts(types)` added, returns the most viable type
- `req.accepted` array of parsed **Accept** values sorted by quality
- `req.acceptsCharset(charset)` added
- `req.acceptedCharsets` array of parsed **Accept-Charset** values sorted by quality
- `req.acceptsLanguage(lang)` added
- `req.acceptedLanguages` array of parsed **Accept-Language** values sorted by quality
- `req.signedCookies` object containing signed cookies
- `req.stale` to see if a request is stale (based on ETag/Last-Modified)
- `req.fresh` to complement `req.stale`
- `req.ips` to return an array of X-Forwarded-For values when "trust proxy" is enabled
- `req.ip` return the upstream addr in `req.ips` or `req.connection.remoteAddress`
- `req.range(size)` parses the Range header field

## Response

- `res.get(field)` gets a response header field value (`res.get('Content-Length')`)
- `res.set(field, value)` sets a response header field (`res.set('Content-Length', n)`)
- `res.set(obj)` set several field values
- `res.type(path)` alias of previous `res.contentType(path)`, allows `res.type('json')`, `res.type('application/x-whatever')`
- `res.format(obj)` to format responses based on qvalue-sorted Accept
- JSON cookie support (`res.cookie('cart', { ids: [1,2,3] })`, `req.cookies.cart.ids`)
- signed cookie support (`res.cookie(name, value, { signed: true })`)
- `res.format(obj)` for content-negotiation
- `res.locals` object (`res.locals.foo = 'bar'`)
- `res.locals(obj)` (`res.locals({ foo: 'bar', bar: 'baz')`)
- `res.redirect()` X-Forwarded-Proto support
- `res.redirect()` relative support (`res.redirect('../new')`, `res.redirect('./edit')`, etc)
- `res.links(obj)` set the "Link" header field with the given links
- `res.jsonp([status], obj)` was added for explicit JSONP support

0 comments on commit 8179986

Please sign in to comment.