Sending Files From Vuex to Backend using Axios - laravel

I have a vuex which stores few informations and also the files uploaded by the users. Now I am trying to send all the vuex data to backend using axios to process it but for some reason the file array is empty.
But when I see in VueDevTools, there is a file object in the vuex state.
This is my axios call, it is just not images which I am trying to send.
axios.post('/admin/form/saveForm',{
data : this.$store.getters.getEditCollector,
formData: this.$store.getters.getCollector,
formName : this.formName,
task: this.task,
id: this.formEntryId,
formId: this.formId,
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(response => {})
.catch(error => {});
My Vuex is below snapshot.
As we can see, the logo state has a File Object, but when the request is received in the backend( PHP- Laravel), It is an empty array.
What is wrong here? Can someone point out the mistake?
EDIT1: Added Network Payload
EDIT2: Added Backend Code - Laravel
public function saveForm(Request $request){
// form data here has logo but empty array <------
$formData = $request->input('data');
$wholeFormData = $request->input('formData');
$this->formID = $request->input('formId');
....
...
}

Related

Can't upload image to a external API using axios

I've been trying to upload an image with axios to an external (laravel) api and it's been giving me nightmares.
resumed template:
<v-form>
<v-file-input
label="Logo*"
v-model="image"
accept="image/*"
#change="onFileSelected"
required
></v-file-input>
<v-btn color="blue darken-1" text #click="createProvider">Create Provider</v-btn>
</v-form>
Methods
methods: {
onFileSelected (event) {
this.selectedImage = event;
},
createProvider() {
let formData = new FormData();
formData.append("image", this.selectedImage, this.selectedImage.name);
const config = {
headers: {
Authorization: this.token,
'content-type': 'multipart/form-data'
}
};
let imageData = {
'image': formData,
'name': 'Provider Image', // Required Field
}
axios.post('http://fake_external_url.com/api/images', imageData, config) // laravel API
.then(console.log)
.catch(console.log)
},
}
The error that I get in return is:
Error: Request failed with status code 422
Request Response:
[HTTP/1.1 422 Unprocessable Entity 1374ms]
{"image":{},"name":"Image Provider"}
I see that the image is not receiving anything.
If I console.log this.selectedImage I get:
File {
name: "happy.jpg",
lastModified: 1596711013544,
webkitRelativePath: "",
size: 41292,
type: "image/jpeg"
}
If I console.log FormData I get crap
FormData
​<prototype>: FormDataPrototype
​​append: function append()
​​constructor: function ()
​​delete: function delete()
​​entries: function entries()
​​forEach: function forEach()
​​get: function get()
​​getAll: function getAll()
​​has: function has()
​​keys: function keys()
​​set: function set()
​​values: function values()
​​Symbol(Symbol.toStringTag): "FormData"
​​Symbol(Symbol.iterator): function entries()
​​<prototype>: Object { … }
My environment: localhost on a XAMPP server (php artisan serve as well). Laravel, VueJS, Vuetify latest versions.
I think my problem likes in my FormData, but it may be from the variables that it's receiving from the event click. I am out of ideas.
[EDIT] Note: I am able to upload image when using POSTMAN.
The reason why I am using event, and not the classic event.target.files[0] it's because there is no target in the response from the console.log.
You have two problems.
FormData
You need to send a FormData object, only a FormData object, and nothing but a FormData object.
If you want to pass additional data, then append it to the FormData object.
Wrapping the FormData object in another object and passing it to (for example) a JSON serializer will just break it.
Content-Type
The multipart/form-data MIME type has a mandatory boundary parameter.
You have omitted it, but you can't know what it is anyway.
Do not set the Content-Type header manually. The underlying XHR object will read it from the FormData object.

React Native fetch getting an Empty array when making POST request to Laravel Api

I am working on a project for my self learning that has laravel in the backend and running react native in the front end. I have implemented the login and register screen for my apps. Now I am trying to connect it to my laravel server through its api routes. I was first having CORS issue so I solved it by making a new middleware and editing the kernel.php file as stated in this thread.
CORS Issue with React app and Laravel API
Now I tried to run some tests first with get request my submit function in react is
handleSubmit = async() => {
const email = this.state.email
const pass = this.state.password
const proxyurl = "https://cors-anywhere.herokuapp.com/";
/*TEST FOR GET REQUEST */
let response = await fetch(`http://mobileprediction/api/user?email=${email}&pass=${pass}`, {
headers: {
"Content-Type" : "application/json",
"Accept" : "application/json"
},
})
let result = await response.json()
console.log(result)
}
and my api.php file in the routes of laravel was
Route::get("/user", function (Request $request) {
return $request;
});
and it gave the desired output, but then when I tested the same way with a post request I am getting an empty array no matter what and I am unable to figure out what the problem is
the handlesubmit function in my react native app is
handleSubmit = async() => {
const email = this.state.email
const pass = this.state.password
/*TEST FOR POST REQUEST */
let response = await fetch(`http://mobileprediction/api/user`, {
method: "POST",
header : {
"Content-Type" : "application/json",
"Accept" : "application/json"
},
body : JSON.stringify({
emailid : email,
password : pass
}),
})
let result = await response.json()
console.log(result)
}
and api.php file in laravel is
Route::get("/user", function (Request $request) {
return $request;
});
Route::post("/user", function(Request $request) {
return $request;
});
I think you write your routes in web.php, for your API could write the endpoints in api.php.
Try to comment VerifyCsrfToken middleware in app/Http/Kenrel.php.
it has security issue, but you can do it in your learning steps.
[ https://laravel.com/docs/6.x/routing ] [search CSRF in this link]
Any routes pointing to POST, PUT, or DELETE routes that are defined in the web routes file should include a CSRF token field.
So what I understood is that from the fetch request in react native its not sending just the inputs but rather a page with a body that has json formatted key values. So I cant access data in my server as
$request->param
or with
request("param")
you could get the json formatted string with
$request->json()->all()
but still I couldnt get to the individual values
for me what was working is to get all the contents and then access the values with
$postInput = file_get_contents('php://input');
$data = json_decode($postInput, true);
return ["email" => $data["emailid"] ];

Vuejs Laravel Passport - what should I do if access token is expired?

I am using Vuejs SPA with Laravel API as backend. I successfully got the personal access token and store in localStorage and Vuex state like below.
token: localStorage.getItem('token') || '',
expiresAt: localStorage.getItem('expiresAt') || '',
I use the access token every time I send axios request to laravel api. Every thing works well. However, initially the token was set to 1 year expiration so when I develop I didn't care about token being expired and today suddenly I thought what is going to happen if token expired. So I set token expiry to 10 seconds in laravel AuthServiceProvier.php.
Passport::personalAccessTokensExpireIn(Carbon::now()->addSecond(10));
and then I logged in and after 10 seconds, every requests stopped working because the token was expired and got 401 unauthorised error.
In this case, how can I know if the token is expired? I would like to redirect the user to login page if token is expired when the user is using the website.
Be as user friendly as possible. Rather than waiting until the token expires, receiving a 401 error response, and then redirecting, set up a token verification check on the mounted hook of your main SPA instance and have it make a ajax call to e.g. /validatePersonalToken on the server, then do something like this in your routes or controller.
Route::get('/validatePersonalToken', function () {
return ['message' => 'is valid'];
})->middleware('auth:api');
This should return "error": "Unauthenticated" if the token is not valid. This way the user will be directed to authenticate before continuing to use the app and submitting data and then potentially losing work (like submitting a form) which is not very user friendly.
You could potentially do this on a component by component basis rather than the main instance by using a Vue Mixin. This would work better for very short lived tokens that might expire while the app is being used. Put the check in the mounted() hook of the mixin and then use that mixin in any component that makes api calls so that the check is run when that component is mounted. https://v2.vuejs.org/v2/guide/mixins.html
This is what I do. Axios will throw error if the response code is 4xx or 5xx, and then I add an if to check if response status is 401, then redirect to login page.
export default {
methods: {
loadData () {
axios
.request({
method: 'get',
url: 'https://mysite/api/route',
})
.then(response => {
// assign response.data to a variable
})
.catch(error => {
if (error.response.status === 401) {
this.$router.replace({name: 'login'})
}
})
}
}
}
But if you do it like this, you have to copy paste the catch on all axios call inside your programs.
The way I did it is to put the code above to a javascript files api.js, import the class to main.js, and assign it to Vue.prototype.$api
import api from './api'
Object.defineProperty(Vue.prototype, '$api', { value: api })
So that in my component, I just call the axios like this.
this.$api.GET(url, params)
.then(response => {
// do something
})
The error is handled on api.js.
This is my full api.js
import Vue from 'vue'
import axios from 'axios'
import router from '#/router'
let config = {
baseURL : process.env.VUE_APP_BASE_API,
timeout : 30000,
headers : {
Accept : 'application/json',
'Content-Type' : 'application/json',
},
}
const GET = (url, params) => REQUEST({ method: 'get', url, params })
const POST = (url, data) => REQUEST({ method: 'post', url, data })
const PUT = (url, data) => REQUEST({ method: 'put', url, data })
const PATCH = (url, data) => REQUEST({ method: 'patch', url, data })
const DELETE = url => REQUEST({ method: 'delete', url })
const REQUEST = conf => {
conf = { ...conf, ...config }
conf = setAccessTokenHeader(conf)
return new Promise((resolve, reject) => {
axios
.request(conf)
.then(response => {
resolve(response.data)
})
.catch(error => {
outputError(error)
reject(error)
})
})
}
function setAccessTokenHeader (config) {
const access_token = Vue.cookie.get('access_token')
if (access_token) {
config.headers.Authorization = 'Bearer ' + access_token
}
return config
}
/* https://github.com/axios/axios#handling-errors */
function outputError (error) {
if (error.response) {
/**
* The request was made and the server responded with a
* status code that falls out of the range of 2xx
*/
if (error.response.status === 401) {
router.replace({ name: 'login' })
return
}
else {
/* other response status such as 403, 404, 422, etc */
}
}
else if (error.request) {
/**
* The request was made but no response was received
* `error.request` is an instance of XMLHttpRequest in the browser
* and an instance of http.ClientRequest in node.js
*/
}
else {
/* Something happened in setting up the request that triggered an Error */
}
}
export default {
GET,
POST,
DELETE,
PUT,
PATCH,
REQUEST,
}
You could use an interceptor with axios. Catch the 401s and clear the local storage when you do then redirect user to appropriate page.

How to use rxjs ajax operator instead of axios in my react project?

I am new to rxjs and want to know how to handle this use case.
This is axios promise, how to convert it so that it uses rxjs's ajax operator
export const onLogin = ({ username, password }) =>
axios({
method: "post",
url: O_TOKEN_URL,
data: querystring.stringify({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: "password",
username,
password
}),
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
});
This is my action,
export const onSubmit = payload => ({
type: FETCH_USER,
payload // payload contains username & password
});
This is my epic for now,
export const loginEpic = action$ =>
action$
.ofType(FETCH_USER)
// somehow import onLogin from above and resolve it
// then, dispatch FETCH_USER_FULFILLED
.do(
payload => console.log(payload.username, payload.password)
// i am able to console these username and password
)
.mapTo(() => null);
I want to resolve onLogin function somehow, when FETCH_USER is dispatched, using rxjs's ajax operator.
And, I want onLogin function, which returns promise/observable, to be set up in different file so that I can keep track of all the ajax requests
These are the packages,
"redux-observable": "^0.18.0",
"rxjs": "^5.5.10",
Could you also point me to a documentation that covers this and various use case for post, put ... requests? I couldn't find any.
The ajax config object is fairly similar to what you already have. I'm assuming the data property for the axios request is the request body.
import {ajax} from 'rxjs/observable/dom/ajax';
export const onLogin = ({ username, password }) =>
ajax({
method: "POST",
url: O_TOKEN_URL,
body: JSON.stringify({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: "password",
username,
password
}),
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
});
Your epic would look something like this:
export const loginEpic = action$ =>
action$
.ofType(FETCH_USER)
.mergeMap(action =>
onLogin(action.payload)
// map will be called when the request succeeded
// so we dispatch the success action here.
.map((ajaxResponse) => fetchUserFulfilled())
// catch will be called if the request failed
// so we dispatch the error action here.
// Note that you must return an observable from this function.
// For more advanced cases, you can also apply retry logic here.
.catch((ajaxError, source$) => Observable.of(fetchUserFailed()))
);
Where fetchUserFulfilled and fetchUserFailed are action creator functions.
There does not seem to be much documentation of the RxJS 5 ajax method yet. Here are the links to the official v5 docs for the AjaxRequest, AjaxResponse and AjaxError. The AjaxError object in particular has 0 information so far (at the time of this answer) so you will need to rely on the source code if you need to use this object for more than a trigger to tell the user that something went wrong. The ajax source code is here.

Can't access local json file using axios

I'm having an issue with loading json data locally. My code worked fine when I loaded data from https://randomuser.me/api, but when I specify location of my json file locally it just returns plain HTML. Definitely doing something wrong, but can't figure it out. I use axios for ajax calls. How do I make the request correctly? My code:
export function fetchUsers () {
return {
type: 'FETCH_USER',
payload: axios.get('./users.json').then( (response) =>
console.log(response.data) )
};
}

Resources