Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Pass FormData using feathers-rest-client #1744

Open
bwgjoseph opened this issue Dec 16, 2019 · 9 comments
Open

Pass FormData using feathers-rest-client #1744

bwgjoseph opened this issue Dec 16, 2019 · 9 comments

Comments

@bwgjoseph
Copy link
Contributor

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
Copy link
Member

daffl commented Dec 17, 2019

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
Copy link
Contributor Author

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') and multer.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
Copy link
Member

daffl commented Dec 20, 2019

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
Copy link
Contributor Author

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
Copy link
Contributor Author

Hi @daffl,

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

Some keys points:

  • (Using restclient/fetch) Manually setting Content-Type to multipart will cause multer to throw Multipart: Boundary not found {"code":500} error
  • (Using restclient/fetch) I cannot don't set Content-Type as well, as it will then send out as application/json which I guess you are right on the bug where it's always stringify the body. But I'm not sure what's the exact issue with Boundary not found. So even if you remove the stringify, I suspect this Boundary issue will still exist.

image

  • (Using native/fetch) I can do like this, without needing to define the Content-Type manually, and the POST to server will work, and multer will decode the files correctly.
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

  • I then went ahead to test using restclient/axios setup, and it actually works. I didn't test all the others though.

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

  • Side issue: Not sure why there will be a type error if I were to do this.
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
Copy link

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

@bwgjoseph
Copy link
Contributor Author

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
Copy link

jurajpiar commented Nov 11, 2020

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
Copy link

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.

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

No branches or pull requests

5 participants