21

Ok So I have a simple node.js / express.js / mongodb app set up here with my config as follows.

var express = require('express'),
    mongoose = require('mongoose');
    http = require('http');

var app = express();

    app.configure(function(){
    app.set('port', process.env.PORT || 3000);
    app.set('views', __dirname + '/views');
    app.set('view engine', 'jade');

    //middleware stack
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(app.router);
    app.use(express.static(__dirname + "/public"));
});

mongoose.connect("mongodb://localhost/hello");

The problem lies when I try to make PUT or DELETE requests. My form is this simple

<form method="POST" action="/users/#{user.name}">
    <input type="hidden" name="_method" value="PUT"/>
</form>

Now my router catches the route with the express .put() method

app.put('/users/:name', function(req, res) {

    var b = req.body;

    Users.update(
        { name: req.user.name },
        { name: b.name, age: b.age, email: b.email },
        function(err) {
            res.redirect('/users/'+b.name);
        });
})

When I make the request I simply get a "Cannot PUT" or "Cannot DELETE" error.

I have tried to make this same request via chomes RESTful client with the same result.

I have read a topic witch has the same problem as me although following the comments the answers did not solve my problem.

Questions I have looked into expressjs support for method delete and put without the methodoverride

Are the PUT, DELETE, HEAD, etc methods available in most web browsers?

Along with a few others. I have also referenced the express.js and mongo documentation several times. I just cant think what could be going wrong.

Any help is appreciated.

Daniel Tate
  • 2,075
  • 4
  • 24
  • 50
  • 2
    Did you ever find a solution to this? I'm working on the same thing, and can't seem to get the DELETE to go thru. – Mike May 31 '13 at 14:23
  • 1
    What did you end up doing about this? I'm having the same problem - http://stackoverflow.com/questions/17603372/express-rest-server-losing-payload-from-ember-data-put – ted.strauss Jul 15 '13 at 12:26
  • 1
    @Mike the solution I accepted is below. – Daniel Tate Jul 15 '13 at 22:31
  • @DanielTate Since you did the "Rollback to Revision 2 - Edit approval overridden by post owner": from the answers, it seems clear that you ask for the "Cannot PUT ..." or "Cannot DELETE ..." error which should be quoted as an error then, with the capital C in "Cannot" and not as if cannot could be put there just in "bad English". The header should be something like `Express "Cannot PUT ..." / "Cannot DELETE ..." error: How to get the PUT / DELETE method working in Express?` – questionto42 Sep 22 '21 at 06:36
  • @questionto42 it was a copy + paste. No need to update anything. – Daniel Tate Sep 23 '21 at 03:03
  • @DanielTate OK then the message popped up like this, I see. I could not expect this when in the question body, you mention "Cannot PUT" or "Cannot DELETE" and I only know the latter error message ("Cannot PUT /mytable"). You should put it in quotations then. – questionto42 Sep 23 '21 at 06:25

7 Answers7

14

Update

As Jonathan Lonowski pointed out PUT can also be used, so you can ignore my old answer. Getting Cannot PUT or Cannot POST errors, means your callback is not executing successfully. My guess is that Users.update is failing, which is why it cannot POST or PUT. Can you check it.

Old answer

Try changing this line

app.put('/users/:name', function(req, res) {

to

app.post('/users/:name', function(req, res) {

since you are trying to submit the form

user568109
  • 47,225
  • 17
  • 99
  • 123
  • 1
    HTML form has only two valid methods GET and POST. – user568109 Mar 04 '13 at 04:44
  • @user568109 hi, can you please update your answer? it is google #1 for "express put not working", but it doesn't answer the question. i faced the same problem and it took 1 hour to find the answer : when we redirect in `put` method, it will redirect to another put. so we get `Cannot PUT` error. see my answer in: https://stackoverflow.com/a/57577635/4718434 – yaya Aug 20 '19 at 16:23
  • This answer saved relieved me after 3 days of deep searching about the DELETE request and over-ride package. Thanks @user568109. – Mo1 Aug 25 '20 at 11:50
  • This answer saved relieved me after 3 days of deep searching about the DELETE request and over-ride package. Thanks @user568109. – Mo1 Aug 25 '20 at 11:51
3

Is the <form> you listed in a view or a static file under __dirname + "/public"?

Within a static file, the #{user.name} probably isn't being replaced with the user's name and will be treated as a URL Fragment.

The <form> will actually submit to /users/ rather than /users/:name since that's the path:

console.log(url.parse('/users/#{user.name}'));

{ hash: '#{user.name}',
  pathname: '/users/',
  path: '/users/',
  href: '/users/#{user.name}' }

The <form> should be generated from a view if it isn't since the action needs to be dynamic and data-driven. With Jade and assuming user is a member of locals, that would be:

form(method='POST', action='/users/' + user.name)
  input(type='hidden', name='_method', value='PUT')
Jonathan Lonowski
  • 121,453
  • 34
  • 200
  • 199
  • I am using a jade template, I apologize but this is psudo code the name is being replaced as I see it in the response but it would be for example "Cannot PUT USERNAME" – Daniel Tate Mar 04 '13 at 04:52
1

Unless there is strange magic at work, your form makes a POST request, not a PUT. If you want to PUT, I would suggest using the jQuery.ajax function with a type: 'PUT' parameter, like this answer, from a form handler, see jQuery.submit. Don't forget to return false so that the form doesn't submit twice.

Community
  • 1
  • 1
Dan Ross
  • 3,596
  • 4
  • 31
  • 60
  • In all of the express documentation I have read they imply that you can achieve a PUT request with the hidden field in my form. – Daniel Tate Mar 04 '13 at 04:53
  • Yep, that sounds like magic :) Could you provide a reference? – Dan Ross Mar 04 '13 at 05:00
  • [this](http://clientexpressjs.com/guide) is a copy from the documentation. I am wondering if the correct method to PUT has changed since this version. Unfortunatly there is no version listed on that tutorial. I have been watching some tutorials on nettutsplus and they use this same method although in their documentation they state they are using version 3.0 of express where I am using 3.1. – Daniel Tate Mar 04 '13 at 06:00
  • That is a neat trick, sometimes the only reason I use jQuery is to get access to the whole HTTP vocabulary. I would certainly not expect a site-breaking change like this in a minor version number update. The only thing I can think of is to try another method on the client, to eliminate the possibility that there is a server side problem. I agree that your method is much more elegant however, and it might be worth it to keep using Express 3.0 for that feature alone (if it actually was removed). – Dan Ross Mar 04 '13 at 18:07
  • one remark: Do not return false. use event.preventDefault at the beginning of the submithandler. – Frederik Prijck Dec 29 '14 at 11:54
1

If your using method override, make sure you have declared it before you use your routes. That was the problem I was having.

0

app.post("/movies/:id") is one solution. If you still want to use app.put("/movies/:id") then try this:

  1. Install method-ovveride from npm.
  2. Require it in your app.js file.
  3. Open the form from where you wanna invoke PUT request
  4. make sure your form has the following attributes:

    action="/movies/<%= movies._id %>?_method=PUT " method="POST" >

These two solutions worked for me. If you are following REST, then use the method-ovveride else app.post() will also do the trick

Encrypted Drink
  • 91
  • 1
  • 11
0

Change res.redirect('path') to res.redirect(303, 'path')

In Put and Delete, if you want to redirect to get address, you should pass 303 as first parameter. (source)

yaya
  • 7,675
  • 1
  • 39
  • 38
0

one solution is to use cors middleware for you PUT,PATCH and DELETE requests like this in your app.js file like this:

first install the cors package via npm :

npm i cors

then add the following code to your app.js:

const cors = require('cors')

app.use(cors())