You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by Apache Wiki <wi...@apache.org> on 2010/09/11 07:11:45 UTC

[Couchdb Wiki] Update of "Throw a 404 or a redirect" by matiu

Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Couchdb Wiki" for change notification.

The "Throw a 404 or a redirect" page has been changed by matiu.
The comment on this change is: tips on redirecting, throwing exceptions, and returning 404 not_found's.
http://wiki.apache.org/couchdb/Throw%20a%20404%20or%20a%20redirect

--------------------------------------------------

New page:
= Simlest ways =

== Throw a 404 error ==

To throw a 404 from inside a _show or _list func .. the easiest way is:

    {{{
    throw (['error', 'not_found', 'Some message like Page not found'])
    }}}

That will be caught by the top level loop thing and turned into a nice response.

== Return a redirect ==

There's no top level catcher thing for redirects, so you can't *throw a redirect*, you have to 'return' it.

To do a redirect, there's a library function that will do it for you:

    {{{
    var redirect = require("vendor/couchapp/lib/redirect");
    return redirect.permanent('http://some.new/place');
    }}}

You can use the path lib to help get some neat urls, have a look at vendor/couchapp/lib/path.js source ..

What this actually does is the equivalent of:

    {{{
    return { code : 301, headers : { "Location" : 'http://some.new/place' } };
    }}}

The 'code' is the http response code, the http response would be something like:

    {{{
    HTTP/1.1 301 Moved Permanently
    Vary: Accept
    Server: CouchDB/1.0.1 (Erlang OTP/R13B)
    Location: http://foot:5984/products/dvrs/standalone-h.264-16-channel-dvr
    Etag: "CYFPH3WEUO9R5B8S26QWV3NK4"
    Date: Sat, 11 Sep 2010 04:51:37 GMT
    Content-Type: application/json
    Content-Length: 0
    }}}

= Case study, Making a Redirect Throwable =

If you're deep in a lib/mystuff.js func, I use this sort of style for a certain checker function.

In this example I have different product lines in my rewrites, of the form:

    {{{
    [
        {
            "from":   "/products/dvrs",
            "to":     "_list/dvrs",
            "method": "GET" 
        },
        {
            "from":   "/products/dvrs/new",
            "to":     "_show/dvr-new",
            "method": "GET" 
        },
        {
            "from":   "/products/dvrs/new",
            "to":     "_show/dvr-save",
            "method": "POST" 
        },
        {
            "from":   "/products/dvrs/*",
            "to":     "_show/dvr/*",
            "method": "GET" 
        },
        // Then a bunch of similar things for other product lines (like /products/cameras/ etc ..)
    ]
    }}}

The trouble is if someone uses the wrong url for the type of product, I want to put them right, this will help keep the
search engine optimization good too. So if someone goes: 

    {{{
    http://mysite.kom/products/cameras/id_of_a_dvr
    }}}

It will call the _show/camera method with the id of a document that has {type: 'dvr'}. Which is bad because I use a
different template but also bad because SEO will freak out if it ends up in google.

To fix it I did the following:

In my _show or _list func I put:

    {{{
    var rewrite = require("lib/rewritehelper").init(req);
    // If we're trying to use this view to see something other than a dvr, make sure we're looking at the right type
      try {rewrite.checkType(doc, 'camera');} catch (e) {return e;}
    }}}

I know people will yell at me for puting too much on one line. But I'm using this in all my _show and _list funcs, I
want it to be nice and short.

In my *lib/rewritehelper.js* file I check the type and all that and if it's wrong I throw an exception, which the _show
func catches and returns as is. Here's the full code for my lib function so far:

    {{{
    /* Helps you get the rewrites right.

    Complements the rewrites.json .. really both should be updated at the same time. Saves having to update every place you
    put a path to something.

    */

    exports.init = function(req) {

        rewrite = {};

        rewrite.url4doc = function (_id, prodType) {
            /* Returns a url to a product, takes doc._id and doc._type */
            // See if it's a product
            switch (prodType) {
                case "forum-topic":
                case "forum-post":
                    return "http://" + req.headers.Host + "/forums/" + prodType + "s/" + _id;
                case "support":
                    return "http://" + req.headers.Host + "/support/" + prodType + "s/" + _id;
                case "dvr": 
                case "camera": 
                    return "http://" + req.headers.Host + "/products/" + prodType + "s/" + _id;
                default:
                    return {code: 404, body: 'Page not found!'}
            };
        };
        rewrite.checkType = function (doc, desiredType) {
            /* Checks a document type and throws an exception that should be returned straight to the client if caught.
               If no exception is raised, the check has passed */
            if ((doc) && (doc.type != desiredType)) {
                var newURL = rewrite.url4doc(doc._id, doc.type);
                if (newURL.body == undefined) {
                    // If it's a url string, throw a redirect error
                    throw { code : 301, headers : { "Location" : newURL } };
                } else {
                    // If it's a {code: 404} block, throw it straight
                    throw newURL;
                }
            } else {
                return null;
            }
        }

        return rewrite;
    };
    }}}

So the cool thing here is the 'throw' statements, if we're totally in the wrong place, I want to get out ASAP.