I am trying to send an image to my laravel APIs from postman.
I added the file(280KB) on the request body like so:
On the server side I am trying to catch the file and save it, but it returns that there is no file.
public function uploadImage(Request $request)
{
//returns false
$request->hasFile('profile_image');
//returns profile_image is required
$request->validate(['profile_image' => 'required|image:jpeg,png,jpg,gif,svg']);
//returns null
$request->file('profile_image');
}
I am calling the function from api.php like so:
Route::put('/creators/{id}/upload_image',[CreatorController::class,'uploadImage']);
I thought maybe I shouldn't put the file in Body, but couldn't find an alternative.
Also I am finally trying to send the file from a vue client, but I had the same Issue when trying to send the file from there.
How do I get the server to catch the file?
Edit: adjusted typo in example code
new code sample
I changed the method from put to post, since I wanted to test the post method as well. I also tried _method put in postman beforehand.
Here is the code sample on Laravel:
//api.php
Route::put('/creators/{id}/upload_image',[CreatorController::class,'uploadImage']);
//CreatorController.php
public function uploadImage(Request $request)
{
if($request->hasFile('profile_image')){
$allowedfileExtension=['gif','jpg','png'];
$file = $request->file('profile_image');
$extension = $file->getClientOriginalExtension();
if(in_array($extension,$allowedfileExtension)) {
$path = $file->store('public/images/profile');
$path_url = $path;
return ["image_url" => $path_url, "image" => $request->file('profile_image')];
}
return false;
}
}
The $request->hasFile('profile_image') returns true, but then $request->file('profile_image') returns null, failing to save the image.
My vue client side code (if it might turn useful):
if(this.profile_image != null){
let data = new FormData();
data.append('profile_image', this.profile_image)
axios
.post('http://localhost:8000/api/creators/'+uid+'/upload_image', data, head)
.then(
response => (
//successfully receives the image_url
this.creatorData.image_url = response.data.image_url,
console.log(response.data)
),
)
.catch(
error => (
localStorage.setItem('error', error),
console.log(error.response)
),
this.loading = false,
)
}
The client side actually receives the "image_url" but the image is not saved on laravel.
laravel dose not support put method directly.
you must use post method then pass _method to your laravel project
like this picture
Related
I'm working on a blog like website and have being adding this rich text editor feature to it. This app is built with Vue for the front and Laravel, and this text editor is a dependency called vue-quill.
I use axios to post all the data and nothing more. Im using it to create posts with Raw html tags from this editor, it actually works fine creating and updating posts locally, but only fails on my server whenever you try to update any post, it returns empty response and status 200.
It does not happen when you create a post. Im not using any kind of image upload service but I'm using my own solution, which consists on transforming images from base 64 to file objects and then send them to a controller via axios. On update action it is similar but the images that were already uploaded on the post are fetched, then converted to base64 again and then converted to file objects again (in order to preserve previous images) for new images I convert them from base64 to file object as I do on my create action.
Here is the code I use to create from base 64 to file object :
dataURLtoFile(dataurl, filename) {
let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, {type:mime});
}
And this would be my axios action:
axios
.post("/api/posts", formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then((response)=>{
this.submit = false;
this.title= '';
this.showAlert("success", 'Post created successfully.' , '')
})
.catch((error) =>{
// etc
});
The formData object only store the raw html, images and a string, nothing more than that, but I'm not sure if the headers of the axios are ok, the laravel action is like this:
public function updatePost(Request $request)
{
$request->validate([
'files.*' => 'required|mimes:jpg,jpeg,png|max:2048'
]);
$post = Post::find($request->postId);
$images = $request->file('files');
// creamos el post primero
$post->title = $request->title;
$post->body = $request->body;
$post->save();
// the rest of the code for storing images
return response()->json(['post' => $post]);
I think something is preventing it to reach to this action, because the response is empty.
Edit: I had a problem later where the request was giving status 301, after searching here and there I found out that everything was okay but in the server. The host has to be configured as stated here in this quick guide for 301 troubles : https://medium.com/#mshanak/laravel-return-empty-response-690f8a308d9a.
I am struggling with a weird problem. I am trying to upload a file to a different server using the API. I am using Laravel 8.X and Using HTTP client for the same. Below is my code which I am calling from a controller
if ($request->hasFile('uploadReceipt') && $request->file('uploadReceipt')->isValid()) {
$receiptContent = file_get_contents($request->file('uploadReceipt'));
$originalName = $request->file('uploadReceipt')->getClientOriginalName();
$responseUploadReceipt = Http::attach('attachment', $receiptContent, $originalName)
->withHeaders([
'Accept'=> 'application/json',
'Authorization' => 'Bearer '.$userAccessToken
])
->post($endpoint, $requestData);
dd($responseUploadReceipt->json());
} else {
dd ("Else");
}
I am getting A 'contents' key is required, in the file vendor/guzzlehttp/psr7/src/MultipartStream.php:86
I spent almost 12 hrs on it but unable to resolve this issue. I also tried with the stream like
$receiptContent = fopen($request->file('uploadReceipt'), 'r');
but the same issue. I followed the same way it is mentioned in the documentation. Anyone who can help me with this will be appreciated.
Thanks and Regards
Ashish
You were probably using multipart in conjunction with form_params , this is not explicitly explained in the documentation of laravel but guzzle won't work with both
Note
multipart cannot be used with the form_params option. You will need to
use one or the other. Use form_params for
application/x-www-form-urlencoded requests, and multipart for
multipart/form-data requests.
This option cannot be used with body, form_params, or json
To solve this problem you will need to parse all the params to multipart, if you are using laravel or lumen you can do it in this way
if(!empty($this->files))
{
//if there is an image parse all the rest parameters to
multipart
$file_keys=array_keys($this->files);
foreach($this->files as $k => $file)
{
$http = $http->attach($k, file_get_contents($file),$k);
}
foreach($this->data as $dk =>&$d)
{
if(!in_array($dk,$file_keys))
{
if(is_array($d))
{
$d=json_encode($d);
}
$http = $http->attach($dk,$d);
}
}
//if there isn't any file just send all as form_params
return $http=$http->post($this->url);
}
return $http=$http->post($this->url,$this->data);
https://docs.guzzlephp.org/en/stable/request-options.html#multipart
I think this line is the problem:
$receiptContent = file_get_contents($request->file('uploadReceipt'));
The examples in the documentation do not use the Request. $request->file('uploadReceipt') should return an instance of Illuminate\Http\UploadedFile and I'm pretty sure file_get_contents doesn't work with an UploadedFile as a parameter.
Digging deeper into the class file, it seems there is a method to get the UploadedFile's contents:
Illuminate\Http\UploadedFile
/**
* Get the contents of the uploaded file.
*
* #return false|string
*
* #throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
public function get()
{
if (! $this->isValid()) {
throw new FileNotFoundException("File does not exist at path {$this->getPathname()}.");
}
return file_get_contents($this->getPathname());
}
Try $receiptContent = $request->file('uploadReceipt')->get(); instead.
I am using the repo https://github.com/mschwarzmueller/laravel-ng2-vue/tree/03-vue-frontend so I have 100% confidence in the reliability of the code. I can post through the laravel api endpoint through the very simple Vue client, and also through Postman. Through Postman I can retrieve the table data array, but not so in the client app. In POSTMAN:
localhost:8000/api/quotes
works just fine.
IN THE vue 2 js CLIENT APP:
methods: {
onGetQuotes() {
axios.get('http://localhost:8000/api/quotes')
.then(
response => {
this.quotes = (response.data.quotes);
}
)
.catch(
error => console.log(error)
);
}
returns nothing. returning the response to Console.log returns nothing. The Network/XHR tab shows the table data rows, but I am not sure what that means.
I know for sure that this code works for others with their unique api endpoints, which I assume may not use localhost or '127:0.0.1:1080.
Edit: in response to request for more info
public function getQuotes()
{
$quotes = Quote::all();
$response = [$quotes];
return response()->json($response, 200);
}
and the relevant route:
Route::get('/quotes', [
'uses' => 'QuoteController#getQuotes'
]);
Just to confirm: I am using verified github repo code in which the ONLY change is my api endpoint addressas mentioned in the first line of the body of this question. . Note that the Laravel back end is also derived from a related repo in Max's fine tutorial. The running code can be seen at
So I really don't think this is a coding error- but is it a configuration error due to me using local host??
EDIT: It WAS a coding error in the laravel controller as shown below
The reason your code isn't working if because you haven't provided a key for your $quotes in your controller but you're looking for it in your vue file (response.data.quotes).
[$quotes] is essentially [0 => $quotes] so when the json response comes through it be 0: [...] not quotes: [...].
To get this to work you just need to change:
$response = [$quotes];
to:
$response = ['quotes' => $quotes];
Furthermore, just an FYI, you don't need to provide the 200 in response->json() as it's the default and you can just return an array and Laravel automatically return the correct json response e.g.:
public function getQuotes()
{
$quotes = \App\Models\Artist::all();
return compact('quotes'); //<-- This is just another way of writting ['quotes' => $quotes]
}
Obviously, you don't have to if you don't want to.
Hope this helps!
Is there a way, in Laravel 5, to call routes internally/programmatically from within the application? I've found a lot of tutorials for Laravel 4, but I cannot find the information for version 5.
Using laravel 5.5, this method worked for me:
$req = Request::create('/my/url', 'POST', $params);
$res = app()->handle($req);
$responseBody = $res->getContent();
// or if you want the response to be json format
// $responseBody = json_decode($res->getContent(), true);
Source:
https://laracasts.com/discuss/channels/laravel/route-dispatch
*note: maybe you will have issue if the route you're trying to access
has authentication middleware and you're not providing the right credentials.
to avoid this, be sure to set the correct headers required so that the request is processed normally (eg Authorisation bearer ...).
UPDATE: i've tried this method with laravel 8 and it works but if you're using PHP version 8.0 you might need to call opcache_reset(); before this line $req = Request::create('/my/url', 'POST', $params); to avoid an error.
see guzzlehttp/guzzle dosn't work after update php to php 8 for more info
You may try something like this:
// GET Request
$request = Request::create('/some/url/1', 'GET');
$response = Route::dispatch($request);
// POST Request
$request = Request::create('/some/url/1', 'POST', Request::all());
$response = Route::dispatch($request);
You can actually call the controller that associates to that route instead of 'calling' the route internally.
For example:
Routes.php
Route::get('/getUser', 'UserController#getUser');
UserController.php
class UserController extends Controller {
public function getUser($id){
return \App\User::find($id);
};
}
Instead of calling /getUser route, you can actually call UserController#getUser instead.
$ctrl = new \App\Http\Controllers\UserController();
$ctrl->getUser(1);
This is the same as calling the route internally if that what you mean. Hope that helps
// this code based on laravel 5.8
// I tried to solve this using guzzle first . but i found guzzle cant help me while I
//am using same port. so below is the answer
// you may pass your params and other authentication related data while calling the
//end point
public function profile(){
// '/api/user/1' is my api end please put your one
//
$req = Request::create('/api/user/1', 'GET',[ // you may pass this without this array
'HTTP_Accept' => 'application/json',
'Content-type' => 'application/json'
]);
$res = app()->handle($req);
$responseBody = json_decode($res->getContent()); // convert to json object using
json_decode and used getcontent() for getting content from response
return response()->json(['msg' =>$responseBody ], 200); // return json data with
//status code 200
}
None of these answers worked for me: they would either not accept query parameters, or could not use the existing app() instance (needed for config & .env vars).
I want to call routes internally because I'm writing console commands to interface with my app's API.
Here's what I did that works well for me:
<?php // We're using Laravel 5.3 here.
namespace App\Console;
use App\MyModel;
use App\MyOtherModel;
use App\Http\Controllers\MyController;
use Illuminate\Console\Command;
class MyCommand extends Command
{
protected $signature = 'mycommand
{variable1} : First variable
{variable2} : Another variable';
public function handle()
{
// Set any required headers. I'm spoofing an AJAX request:
request()->headers->set('X-Requested-With', 'XMLHttpRequest');
// Set your query data for the route:
request()->merge([
'variable1' => $this->argument('variable1'),
'variable2' => $this->argument('variable2'),
]);
// Instantiate your controller and its dependencies:
$response = (new MyController)->put(new MyModel, new MyOtherModel);
// Do whatever you want with the response:
var_dump($response->getStatusCode()); // 200, 404, etc.
var_dump($response->getContent()); // Entire response body
// See what other fun stuff you can do!:
var_dump(get_class_methods($response));
}
}
Your Controller/Route will work exactly as if you had called it using curl. Have fun!
I trying to pass some json to a controller in cakePHP 2.5 and returning it again just to make sure it is all going through fine.
However I getting no response content back. Just a 200 success. From reading the docs I am under the impression that if I pass some json then the responseHandler will the return json as the response.
Not sure what I am missing.
Data being passed
var neworderSer = $(this).sortable("serialize");
which gives
item[]=4&item[]=3&item[]=6&item[]=5&item[]=7
appController.php
public $components = array(
'DebugKit.Toolbar',
'Search.Prg',
'Session',
'Auth',
'Session',
'RequestHandler'
);
index.ctp
$.ajax({
url: "/btstadmin/pages/reorder",
type: "post",
dataType:"json",
data: neworderSer,
success: function(feedback) {
notify('Reordered pages');
},
error: function(e) {
notify('Reordered pages failed', {
status: 'error'
});
}
});
PagesController.php
public function reorder() {
$this->request->onlyAllow('ajax');
$data = $this->request->data;
$this->autoRender = false;
$this->set('_serialize', 'data');
}
UPDATE:
I have now added the following to the routes.php
Router::parseExtensions('json', 'xml');
and I have updated my controller to
$data = $this->request->data;
$this->set("status", "OK");
$this->set("message", "You are good");
$this->set("content", $data);
$this->set("_serialize", array("status", "message", "content"));
All now works perfectly.
A proper Accept header or an extension should to be supplied
In order for the request handler to be able to pick the correct view, you need to either send the appropriate Accept header (application/json), or supply an extension, in your case .json. And in order for extensions to be recognized at all, extension parsing needs to be enabled.
See http://book.cakephp.org/...views.html#enabling-data-views-in-your-application
The view only serializes view vars
The JSON view only auto-serializes view variables, and from the code you are showing it doesn't look like you'd ever set a view variable named data.
See http://book.cakephp.org/...views.html#using-data-views-with-the-serialize-key
The view needs to be rendered
You shouldn't disable auto rendering unless you have a good reason, and in your case also finally invoke Controller:render() manually. Currently your action will not even try to render anything at all.
CakeRequest::onlyAllow() is for HTTP methods
CakeRequest::onlyAllow() (which btw is deprecated as of CakePHP 2.5) is for specifying the allowed HTTP methods, ie GET, POST, PUT, etc. While using any of the available detectors like for example ajax will work, you probably shouldn't rely on it.
Long story short
Your reorder() method should look more like this:
public function reorder() {
if(!$this->request->is('ajax')) {
throw new BadRequestException();
}
$this->set('data', $this->request->data);
$this->set('_serialize', 'data');
}
And finally, in case you don't want/can't use the Accept header, you need to append the .json extension to the URL of the AJAX request:
url: "/btstadmin/pages/reorder.json"
and consequently enable extension parsing in your routes.php like:
Router::parseExtensions('json');
ps
See Cakephp REST API remove the necessity of .format for ways to use the JSON view without using extensions.
Output your json data
public function reorder() {
$this->request->onlyAllow('ajax');
$data = $this->request->data;
$this->autoRender = false;
$this->set('_serialize', 'data');
echo json_encode($data);
}