重新删除方法

CORS is starting to fry my brain a bit. Everything is good now, apart from one method. I'm building an app with backbone on the frontend and node.js/restify on the backend. The server.coffee looks like this:

server.get '/todos', todos.find_all
server.get '/todos/:id', todos.find_by_id
server.del '/todos/:id', todos.delete

Whenever a model in backbone calls destroy however I get this rather annoying error:

MLHttpRequest cannot load http://localhost:8080/todos/. Method DELETE is not allowed by Access-Control-Allow-Methods.

I read about this a bit and using restify done the following:

unknownMethodHandler = (request, response) ->
    if(request.method.toLowerCase() == 'options')
        allowHeaders = ['Accept', 'Accept-Version', 'Content-Type', 'Api-Version']

        if(response.methods.indexOf('OPTIONS') == -1) then response.methods.push('OPTIONS')

        response.header 'Access-Control-Allow-Credentials', true
        response.header 'Access-Control-Allow-Headers', allowHeaders.join(', ')
        response.header 'Access-Control-Allow-Methods', ['GET', 'DELETE', 'TEST!']
        response.header 'Access-Control-Allow-Origin', request.headers.origin

        response.send 204
    else
        response.send new restify.MethodNotAllowedError()

server.on 'MethodNotAllowed', unknownMethodHandler

But even still, I get this as the response header:

HTTP/1.1 204 No Content
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Api-Version, X-Request-Id, X-Response-Time
Connection: Keep-Alive
Date: Mon, 04 Feb 2013 12:24:25 GMT
Server: restify
X-Request-Id: fbd4e15a-a22e-48b6-bf5c-a46b94926748
X-Response-Time: 0

I just don't get what I'm doing wrong!

If you're expecting a response, you should use a '200' response code, not a 204 as that's a No Content response. See the W3C Spec for the details

9.7 DELETE

The DELETE method requests that the origin server delete the resource identified by the Request-URI. This method MAY be overridden by human intervention (or other means) on the origin server. The client cannot be guaranteed that the operation has been carried out, even if the status code returned from the origin server indicates that the action has been completed successfully. However, the server SHOULD NOT indicate success unless, at the time the response is given, it intends to delete the resource or move it to an inaccessible location.

A successful response SHOULD be 200 (OK) if the response includes an entity describing the status, 202 (Accepted) if the action has not yet been enacted, or 204 (No Content) if the action has been enacted but the response does not include an entity.

If the request passes through a cache and the Request-URI identifies one or more currently cached entities, those entries SHOULD be treated as stale. Responses to this method are not cacheable.

You're seeing the Access-Control-Allow-Origin: * in the response header. This is coming from the .../restify/lib/router.js preflight() method. The comment states "user will need to defined their own .opts handler".

Just set header res.setHeader('Access-Control-Allow-Methods', '*');

Here is the answer: https://github.com/mcavage/node-restify/issues/296#issuecomment-12333568

Use server.opts method to wirte your own handler for OPTIONS request. Below is the example you can use.

Also tell me if you are using set-credentials flag to true while making request from the browser. This handle in that case would have to respond with access cookies.

In the example below, I am returning the allowed origin for exact match. You can tweak it to be substring match also. But always return the exact value as found in request header origin in the response header 'Access-Control-Allow-Origin'. Its a good practice.

server.opts('/api/(.)*', (req, res) => {
const origin = req.header('origin');
const allowedOrigins = ['example.com', 'example.org'];
if (allowedOrigins.indexOf(origin) === -1) {
    //origin is not allowed
    return res.send(405);
}
//set access control headers to allow the preflight/options request
res.setHeader('Access-Control-Allow-Origin', header);
res.setHeader('Access-Control-Allow-Headers', 'Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE,OPTIONS');

// Access-Control-Max-Age header catches the preflight request in the browser for the desired
// time. 864000 is ten days in number of seconds. Also during development you may want to keep


   // this number too low e.g. 1.
    res.setHeader('Access-Control-Max-Age', 864000);
    return res.send(200);
  });