PATCH and PUT Request Does not Working with form-data
Asked Answered
P

15

92

I am using Laravel to create a RESTFUL application and I test the application with Postman. Currently, there is an issue for PATCH or PUT if the data sent from Postman with form-data.

// Parameter `{testimonial}` will be sent to backend.
Route::post  ('testimonials/{testimonial}', 'TestimonialController@update');

// Parameter `{testimonial}` will not be sent to backend (`$request->all()` will be empty) if sent from Postman with form-data.
Route::patch ('testimonials/{testimonial}', 'TestimonialController@update');
Route::put   ('testimonials/{testimonial}', 'TestimonialController@update');
  • Using form-data, $request->all() will be okay for POST.
  • Using x-www-form-urlencoded, $request->all() will be okay for PATCH, PUT, and POST.
  • However, if I am sending PUT and PATCH with form-data from Postman, the $request->all() will be empty (the parameters will not be sent to backend).

Right now the solution is to use POST for updating a model. I want to know why PATCH and PUT is not working when sent with form-data from Postman.

Prostomium answered 5/6, 2018 at 4:15 Comment(7)
I don't think it covers why would form-data does not work with PATCH and PUT request though.Prostomium
Related: laracasts.com/discuss/channels/requests/…Sumptuary
More Related: github.com/laravel/framework/issues/13457Sumptuary
I saw that, it does not have solution yet until now.Prostomium
You must not have read it properly, please check my answer.Sumptuary
I did not. Read it again... still can't find why your links give me solution.Prostomium
What a huge waste of time. I can't believe this is still an issue.Gonta
S
89

I learnt how to solve it here on this post and I'd like to share what did I do.

The following image is how I setup the Postman to send a HTTP POST request and go into PUT Request and make it receive my files.

I'm not sure whether it is the right way to do a RESTFul API. But it works fine

An example on Postman how to setup your HTTP Request

Suzysuzzy answered 20/3, 2020 at 21:12 Comment(2)
in my case _method=PUT didn't work but _method=PATCH works fineDore
We can send _method=PUT as params or input value alsoRamose
S
85

This is a known issue and the workaround suggestion as per the following Github comment is that when sending a PATCH / PUT requests you should do the following:

You should send POST and set _method to PUT (same as sending forms) to make your files visible

So essentially you send a POST request with a parameter which sets the actual method and Laravel seems to understand that.

As per the documentation:

Since HTML forms can't make PUT, PATCH, or DELETE requests, you will need to add a hidden _method field to spoof these HTTP verbs. The @method Blade directive can create this field for you:

<form action="/foo/bar" method="POST">
    @method('PUT')

    ...
</form> 

Alternatively, you can use the method_field helper function to do the above:

The method_field function generates an HTML hidden input field containing the spoofed value of the form's HTTP verb. For example, using Blade syntax:

<form method="POST">
    {{ method_field('PUT') }}
</form>
Sumptuary answered 5/6, 2018 at 4:22 Comment(4)
I am making RESTFUL appProstomium
I don't understand how that would change the fact that it is a known issue? You need to go about it differently. It is as simple as that. It might not be the ideal answer you'd like to have but it is the given answer.Sumptuary
This does seem like an unnecessary limitation. Most modern HTTP clients that would be used to fuel a REST API can make any kind of request and aren't limited like browsers. So allowing for different methods with at POSTed form is great, but that's no reason to not be able to treat a proper PUT request with multipart/form-data as exepcted.Tara
in html manually set field _method of a form or FormData is referred to as "Form Method Spoofing"Indistinctive
S
17

Laravel PATCH and PUT method does not work with form-data, it's known issue of Symfony and even PHP (Google for that - Laravel use many Symfony foundation packages, include Request).

  1. If you do not need to pass file(s) via request, change form-data to raw with json content-type. E.g: {"name":"changed"}. It will be read as php://input and your code should work well ($request->all() is now ["name" => "changed]).

  2. If you need to pass file(s), in my opinion, DO NOT pass it within the REST API methods. You can write another method to do whatever you need with your file(s) (E.g: POST form-data -> upload file -> update db -> return a file path/url/even its base64 content), then you can use its output/result to continue with your patch/put method (raw with json content-type). I always do that when I work with files in API.

Hope this help!

Slipperwort answered 5/6, 2018 at 4:54 Comment(2)
What if I change PATCH or PUT into POST instead? Is this a good practice?Prostomium
It's up to you. However, we should use a convention for our REST API. POST to create new, PATCH or PUT to update, DELETE to delete... Sorry for replying you late. Super late. :DSlipperwort
P
16

so as everyone mentioned above and explained everything, but still i dont see the answer for cases when using a REST API so i fallowed @Caique Andrade answer and send a POST request and formed my URL link like this:

url = 'https://yourwebsite.com/api/v1/users/$id?_method=PUT';

$id is the variable id for the user.

?_method=PUT is added to the url POST request to spoof the request and it works

in my case i used Dart in flutter and sent a post request using Http package Laravel catches that POST request as a PUT request

Porush answered 22/5, 2020 at 16:52 Comment(3)
I'm getting a 403 code when I use this method. Any suggestion? Will need to update something on the backend?Beadroll
you probably not setting it right, create a question and provide your code so we can help you solve it, this method works perfectly in laravel 7 projectsPorush
Thank you @RidhaRezzag, this worked great for a Laravel 8 form data with multiple images put ajax call.Gaol
G
5

InertiaJS Solution

I had the same problem. When no file was send, everything works perfectly, but when i send a file, none of the fields make it to the backend.

It seems that it's a PHP limitation, receiving no file via put, as said here before.

So, for those using InertiaJS, you need to make a post - instead of a put - call and add _method: "put" to your inertia form, like this:

updateForm: this.$inertia.form({
            _method: "put",
            "other fields"
        }),

Your controller will understand it like a PUT call, but with the file accessible to the backend.

Source: https://inertiajs.com/manual-visits#method

Gwenny answered 24/7, 2021 at 20:44 Comment(0)
V
4

As mentioned, this isn't a symfony (or laravel, or any other framework) issue, it's a limitation of PHP.

After trawling through a good few RFCs for php core, the core development team seem somewhat resistant to implementing anything to do with modernising the handling of HTTP requests. The issue was first reported in 2011, it doesn't look any closer to having a native solution.

That said, I managed to find this PECL extension. I'm not really very familiar with pecl, and couldn't seem to get it working using pear. but I'm using CentOS and Remi PHP which has a yum package.

I ran yum install php-pecl-apfd and it literally fixed the issue straight away (well I had to restart my docker containers but that was a given).

That is, request->all() and files->get() started working again with PATCH and PUT requests using multipart/form-data.

I believe there are other packages in various flavours of linux and I'm sure anybody with more knowledge of pear/pecl/general php extensions could get it running on windows or mac with no issue.

Vinna answered 25/6, 2020 at 13:43 Comment(0)
D
4

I hope it is not too late, or if someone is seeking help with the FormData interface of JavaScript. Here is the solution, In Laravel, you can use @script47 answers above, for normal Ajax request you can append the data like this, (PS: I'm using same form for Add and Update so here is my code)

let _url = '';
let _type = 'POST';
let _formData = new FormData(this);
if(user_id == '' || user_id == null){
    _url = "{{ route('users.store') }}";
}else{
    _url = "{{ route('users.update', ':id') }}";
    _url = _url.replace(':id', user_id);
    _formData.append('_method', 'PUT');
    // _type = 'PUT';
}
Disjoin answered 9/11, 2021 at 6:25 Comment(1)
why encapsulate not object oriented code mate?Columnar
R
3

As @DazBaldwin says, this is a php limitation and can be solve installing apfd extension. On windows just download the dll file here according to your system settings and put php_apfd.dll on path-to-php/ext directory finally put extension=apfd in php.ini file.

it worked for me on windows.

Rocket answered 26/10, 2020 at 20:20 Comment(0)
S
1

The form media types do not have any semantics defined for PATCH, so it's really a bad idea to use them (see https://www.rfc-editor.org/errata/eid3169).

For PUT, the expected behaviour would be to store just the form-encoded payload (in that format). Is this really what you want here?

Stratocumulus answered 5/6, 2018 at 8:5 Comment(0)
S
1

If You Route Method Is Patch And Use Postman For Api Request

If you want to send a file, for the controller, when using postman, you must set the sending mode to the post method and in the form-data section key = _Method Value = PATCH You do not have to set the file so that you do not encounter any errors when sending the request.

Soaring answered 15/12, 2021 at 17:58 Comment(0)
G
1

use x-www-form-urlencoded instead of form-data, in your request:

Content-Type: application/x-www-form-urlencoded

Gaelic answered 13/4, 2023 at 5:13 Comment(0)
N
1

I didn't realise this issue until moving everything into docker getting ready for EKS. when i tested i realised that PATCH, PUT and DELETE verbs were not working in laravel and realised as a result that doesn't naturally work in php.

I then read up on apfd extension which to me not realising was on the old servers already installed so would never have noticed all this time that when i setup the servers i installed the apfd extension.

So in docker i simply added the command

RUN pecl install --force apfd && rm -rf /tmp/pear && docker-php-ext-enable apfd
N answered 6/2 at 20:24 Comment(0)
S
0

You can also create a custom function in your controller to update the product then create an api route.

  public function updateTestimonial($id, Request $request) {
    $testimonial = Testimonial::where('id', '=', $id)->first();
    //update logic
}

Api route

Route::post('updatetestimonial/{id}', 'Testimonial@updateTestimonial');

Use a post request and pass testimonial id.

submitForm() {
        let data = new FormData();
        data.append('id', this.testtimonial.id);
        data.append('description', this.testtimonial.description);
        axios.post("/api/updatetestimonial/" + this.testimonial.id , data, {
            headers: {
                'accept': 'application/json',
                'Accept-Language': 'en-US,en;q=0.8',
                'Content-Type': 'multipart/form-data',
            }
        }).then(({ data }) => {
             console.log("success");
        });
    },
Strapless answered 12/10, 2021 at 10:35 Comment(0)
Q
0

I know this article is old.

But unfortunately, PHP still does not pay attention to form-data other than the Post method.

But you can use the library I wrote for this purpose:

composer require alireaza/php-form-data

You can also use composer require alireaza/laravel-form-data in Laravel.

Quenna answered 10/12, 2022 at 14:14 Comment(0)
W
-1

You can use post method. const form = new just append form.append('_method', 'PATCH');

Whencesoever answered 30/1, 2021 at 5:14 Comment(2)
Give us more detailsSuzysuzzy
Not a full and valid answer. Be more clear next timeValerio

© 2022 - 2024 — McMap. All rights reserved.