How to get text data from FormData? request.all() not working

Say I do something like formData.append('username', 'Chris'). How would I obtain this value in Adonis? I tried doing request.all(), but this gave me an empty object. I can clearly see the FormData being passed in on request.

2 Likes

Hi :wave: , welcome to our community

Can you please share more details (http request result, form, controller, …)

Thank you for your reply.

General

Headers

  • Content-Length:
    330296

  • Content-Type:
    multipart/form-data

Payload

------WebKitFormBoundarykKYddVTth7zH5iY6
Content-Disposition: form-data; name=“username”

Chris
------WebKitFormBoundarykKYddVTth7zH5iY6
Content-Disposition: form-data; name=“file”; filename=“avatar.png”
Content-Type: image/png

------WebKitFormBoundarykKYddVTth7zH5iY6–

Response

{}

Controller

  async upload({ request }) {
    return request.all();
  }

Please let me know if you need any more info.

2 Likes

Thx. Do you want to send a file? Is that correct?

If so, I advise you to follow the documentation : https://adonisjs.com/docs/4.1/file-uploads#_basic_example

Like:

...
const image = request.file(...)

Yes, I am uploading a file, but it’s not really relevant for the question I’m asking. I’m looking to retrieve the text values from the form data. So for instance, I did formData.append('username', 'Chris'). How would I retrieve the value of username?

Make sure you hit right controller method in router.

What’s the content of “files” in “config/bodyParser.js”

Especially “autoProcess” and “processManually”

1 Like

I tried on my side successfully ( request.all()).
Try to do what @McSneaky said :slightly_smiling_face:

I tested my controller to add my hard coded values in the return, and I clearly am hitting the correct controller in the router.

my values are: autoProcess: false and processManually: ['/upload']. I have tried various configurations of this and always get the same result, even if I leave out upload or set autoProcess to true.

Can someone maybe someone please give me instruction on how to achieve this server-side? I would be very grateful. Again, I’m using FormData and I want to get the appended text values. I honestly can’t figure out how you guys got it to work.

Okay. Can you share the project? Or the important parts?
So I can try to reproduce the problem

Ok, so I figured it out. The reason it didn’t work is because you can’t set the Content-Type header to multipart/form-data. It needs a full one of something like:

multipart/form-data; boundary=----WebKitFormBoundaryBlnuCrwlkolTNNCx

So how do you get this? Just don’t pass in anything for Content-Type and it’ll automatically do it for you.

Then to get the text values, you have to do (credit):

const body = {};

await request.multipart.field((name, value) => {
  body[name] = value;
});

await request.multipart.process();

return body;

Thanks to both of you guys for trying. I really appreciate the effort.

4 Likes

You have to get values out of request manually because you have processManually enabled for given route.

Enabling manual process is quite useless on small file uploads imo, but it starts to makes sense when you want to deal with bigger files and data streams

2 Likes

Isn’t it preferred to disable auto-processing when using something like S3? Also, this was actually the only way I was able to successfully get the text values of formData. No solution worked when enabling auto-processing for me. I’d be happy to take a look at a solution for that if you have one.

Specific processManually rule overwrites general autoProcess value, if autoProcess is true but route matches route in processManually then it will still be manually processed.

For s3 it’s disabled in example because in there it’s sending streams. Whenever you want to deal with file streams directly it’s better to disable it (otherwise you’d have to read file from file to stream manually)

When dealing with small image uploads, like profile pics etc I go with automatic processing since stream validation is pain in the arse. Built in validations do not work for file streams. You can let your external provider do validation tho, then you have to handle all kinds of different errors.

Easier to validate profile image size, extensions etc with built in validator

Regarding example code it’s quite straightforward. Create new Adonis API (or fullstack or slim, for slim need to add bodyParser)
adonis new test --api

Add route

Route.post('/upload', async ({ request }) => {
  return request.file('image')
})

And make request

No need to make any config changes or anything :slight_smile:

2 Likes

I pretty much always use S3 for uploads, but thank you for letting me know. I just have one question though, how would I retrieve the appended text values in the FormData with auto-processing enabled? I tried to do something like request.field('username'), but that didn’t work.

request.field(...) does not exist.
Try to use : const username = request.input('username')

1 Like

Its a shame that this part:

await request.multipart.field((name, value) => {
  body[name] = value;
});

is not in the docs.

I had a form where a file is sent together with data, and was being sent via JavaScript to server (using FormData).

I was pulling my hair out on why request.all() is empty :confused:

Its times like this that I wish there was a way to set processManually on controllers. Its hard to keep track of routes and add to the fact that you had to set it in a config file. What if the route changes? Can we use route names in processManually?

You can use route patterns

But routes really shouldn’t change too often, especially when writing API

Hello,

The reason I’m here is because I’m facing a problem related to this topic.
I have a function that works with a request which has a header Content-Type: multipart-form.

The body of the request (data) is sent through an axios instance in a React App and it has this structure:

const fd = new FormData();
fd.append('questions', questions) //This is an array of objects
fd.append('file', myFile) //This is an uploaded file by the user

I sent that fd through axios and received the request in my controller in Adonis.

When I’m reading the request in the method at my controller I face this issue; I’m trying to store the input ‘questions’ which is an array of objects by doing this:

const questionsArray = request.input('questions');

However, when I print it in the console I get this:

console.log(questionsArray)
Outputs:
{ questions: '[object Object],[object Object],[object Object]' }

I have no way to access to each object of the array. It’s like the variable questionsArray was just storing an object with the key ‘questions’ and the rest ‘[object Object],[object Object],[object Object]’ was only a String and not the actual array of objects.

I must have the request’s header as multipart because I’m trying to store files too. In order to do so, I have to have access to the array of objects first.

Do you have and idea why I cannot access to it?

I already try to iterate with a forEach and for() loop through the questionsArray object.

I also tried using the following way, but it prints an empty object instead:

await request.multipart.field((name, value) => {
   if(name === 'questions') {
     questionsArray = value;
   }
  });
1 Like