Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel $request->all() is empty using multipart/form-data

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();
}
like image 887
Mohamed Magdy Avatar asked Dec 06 '17 14:12

Mohamed Magdy


2 Answers

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')
like image 149
PatricNox Avatar answered Oct 16 '22 14:10

PatricNox


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.

like image 4
agm1984 Avatar answered Oct 16 '22 15:10

agm1984