ga-wdi-boston / team-project

Other
2 stars 39 forks source link

AJAX is sending an empty body to server #320

Closed jbeltrami closed 7 years ago

jbeltrami commented 7 years ago

We're trying to make an upload to S3, using the request body to pass in data. But our AJAX request is passing an empty body to our controller.

This is the event that creates the form:

'use strict'

// const getFormFields = require('../../../lib/get-form-fields')

const uploadApi = require('./api')
const uploadUi = require('./ui')

const createUploadMultiPart = function (event) {
  event.preventDefault()
  console.log(`createUploadMultiPart Event: ${event.target}`)

  const myForm = event.target
  const data = new FormData(myForm)

  // const title = $('#file-upload-title').val()
  // const file = $('#file-upload-file').val()

  // const data = new FormData()
  // data.append('title', title)
  // data.append('file', file)

  // const data = {
  //   title: title,
  //   file: file
  // }

  // debugger

  console.log(`createUploadMultiPart data: ${data}`)

  uploadApi.createMulti(data)
    .then(uploadUi.success)
    .catch(uploadUi.error)
}

const addHandlers = function () {
  $('body').on('submit', '#multipart-form-data', createUploadMultiPart)

  $('body').on('change', '#file-selector', () => {
    const filename = $(event.target).val().replace(/.*[\/\\]/, '')
    // file-upload-title
    $('#file-upload-title').val(filename)
  })
}

module.exports = {
  createUploadMultiPart,
  addHandlers
}

This is how the HTML looks like:

<div id="upload-modal" class="modal fade" role="dialog">
  <div class="modal-dialog modal-md">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal">&times;</button>
        <h4 class="modal-title">File Upload</h4>
      </div>
      <div class="modal-body">
        <form id="multipart-form-data" enctype="multipart/form-data" class="form form-inline">
          <fieldset>

            <div class="form-group">
              <label for="file-upload-title" class="sr-only">Title</label>
              <input type="text" class="form-control" name="file[name]" id="file-upload-title" placeholder="Title" value="">
            </div>

            <div class="form-group">
              <label class="btn btn-primary" for="file-selector">
                <input class="form-control" id="file-selector" type="file" style="display:none;" name="file[path]">
                Choose File
              </label>
            </div>

            <button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-cloud-upload"></span> Upload</button>

          </fieldset>
        </form>
      </div>
      <div class="modal-footer">
        <!-- <button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span> Cancel</button>
        <button type="button" class="btn btn-danger" data-dismiss="modal" id="delete-modal-confirm"><span class="glyphicon glyphicon-trash"></span> Delete</button> -->
      </div>
      </form>
    </div>
  </div>
</div>

This is the API call:

'use strict'

const config = require('../config')
const store = require('../store')

const createMulti = function (data) {
  console.log(`createMulti data: ${data}`)
  console.log(data.get('file[name]'))
  console.log(data.get('file[path]'))
  return $.ajax({
    // ajax options go here
    method: 'POST',
    url: config.apiOrigin + '/uploads',
    headers: {
      'Authorization': 'Token token=' + store.user.token
    },
    data,
    contentType: false,
    processData: false
  })
}

module.exports = {
  createMulti
}

This is my route on the server side:

'use strict';

module.exports = require('lib/wiring/routes')

// create routes

// what to run for `GET /`
.root('root#root')

// standards RESTful routes
.resources('examples')

// File managing routes
.resources('uploads', { except: ['edit', 'new'] })
// .get('/uploads', 'uploads#index')
// .get('/uploads/:id', 'uploads#read')
// .delete('/uploads/:id', 'uploads#delete')
// .patch('/uploads/:id', 'uploads#update')

// users of the app have special requirements
.post('/sign-up', 'users#signup')
.post('/sign-in', 'users#signin')
.delete('/sign-out/:id', 'users#signout')
.patch('/change-password/:id', 'users#changepw')
.resources('users', { only: ['index', 'show'] })

// all routes created
;

And this is how the controller is handling that request:

'use strict';

const controller = require('lib/wiring/controller');
const models = require('app/models');
const Upload = models.upload;

const multer = require('multer')
const multerUpload = multer({ dest: '/tmp/' })

const authenticate = require('./concerns/authenticate');
const setUser = require('./concerns/set-current-user');
const setModel = require('./concerns/set-mongoose-model');

const s3Upload = require('lib/aws-s3-upload')

const create = (req, res, next) => {
  console.log('I\'m getting into create')
  console.log(req.body);
  let upload = Object.assign(req.body.upload, {
    _owner: req.user._id
  })
  console.log('upload is: ', upload);

  const file = {
    path: req.file.path,
    name: req.file.name,
    originalname: req.file.originalname,
    mimetype: req.file.mimetype
  }
  console.log('file is: ', file)

  s3Upload(file)
  .then((s3Response) => {
    console.log('s3Response is: ', s3Response);
    return Upload.create({
      url: s3Response.Location,
      name: file.name,
      originalname: req.file.originalname,
      mimetype: req.file.mimetype
    })
  })
  .catch((error) => {console.error})

};

I'm getting a 500 error from the server and the script never gets to the line of creating upload (on the controller) @pjliddy @prankmode @eliottenos

scottyscripts commented 7 years ago

take a look at your route for the uploads controller. Is it creating a route for all restful actions?

jbeltrami commented 7 years ago

It is getting into the action I want in the controller. I'm trying to get to create. The problem seems to be with the body of the object I'm passing in with req.

jbeltrami commented 7 years ago

The solution was to set multer as a before action and fix the owner of the new upload. All changes made to the controller that now looks like this:

'use strict';

const controller = require('lib/wiring/controller');
const models = require('app/models');
const Upload = models.upload;

const multer = require('multer')
const multerUpload = multer({ dest: '/tmp/' })

const authenticate = require('./concerns/authenticate');
const setUser = require('./concerns/set-current-user');
const setModel = require('./concerns/set-mongoose-model');

const s3Upload = require('lib/aws-s3-upload')

const index = (req, res, next) => {
  console.log('I\'m getting in index.')
  Upload.find()
    .then(uploads => res.json({
      uploads: uploads.map((e) =>
        e.toJSON({ virtuals: true, user: req.user })),
    }))
    .catch(next);
};

const show = (req, res) => {
    console.log('I\'m getting in show.')
  res.json({
    upload: req.upload.toJSON({ virtuals: true, user: req.user }),
  });
};

const create = (req, res, next) => {

  const file = {
    path: req.file.path,
    name: req.body.file.name,
    originalname: req.file.originalname,
    mimetype: req.file.mimetype
  }

  s3Upload(file)
  .then((s3Response) => {
    return Upload.create({
      url: s3Response.Location,
      name: file.name,
      _owner: req.user._id,
      originalname: req.file.originalname,
      mimetype: req.file.mimetype
    })
  })
  .catch((error) => {console.error})

};

const update = (req, res, next) => {
    console.log('I\'m getting in update.')
  delete req.body._owner;  // disallow owner reassignment.
  req.upload.update(req.body.upload)
    .then(() => res.sendStatus(204))
    .catch(next);
};

const destroy = (req, res, next) => {
    console.log('I\'m getting in destroy.')
  req.upload.remove()
    .then(() => res.sendStatus(204))
    .catch(next);
};

module.exports = controller({
  index,
  show,
  create,
  update,
  destroy,
}, { before: [
  {method: multerUpload.single('file[path]'), only: ['create'] },
  { method: setUser, only: ['index', 'show'] },
  { method: authenticate, except: ['index', 'show'] },
  { method: setModel(Upload), only: ['show'] },
  { method: setModel(Upload, { forUser: true }), only: ['update', 'destroy'] },
], });