405 (Method Not Allowed) - Laravel and Vue.Js - laravel

Good afternoon everyone, I'm trying to implement a notification system with the possibility to mark this notification with a like.
I am using laravel 7 for the back end and vuejs for the front end.
The code works correctly on localhost, but when I deploy to Heroku it stops working and to give me the message below.
http://springalert.herokuapp.com/api/like 405 (Method Not Allowed)
Uncaught (in promise) Error: Request failed with status code 405
at createError (app.js:5347)
at settle (app.js:5608)
at XMLHttpRequest.handleLoad (app.js:4816)
Someone with any tips for the subject, I researched about it and I know that we have to configure CORS but for this version of laravel it supposedly would no longer be necessary.
follow the code, thank you for your help.
ROUTE
Route::post('/api/like/', 'NotificationController#api_like');
CONTROLLER
public function api_like(Request $request) {
$like = new Like;
$like->notification_id = $request->id;
$like->user_id = auth()->id();
$like->save();
}
VUEJS
<b-card-text class="text-right" v-if="Object.keys(notification.like).length == 0">
<a #click="makelike('success', 'Informação', notification.id)" class="a"><i class="fas fa thumbs-up"></i></a>
</b-card-text>
makelike(variant = null, title, notification_id) {
this.id = notification_id
axios.post('api/like/',{ id:this.id })
.then((response) => {
this.set_notifications()
this.$bvToast.toast('Obrigado pela tua visualização', {
title: title,
variant: variant,
solid: true
})
})
},

As user Mihai pointed out, you need to chain your POST request after a get request to csrf.
Here's how it looks like with Laravel Sanctum:
import axios from 'axios'
const baseUrl = 'http://yourdomain.com'
const api = axios.create({
baseURL: baseUrl,
withCredentials: true
})
const payload = {}
api().get('/sanctum/csrf-cookie').then(() => {
return api().post('api/like', payload).then(resp => {
// Do stuff with resp
})
})

Related

419|PAGE EXPIRED in production, CSRF problem with Laravel 9 and Inertia?

After deployment, when I try to use a delete route, this error is thrown:
419 | PAGE EXPIRED
Failed to load resource: the server responded with a status of 419 ()
Function inside vue component from where the route is triggered:
const destroyEvent = (record) => {
if (confirm('Delete event?')) {
Inertia.delete(route('admin.event.destroy', {
id: record.id,
title: record.title,
image_path: record.image_path,
}), { preserveScroll: true })
}
}
Web route:
Route::delete('/admin/event/destroy', [EventsController::class, 'destroyEvent'])->name('admin.event.destroy');
The Request does not seem to reach EventsController, which I've tested with dd().
According to this stack question, it might be an CSRF token problem. I've tried everything proposed in there but it didn't help. Though commenting out:
\App\Http\Middleware\VerifyCsrfToken::class
from app\Kernel.php removes the 419 | PAGE EXPIRED screen and instead shows a blank page. Is this an indication of a CSRF problem?
From this laracasts question, I've also tried adding:
public function boot()
{
if($this->app->environment('production') || $this->app->environment('staging'))
{
\URL::forceScheme('https');
}
}
to AppServiceProvider.php with no improvement of the problem.
Any idea how to fix this?
I found a workaround by using form.delete instead of Inertia.delete:
const destroyEvent = (record) => {
if (confirm('Delete Event?')) {
const form = useForm({
id: record.id,
title: record.title,
image_path: record.image_path
})
form.delete(route('admin.event.destroy'), { preserveScroll: true })
}
}
It would be useful to know how the form helper handles CSRF different from Inertia.

Where to add axios interceptors code in vue js

I am using vue js as frontend with laravel. I am using laravel passport for auth, now i wants to show some error message once received 401 unauthentic error message that mostly occurs when my token expired. so to do this i am using axios interceptors my code is like
axios.interceptors.response.use(function (response) {
return response
}, function (error) {
// const { config, response: { status } } = error
const { config, response } = error
const originalRequest = config
if (response && response.status === 401) {
//notication or redirection
this.$vs.notify({
title: 'Error',
text: response.data['message'],
iconPack: 'feather',
icon: 'icon-check-circle',
color: 'danger'
})
}
return Promise.reject(error)
})
Now Question is that where i put this code in vue js so that it call after every request & so an error message shown & redirect to login once get 401 unauthorized..
Any Suggestion from anyone.
Thanks in advance!!
app.js or add them to separate file and include in app.js. There is post about this https://medium.com/#yaob/how-to-globally-use-axios-instance-and-interceptors-e28f351bb794

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 get each http body updates on angular Http request?

I'm using an express api (my back-end) and an angular app (my front-end).
One express js end point (let's call it '/foo') is processing a lot of files,
i send data using res.write() after each treatment so the http response body is update.
I would like to get this update on my angular app.
I was using ajax in a previous version and it worked fine with ajax call :
xhrFields: {
// Getting on progress streaming response
onprogress: function(e)
{
var progressResponse;
var response = e.currentTarget.response;
if(lastResponseLength === false)
{
progressResponse = response;
lastResponseLength = response.length;
}
else
{
progressResponse = response.substring(lastResponseLength);
lastResponseLength = response.length;
}
actualResponse += progressResponse
}
Unfortunatly i found nothing to get partial http body. I tried to use 'reportProgress' Parameter but it's not working.
For some more context my front-end angular code:
service.ts :
setHolidaysDirectory(holidaysKey: string, path: string): Observable<Object>{
const setHolidayDirectoryStreamHttpRequest =
new HttpRequest('POST', 'http://localhost:8089/holidays/pictures/edit', { 'key': holidaysKey,
'path': path
}, {headers: this._httpHeaders, reportProgress: true, responseType: 'text'});
// pipe stream answer
return this._http.request(setHolidayDirectoryStreamHttpRequest);
}
and my component just call the service and subscribe :
this._holidaysService
.setHolidaysDirectory(key, finalHolidaysForm.path)
.subscribe((stream) => {
console.log('new answer');
console.log(stream);
}, error => console.log(error));
But unfortunatly i got empty answer and all the http body is recovered after res.end() (server side)
Can anyone help pls !
Thank a lot !

Resources