35

I'm sending request includes form data object with some data from angular 4 to laravel api ... sometimes request data is received correctly, other times request is null 'empty request'

and here is my request details

Accept:application/json
Accept-Encoding:gzip, deflate, br
Accept-Language:en-US,en;q=0.9
Authorization:Bearer ---------
Connection:keep-alive
Content-Length:973
Content-Type:multipart/form-data; boundary=----WebKitFormBoundarydEkuATdI8JBFdnBM
Host:127.0.0.1:8000
Origin:http://localhost:4200
Referer:http://localhost:4200/shop_admin
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36

Request Payload

------WebKitFormBoundarydEkuATdI8JBFdnBM
Content-Disposition: form-data; name="business_id"

249
------WebKitFormBoundarydEkuATdI8JBFdnBM
Content-Disposition: form-data; name="maintenance_flag"

0
------WebKitFormBoundarydEkuATdI8JBFdnBM
Content-Disposition: form-data; name="type"

shop
------WebKitFormBoundarydEkuATdI8JBFdnBM
Content-Disposition: form-data; name="name"

qewqweqweqwe
------WebKitFormBoundarydEkuATdI8JBFdnBM
Content-Disposition: form-data; name="website_uri"


------WebKitFormBoundarydEkuATdI8JBFdnBM
Content-Disposition: form-data; name="phone_number"


------WebKitFormBoundarydEkuATdI8JBFdnBM
Content-Disposition: form-data; name="facebook_link"


------WebKitFormBoundarydEkuATdI8JBFdnBM
Content-Disposition: form-data; name="logo_uri"

uploads/businesses/249/249_1.jpg
------WebKitFormBoundarydEkuATdI8JBFdnBM
Content-Disposition: form-data; name="brands"

undefined
------WebKitFormBoundarydEkuATdI8JBFdnBM--

here is a screenshot for request back from laravel into console

UPDATE

here is my code

angular side:

form data object here is angular form data object

let formData: FormData = new FormData();
formData.append( 'business_id', that.businessId );
formData.append( 'maintenance_flag', that.maintenance_flag );
formData.append( 'type', edit_type );
formData.append( 'name', name );
formData.append( 'website_uri', website );
formData.append( 'phone_number', phone );
formData.append( 'facebook_link', face );
formData.append( 'logo_uri', that.basicData.logo_uri );
formData.append( 'brands', that.selectedBrands );
if ( pic.files.length > 0 )
  formData.append( 'logo_uri', pic.files[ 0 ] );

api:

that.apiService.changeBusiness( formData ).subscribe( ( res ) => 
{
    console.log( res );
}

shopUpdate ( shop_basic_info ){

    return this.http.post( environment.api_base_url + 'api/shop_update', shop_basic_info ).map( res => res.json() );

}

laravel side

Route

Route::post('/shop_update', 'ShopController@handleUpdate');

controller file

public function handleUpdate(Request $request)
{
    return $request->all();
}
Mohamed Magdy
  • 351
  • 1
  • 3
  • 4

7 Answers7

61

This is a problem with PHP.

It does not parse multi part form data unless the request method is POST:

https://bugs.php.net/bug.php?id=55815

Solution:

There are many workarounds, none with is tidy but:

  1. Use PATCH/PUT for best practice, or just simply POST for demo scenarios.

  2. If you can't switch method, then simply rework the code from not using Formdata and PHP will process the file properly.

  3. Send with POST and add _method:put on formData

(3) Like so:

formData.append('_method', 'PUT')
PatricNox
  • 3,306
  • 1
  • 17
  • 25
5

I was getting this problem with Axios via JavaScript because the content-type header was multipart-form-data but the boundary was missing.

Based on my research, a good way to handle it is to allow Axios to auto-detect the content type and set the headers correctly itself.

Here is an idea for how to accomplish this:

const formDataWithFiles = hasFiles ? new FormData() : undefined;

if (formDataWithFiles) {
    // axios will automatically set the content-type to multipart/form-data if the
    // data param is a FormData object
    // otherwise, it will use application/json
    // (study the Dev Tools > Network tab > XHR tab headers)
    Object.keys(modifiedFields)
        .forEach(field => formDataWithFiles.append(field, modifiedFields[field]));
}

const { data } = await axios({
    method,
    url: actionUrl,
    data: hasFiles ? formDataWithFiles : modifiedFields,
    headers: {
        ...axios.defaults.headers,
        ...headers,
    },
});

return data;

The above code is in a generic handleSubmit function that can be called from anywhere in the client-side.

Here is the function signature:

const { data } = await this.submitForm({
    actionUrl: this.actionUrl,
    method: this.method,
    modifiedFields: {
        ...this.modifiedUser,
    },
    hasFiles: true,
});

In the above code, there are two use cases. The first is the default case, where a normal payload is sent via a flat object. The second is the case when the form has files and you want multipart/form-data. In this case, we use the FormData Object as a vessel to instruct Axios to auto-detect the necessary headers and set the correct boundary.

If you do not specify the headers correctly, it is possible to receive an empty $request->all() Array in Laravel.

The short answer to my answer is to use the FormData Object because it contains more information than a plain-old-JavaScript-object. With it, you can also access:

const formData = new FormData();

console.log('boundary:', formData._boundary);

As my annotation above hints towards, use the Dev Tools > Network tab > XHR tab to examine your request headers and make sure you have content-type application/json or application/x-www-form-urlencoded for regular form submits and multipart/form-data' if you are uploading a file.

agm1984
  • 15,500
  • 6
  • 89
  • 113
  • I've been scratching my head for an hour - thank you so much for pointing out this seemingly obvious idea - "let the framework do its job and set the headers for you" – Kevin Foster Aug 15 '20 at 01:35
  • 1
    We all do it. Sometimes the trick is to just ram your head into the keyboard a couple times. – agm1984 Oct 31 '20 at 20:39
  • Been on this for the whole day. In my own case using the JavaScript fetch API. However, allowing the API to automatically set the header did the trick for me. Initially I was manually setting the multipart/form-data header programmatically. – Dev May 05 '23 at 19:48
2

Be aware that FormData only works with POST method.
@see PATCH and PUT Request Does not Working with form-data

Niko
  • 813
  • 8
  • 11
0

you must add CSRF token in to Request Add : formData.append( '_token', {{csrf_token()}} );

0

1- you need the token unless your route is not under web middleware 2- check the post size it might be bigger than the max allowed post size...if it is bigger you have to increase the allowed max post size in the php.ini or .htaccess or inside the function

mohammad zein
  • 300
  • 3
  • 6
-1

Check file_uploads, upload_max_filesize, post_max_size directive in php.ini.

MingalevME
  • 1,827
  • 1
  • 22
  • 19
-2

You should use like this:

$request->file('filename')
סטנלי גרונן
  • 2,917
  • 23
  • 46
  • 68