Why does promise go into the .then() when the response is an error? - Laravel - laravel

I am making a SPA in Laravel with vue.js and vuex and vue router. I made an action in my store for logging, but when I log in with invalid credentials something really strange happens. It works fine when I log in with the right credentials.
So it debugs the following
login
app.js:279 POST http://127.0.0.1:8000/api/auth/login 401
(Unauthorized)
app.js:58018 login succes
app.js:58023 login failed
app.js:43201 Uncaught (in promise) NavigationDuplicated {_name:
"NavigationDuplicated", name: "NavigationDuplicated", message:
"Navigating to current location ("/") is not allowed", stack: "Error↵
at new NavigationDuplicated (http://127.…)↵ at
http://127.0.0.1:8000/js/app.js:57159:12"} message: "Navigating to
current location ("/") is not allowed" name: "NavigationDuplicated"
_name: "NavigationDuplicated" stack: "Error↵ at new NavigationDuplicated (http://127.0.0.1:8000/js/app.js:43124:14)↵ at
HTML5History.confirmTransition
(http://127.0.0.1:8000/js/app.js:43240:18)↵ at
HTML5History.transitionTo (http://127.0.0.1:8000/js/app.js:43184:8)↵
at HTML5History.push (http://127.0.0.1:8000/js/app.js:43515:10)↵ at
http://127.0.0.1:8000/js/app.js:43929:22↵ at new Promise
()↵ at VueRouter.push
(http://127.0.0.1:8000/js/app.js:43928:12)↵ at
http://127.0.0.1:8000/js/app.js:57159:12"
proto: Error
The thing that is crazy to me is that the promise goes into the .then() and console.logs "login succes"? It shouldn't ever get in the .then() right? Because the credentials are wrong, so it should just go for the .catch(). But what is even more strange is that it does nog debug the second console.log(response.data) in the .then()???? Also I do not understand the Navigation Duplicated.
Credentials is just an {username, password}. I am using JWT and the /login route leads to the standard jwt authcontroller login method.
Login.vue component method
methods: {
login() {
this.$store
.dispatch("tryLogin", this.form)
.then(response => {
this.$router.push({ path: "/home" });
})
.catch(error => {
this.logginError = error;
});
}
}
Store action
tryLogin(context, credentials) {
context.commit("login");
console.log("login");
return new Promise((resolve, reject) => {
axios
.post("/api/auth/login", credentials)
.then(response => {
console.log("login succes");
console.log(response.data);
context.commit("loginSucces", response.data);
resolve(response.data);
})
.catch(error => {
console.log("login failed");
context.commit("loginFailed", error);
reject(error);
});
});
}
AuthController login function
/**
* Get a JWT via given credentials.
*
* #return \Illuminate\Http\JsonResponse
*/
public function login()
{
$credentials = request(['email', 'password']);
if (!$token = auth('api')->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}

Ok, I will try to explain (sorry for my english) The following is working fine, when you login with wrong credential it will return a nice JSON response:
if (!$token = auth('api')->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
That is is not a failure, for that reason it enters to .then and print 401 (Unauthorized)
then it print console.log("login succes") and then when it try to call the context.commit("loginSucces", response.data); it fails and will go to the catch and say app.js:58023 login failed.
You can fix this just asking in the .then
axios
.post("/api/auth/login", credentials)
.then(response => {
if (response.data.status === "error") {
console.log(response.data);
}
else{
context.commit("loginSucces", response.data);
resolve(response.data);
}
})

Related

How to access laravel API with VUE JS?

so i want to use mylogin api but its not working,it keep push the route to dashboard even the email and the password incorrect
here is my code
export default {
data(){
return{
form: {
email: null,
password: null
},
user: {},
error: false
}
},
methods: {
login() {
this.user.append("email", this.form.email);
this.user.append("password", this.form.password);
this.axios.post('http://127.0.0.1:8000/api/login', this.user).then(response => {
localStorage.setItem("Name", response.data.first_name);
this.$router.push('/userDashboard/Dashboard')
});
},
register() {
this.$router.push('/RegisterPage')
}
},}
my laravel route api
Route::post('/login', 'UserController#login');
Login function
public function login(Request $request, User $user)
{
$email = $request->input('email');
$password = $request->input('password');
$user = User::where('email', '=', $email)->first();
if (!$user) {
return response()->json(['success'=>false, 'message' => 'Login Fail, please check email']);
}
if (!Hash::check($password, $user->password)) {
return response()->json(['success'=>false, 'message' => 'Login Fail, pls check password']);
}
return response()->json(['success'=>true,'message'=>'success', 'data' => $user]);
}
sorry for my english
This is because your laravel app always return 200 HTTP responses and this causes the .then( ... ) in the frontend to always be executed.
Either in the .then( ... ) your check the success value on the response that your Laravel has set, like this:
this.user.append("email", this.form.email);
this.user.append("password", this.form.password);
this.axios.post('http://127.0.0.1:8000/api/login', this.user).then(response => {
if (response.data.success === false) {
// handle the error and stop the code with a return
this.handleError();
return;
}
localStorage.setItem("Name", response.data.first_name);
this.$router.push('/userDashboard/Dashboard')
});
OR, you can also in Laravel throw a 401 or 400 response to say the login failed which will throw an exeception in the frontend, that you can catch with .then( ... ).catch( ... ).
That is the most clean way, because no need to send 'success' => true true anymore, since the HTTP code will be the source of truth with what happend.
public function login(Request $request, User $user)
{
$email = $request->input('email');
$password = $request->input('password');
$user = User::where('email', '=', $email)->first();
if (!$user || !Hash::check($password, $user->password)) {
// never tell the person if it's email or password, always say it's one of both for security reasons
return response(401)->json(['message' => 'Login Fail, please check email or password']);
}
return response()->json(['data' => $user]);
}
Last thing, I don't understand how this.user.append("email", this.form.email); works, because this.user seems to just be a simple object, so there isn't any append method on it.
So unless I'm missing something here, the best thing should just be to do:
const user = {
email: this.form.email,
password: this.form.password
}
// OR, make a copy
const user = { ...this.form }
// then send the user var to axios
this.axios.post('your url', user).then( ... )

How to check request is axios in laravel

export const category_load = ()=>{
return async (dispatch) => {
await fetch('https://example.com/api/user/categorylist')
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
})
.catch((error) => {
console.error(error);
});
}
}
I sent the axios request to the server.
Here is the server code.
...
if($request->ajax()){
return $Categories;
}
return view(Route::currentRouteName(), compact('Categories'));
If I sent ajax it returns $Categories but I sent axios it returns view.
I have to check axios request like ajax and return $Categories.
How to solve this?
The above answer will work, but it's insecure what if someone gets to know why are you using the additional param and they use it on the browser direct?
Instead, pass header which by default Laravel will read to check if the incoming request is ajax or not.
axios.get(url, {
body:{},
headers: {
"X-Requested-With": "XMLHttpRequest",
},
})
Now you can simply do $request->ajax() in the controller and it will work.
API endpoints are not meant to be used to return views. :) Use non API endpoints to return views.
BTW, with your current implementation you can do this way:
Send an extra parameter, for example, api=true with your Axios request endpoint, as:
export const category_load = ()=>{
return async (dispatch) => {
await fetch('https://example.com/api/user/categorylist?api=true')
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
})
.catch((error) => {
console.error(error);
});
}
Now, in your controller, you can check if the api parameter is set or not. If it's set you can just return the categories like you're doing for the ajax request, as:
// return $Categories if the request is from Axios or Ajax
if($request->ajax() || $request->api == true) {
return $Categories;
}
return view(Route::currentRouteName(), compact('Categories'));

React-Native fetch post to lumen/laravel returns MethodNotAllowed(405) but postman works

I know this questions have been asked before but non of the answers worked for me.
I am working with React Native and sending API's to Lumen-Backend and i realised that all POST request to LUMEN returns 405 error. Tested it with Postman and it works very fine.
Tried using fetch and axios but they all return 405 errors. Find codes Bellow
Postman request working image
FETCH CALL
const BASE_URL = 'http://192.168.43.232/2019/betbank_api/public/api/';
const url = '/app/auth/login/'
const endPoint = BASE_URL.concat(url);
const data ={ email: 'okeke', password:'passs' }
async function postData(url = '', data = {}) {
const response = await fetch(url, {
method: 'POST',
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
headers: {
'Content-Type': 'application/text'
},
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
return await response.text(); // parses JSON response into native JavaScript objects
}
postData(endPoint, { email: 'okeke', password:'passs' })
.then((data) => {
console.log(data); // JSON data parsed by `response.json()` call
alert(data)
});
Also tried implementing the same thing using AXIOS
but ir returns same 405 error. Find Axios code bellow
axios.post(endPoint, data, {
headers: {
'Accept': 'application/json;charset=utf-8',
'Content-Type': 'application/json;charset=utf-8',
}
}).then( (response)=>{
console.log(JSON.stringify(response.data))
alert(JSON.stringify(response.data))
}
).catch( (error)=>{
console.log(error)
alert(error)
})
Find the Lumen Route - API bellow
$router->group(['prefix' => 'api'], function () use ($router) {
$router->post('/app/auth/login', 'AppUserController#postLogin');
});
FInd the method postLogin Bellow
class AppUserController extends Controller
{
protected $jwt;
public function __construct(JWTAuth $jwt)
{
$this->jwt = $jwt;
}
public function postLogin(Request $request)
{
$email = $request->input('email');
$this->validate($request, [
'email' => 'required|email|max:255',
'password' => 'required',
]);
try {
if (! $token = $this->jwt->attempt($request->only('email', 'password'))) {
return response()->json(['status'=>'error','data'=> 'Invalid username and passowrd'], 401);
}
} catch (\Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
return response()->json(['token_expired'], 500);
} catch (\Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
return response()->json(['token_invalid'], 500);
} catch (\Tymon\JWTAuth\Exceptions\JWTException $e) {
return response()->json(['token_absent' => $e->getMessage()], 500);
}
return response()->json(compact('token'));
}
}
Everythings seems in order but somehow, neither fetch or axios would work when i use the POST method.
But if i change it to GET method, the error stops but the issue would now be how to get the data's been posted from the APP.
QUESTION
Why would all Post request from my App (React Native) be returning 405 from Lumen/Laravel

On get request why do I get back the blade view, when I should get data from the database instead?

I a have the following get request, which is executed on mounted().
In some weird mysterious ways, I get back my main view app.blade as a response when I am clearly requesting some data from the database.
Can someone spot what I messed up?
My get request on the front-end:
mounted() {
this.getProjectRequests();
},
methods: {
getProjectRequests: function() {
var self = this;
let clientId = this.$route.path.substring(
this.$route.path.lastIndexOf("/") + 1
);
axios({
method: "get",
url: "/get-project-requests/" + clientId
})
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
// TODO error handling
});
}
}
My route:
Route::get('/get-project-requests/{client_id}',
'SinglePageController#getProjectRequests');
And my controller method:
public function getProjectRequests($clientId) {
try {
$projectRequests = ProjectRequest::where('client_id',
$clientId)->value('name');
return response()->json( [
'success'=> true,
'projectRequests' => $projectRequests
]);
} catch(\Exception $e){
return ['success' => false, 'message' => 'getting
project requests failed'];
}
}
I think this ProjectRequest::where('client_id', $clientId)->value('name'); giving exception.
Either you check your laravel.log inside storage/logs folder or change that method into
// Not working on eloquent model
$valueOject = ProjectRequest::where('client_id',$clientId)->value('name');
// DB facade its working. Change to this method
$valueOject = DB::table('{your_table}')->where('client_id', $clientId)->value('name');
dd($valueOject);

Laravel-Vue: How can I use try-catch block to catch validation exception in Validation Requests?

I'm using vue.js. I'm hitting my server with axios like:
try{
const resp = await axios.post('storeProduct', data,
{
headers : header(state)
});
console.log(resp);
}catch(error){
console.log("you are at error");
console.log(error);
}
Here, I'm console logging the error where I get error 422 but I want to get the message as well. If I use try catch in a simple validation it works. But cant make it working with the Validation Request object.
In my Controller: 'ProductRequest' is the validation object which has validation rules. It gives me the errors but can't catch in the try-block of axios in vue.
public function storeProduct(ProductRequest $request){
try{
return $controller->saveProducts($request);
}catch(\Exception $e){
return $e;
}
}
ProductRequest.php
public function rules()
{
try{
return validation_value('add_products');
}catch(\Exception $e){
return $e;
}
}
Is there anyway that I could return the error message from here and catch it in my 'vue axios try/catch block'
Try it like this :
axios.post('storeProduct', data,
{
headers : header(state)
})
.then(response => console.log(response.data))
.catch(error => {
if (error.response) {
console.log("you are at error");
console.log(error.response);
}
});

Resources