feathersjs / feathers

The API and real-time application framework
https://feathersjs.com
MIT License
15.08k stars 752 forks source link

Pass FormData using feathers-rest-client #1744

Open bwgjoseph opened 4 years ago

bwgjoseph commented 4 years ago

Hi,

I would like to use feathers-rest-client and send data through rest with attachments (FormData), together with the rest of the body. The application is setup and configure with both rest and socket client. If there are data with attachments, it will use rest client to communicate with server, otherwise, use socket client.

I have configured feathers-rest-client to pass data to server. feathers-rest-client is configured something like this.

const app = feathers();
const restClient = rest('http://localhost:3030')
app.configure(restClient.fetch(window.fetch));

There's a form for user to fill up some fields, and add some attachment. After which I hope to call the test endpoint using feathers-rest-client as such..

fData = {
 field1: 'test',
 field2: 'test2',
 file: [attachments] // this is a FormData
}

// Also tried
FormData fData = new FormData();
fData('field1', 'test');
fData('field2', 'test2');
fData('file', attachments);
restClient.service('test').create(fData);

Server has something like this.

app.use('/test', multer.array('file'), setReqFileToFeatherFile(), createService());

However, no matter how I change it, I am not able to get and extract the data at the server side. Meaning to say, I think the data is set wrongly, and hence, multer does not know how to extract/decode from the correct field to get the attachments and so on.

But If I were to use POSTMAN to send a request to my endpoint, server is able to extract/decode the data correctly. Meaning to say, the file are passed through multer to extract the attachments (available in feathers.params.files), and the body are able to retrieve from context.data.

image

Any idea what am I doing wrong?

Thanks.

daffl commented 4 years ago

It's probably because the rest-client will always request application/json not form-data. You should be able to set the appropriate header for the request via params.headers:

restClient.service('test').create(fData, {
  headers: {
    'Content-Type': 'multipart/form-data'
  }
});
bwgjoseph commented 4 years ago

I tested it earlier on, but it doesn't seem to work.

By setting the headers as suggested, it will throw this error. Multipart: Boundary not found. I did some searching, and it seem like there need to have some Boundary value attached to the Content-Type like such..

Content-Type: 'multipart/form-data; boundary=-------------------12398123493844

When using native fetch or postman, this Boundary thingy seem to be generating, but not when using feathers-rest-client.

const fData = new FormData();
  fData.append('filename', 'x');
  fData.append('file', attachments);
  // This will generate the Boundary
  await fetch(url/service, {
    method; 'POST',
    body: fData,
  })

  // This will not
  restClient.service('test').create(fData, {
  headers: {
    'Content-Type': 'multipart/form-data'
  }
});

Even when using fetch, I tried to print out the context.data received, it gives me the following...

{
  filename: 'x',
  file: '[object File]'
}

multer doesn't seem to be able to decode the body and grab the files to process. I have tried with both multer.array('file') andmulter.fields([{ name: 'file' }])

When I print req.feathers.file , it gives me {}


I will be working on trying to get a repo this couple of days so maybe you could get a better idea on where went wrong. Meanwhile, if you could think of anything for me to try out, just let me know.

daffl commented 4 years ago

Ah, I think this may be a bug for the REST client always using JSON.stringify on the body in https://github.com/feathersjs/feathers/blob/master/packages/rest-client/lib/fetch.js#L12. You could use fetch directly for now and pass the Authorization header for authentication.

bwgjoseph commented 4 years ago

That seem to be the same in jquery? Would the same happen if I were to configure to use axios with feathers-rest-client?

I actually have tried with fetch directly, but multer is still not decoding the data correctly. Let me try to work some a repo to replicate the behavior.

bwgjoseph commented 4 years ago

Hi @daffl,

I have worked out a repo @ feathers-rest-upload-issue

Some keys points:

image

const fData = new FormData();
            fData.append('name', values.name);
            fData.append('type', values.type);
            for (let i = 0; i < values.files.length; i++) {
              fData.append('files', values.files[i]);
            }

fetch('http://localhost:3030/event', {
              method: 'POST',
              body: fData,
            });

image

app.configure(restClient.axios(axios));

app.use('/event',
    multerware.array('files'),
    (req, res, next) => {
      if (req.feathers) {
        req.feathers.file = req.files;
      }
      next();
    },
    // If don't cast to `any` here, will complain
    new Event(options, app));

image

So I had to cast to any for now.

app.use('/event',
    multerware.array('files'),
    (req, res, next) => {
      if (req.feathers) {
        req.feathers.file = req.files;
      }
      next();
    },
    new Event(options, app) as any);

Hope this helps!

cakesforfree commented 4 years ago

Sorry to bother, but what did you do at the end? I am having the exact same issues. :'C

bwgjoseph commented 4 years ago

Am using native fetch for now. But you can configure feathers-rest-client to use axios instead of fetch as mentioned in my post below.

app.configure(restClient.axios(axios))

This will work as well

jurajpiar commented 4 years ago

Ah, I think this may be a bug for the REST client always using JSON.stringify on the body in https://github.com/feathersjs/feathers/blob/master/packages/rest-client/lib/fetch.js#L12. You could use fetch directly for now and pass the Authorization header for authentication.

So.... is this going to be actioned?

Personally, I would really quite like to use the client (for multipart and with fetch) as I don't want to lose my abstraction just because there is a bug. On the other hand if it is for some reason unnecessary to keep it the way it is, this should be made clear (probably in this issue as well as in the docs!), so that users don't have to waste time with this if they need to send multipart.

lukvermeulen commented 2 years ago

Hey, does somebody know if there was any solution to this issue in the meantime? I appear to have the same issues with the rest client (configured with fetch) as the OP.