js:
$.ajax({
url: '/site/updateuserdata',
method: 'POST',
//async: true,
//cache:false,
data: {
'type': 'sort'
//val: val
//csrfParam: csrfToken
},
dataType: 'text',
error: function(jqXHR, textStatus, errorThrown) {
alert('error by ajax');
},
success: function(data, status, jqXHR) {
alert('success by ajax');
}
});
Controller:
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'actions' => ['updateuserdata'],
'allow' => true,
// 'roles' => ['*'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'updateuserdata' => ['post'],
],
],
];
}
public function actionUpdateuserdata()
{
/*if (Yii::$app->request->isAjax) {
$message = 'Ваше сообщение успешно отправлено';
Yii::$app->response->format = Response::FORMAT_JSON;
$response = [
'success' => true,
'message' => $message
];
return $response;
}*/
$f = fopen('testajax.txt','a+');
fwrite($f, 'ajax: '.(isset($_POST['type'])?$_POST['type']:'error'));
fclose($f);
if(isset($_POST['type']))
return $_POST['type'];
else return 'error1';
// return Yii::$app->getResponse()->redirect(Yii::$app->request->referrer, 302, FALSE);
}
yii.js:
function initRedirectHandler() {
// handle AJAX redirection
$(document).ajaxComplete(function (event, xhr) {
var url = xhr && xhr.getResponseHeader('X-Redirect');
alert(url); //my code
if (url) {
window.location.assign(url);
}
});
}
I see first alert(ajax error) "error by ajax" and then alert(yii.js) "..../site/updateuserdata...", why ajax error? File testajax.txt not create.
I tried comment 'updateuserdata' => ['post'], and get error too.
Updated.
Also, tried:
public function beforeAction($action)
{
if ($action->id == 'updateuserdata') {
$this->enableCsrfValidation = false;
}
return parent::beforeAction($action);
}
and uncomment csrf parameters in ajax.
And 'error' return status 302(jqXHR.status).
dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
I find problem, it's stupid problem:
url: '/mag/updateuserdata/',
i have in 'urlManager':
'suffix' => '/',
i think this added for all, but not.....
Cause you commented
//csrfParam: csrfToken
from your ajax js file you have 400 http status code in your ajax.
So you can solve it in two way:
one: disable csrf validation by adding
$this->enableCsrfValidation = false;
in your actionUpdateuserdata method.
two: add csrf token and csrf param to your ajax.
print this code in your js, where you commented it.
Yii::$app->request->csrfParam . ':' . Yii::$app->request->csrfToken
then your request sent successfully and if you have other error you must check your code.
Related
I'm starting to work with Codeigniter 4 Shield.
I added this piece of code to my app/Config/Routes.php file.
$routes->get('/access/token', static function() {
$token = auth()->user()->generateAccessToken(service('request')->getVar('token_name'));
return json_encode(['token' => $token->raw_token]);
});
When I try to access the route in my web browser using the URL https://example.com/access/token, I obtain the error:
Call to a member function generateAccessToken() on null
produced by the line of code below:
$token = auth()->user()->generateAccessToken(service('request')->getVar('token_name'));
Background information:
I have installed Codeigniter 4 Shield using Composer, ran the respective database migrations, and everything else works fine.
My Codeigniter 4 Shield 'login' and 'register' pages work fine.
How can I load generateAccessToken() automatically in the app/Config/Routes.php file?
You need to submit the login credentials (email & password) along with your HTTP POST request to help identify the User requesting the access token. Otherwise, auth()->user() is empty, hence the error.
To generate an access token, you need to first authenticate the User.
For example: (Using email & password)
Define your 'access token' route. Notice the use of ->post(...) instead of ->get(...).
File: app/Config/Routes.php
$routes->post('auth/token', '\App\Controllers\Auth\LoginController::accessToken');
Define your Controller method that will handle the 'access token' generation. Read: Issuing the Tokens
File: app/Controllers/Auth/LoginController.php
<?php
namespace App\Controllers\Auth;
use App\Controllers\BaseController;
class LoginController extends BaseController
{
public function accessToken()
{
// Validate credentials
$rules = [
'email' => [
'label' => 'Auth.email',
'rules' => config('AuthSession')->emailValidationRules,
],
'password' => [
'label' => 'Auth.password',
'rules' => 'required',
],
];
if (!$this->validate($rules)) {
return $this->response
->setJSON(['errors' => $this->validator->getErrors()])
->setStatusCode(422);
}
if (auth()->loggedIn()) {
auth()->logout();
}
// Attempt to login
$result = auth()->attempt([
'email' => $this->request->getPost('email'),
'password' => $this->request->getPost('password')
]);
if (!$result->isOK()) {
return $this->response
->setJSON(['error' => $result->reason()])
->setStatusCode(401);
}
// Generate token and return to client
$token = auth()->user()->generateAccessToken($this->getDeviceName());
return $this->response
->setJSON(['token' => $token->raw_token]);
}
public function getDeviceName()
{
$agent = $this->request->getUserAgent();
if ($agent->isBrowser()) {
$currentAgent = $agent->getBrowser() . ' ' . $agent->getVersion();
} elseif ($agent->isRobot()) {
$currentAgent = $agent->getRobot();
} elseif ($agent->isMobile()) {
$currentAgent = $agent->getMobile();
} else {
$currentAgent = 'Unidentified User Agent';
}
return $agent->getPlatform() . " - " . $currentAgent;
}
}
Protect your /api routes using the $filters setting on app/Config/Filters.php. Read: Protecting Routes
Exclude your 'access token' ("auth/token") route together with all API routes ("api/*") from the global "session" & "toolbar" filters.
File: app/Config/Filters.php
<?php
// ...
class Filters extends BaseConfig
{
// ...
public array $globals = [
'before' => [
'session' => ['except' => [
"login*",
"register",
"auth/a/*",
"auth/token",
"api/*"
]],
],
'after' => [
'toolbar' => ['except' => ["auth/token", "api/*"]],
],
];
// ...
public array $filters = [
'tokens' => ['before' => ["api/*"]],
];
}
Make a one-time initial HTTP POST request to the auth/token route to receive the 'access token'. Upon receiving the token, store it with the client. I.e: in localStorage
$.ajax({
url: "https://your-site-domain.com/auth/token",
type: "POST",
data: {
"email": "USER-EMAIL-ADDRESS-HERE",
"password": "USER-PASSWORD-HERE",
},
success: function (response) {
window.localStorage.setItem('token', response.token);
},
error: function (jqXHR) {
console.log(jqXHR.responseText);
},
});
You may now send the received/stored access token using the Authorization header along with all your other protected API HTTP requests in your application without reauthenticating the user. i.e:
$.ajax({
url: "https://your-site-domain.com/api/rest/v1/employees",
type: "GET",
beforeSend: function (jqXHR) {
jqXHR.setRequestHeader(
"Authorization",
"Bearer " + window.localStorage.getItem('token')
);
},
data: {},
success: function (response) {
// Use the response here on success.
// I.e: listing all employees in a table.
},
error: function (jqXHR) {
console.log(jqXHR.responseText);
},
});
How isit possible to throw an custom Error from Laravel in Inertiajs.vue without redirecting then?
Vue Component:
Inertia.post('company-organisations-create', {
name: this.newOrganisation.name,
description: this.newOrganisation.description
},
{
preserveScroll: true,
onSuccess: (page) => {
return Promise.all([
window.Toast.success(this.$page.props.toast.message),
this.newOrganisation.name = '',
this.newOrganisation.description = '',
])
},
onError: (errors) => {
window.Toast.error(errors.toastMessage)
}
});
LaravelController():
public function createOrganisations(Request $request)
{
try {
CompanyOrganisations::create([
'company_id' => $companyID,
'name' => $orgName,
'description' => $orgDescription,
]);
} catch(Excpetion) {
// Create Inertia Error 'onError'
/* As example with json response
return response()->json([
'message' => 'ups, there was an error',
], 403); */
}
return Redirect::route('company.organisations',
)->with([
'toastMessage' => 'Organization created!'
]);
}
As Im not able to receive json format in Inertia request, I need to throw a error in Inertiajs.vue Component.
Thank you very much.
Try this:
try {
// ...
} catch(Excpetion) {
return redirect()->back()->withErrors([
'create' => 'ups, there was an error'
])
}
The errors should be received onError
onError: (errors) => {
window.Toast.error(errors.create)
}
I have a number of working controller functions, however, for usability I want to switch some of them over to be called via AJAX.
Here is my jQuery code:
$.ajax({
method: 'GET',
url: $(this).data('url'),
data: {},
success: function( response ){
console.log( response );
},
error: function( e ) {
console.log(e);
}
});
And here is my controller function:
public function add( $user_id, $project_id )
{
$project = Project::findOrFail( $project_id );
if( empty( $project ) ) {
return view( 'home' )->with('message', 'Sorry, user not found');
}
if ( $project->user_id != $user_id ) {
$scrapbook = Scrapbook::firstOrNew(['user_id' => $user_id]);
$scrapbook->save();
$scrapbook->projects()->attach($project_id);
return true;
} else {
return false;
}
}
As it stands, the controller function actually does what it is supposed to (adds a project to a scrapbook). But what it returns is causing the AJAX request to class as an error so I am unable to handle this correctly. I assume I am supposed to return some kind of response object but I don't know what this is or what it needs to include.
You can simply return an array containing whether it is success or it is failed.
$output_data = ['response' => true, 'message' => 'request is successful.' ];
$output_data = ['response' => false, 'message' => 'error message for user.' ];
return $output_data;
This will return control in success block of ajax.
You can check there is the response is true or false and execute the code accordingly.
You may return a json response on success:
public function add( $user_id, $project_id )
{
// Other code ...
$isSuccess = false;
if ($project->user_id != $user_id) {
$scrapbook = Scrapbook::firstOrNew(['user_id' => $user_id]);
if ($isSuccess = $scrapbook->save()) {
// only attach it if successfully saved
$scrapbook->projects()->attach($project_id);
}
}
return response()->json([
'success' => $isSuccess
]);
}
So in your ajax response you can check:
success: function( response ){
console.log( response.success ); // true or false
}
You need to return a json response using json() fucntion
return response()->json(true);
or if want to send additional data back
return response()->json([
'message' => 'successfull',
'items' =>$insertedItems
],200);
or on fail
return response()->json(['error'=>'error text'],$statusCode)
In Routes:
Route::get('/url', 'controller_name#controller_function');
Ajax:
$.ajax({
method: 'GET',
url: 'url',
data: {},
success: function( response ){
console.log( response );
},
error: function( e ) {
console.log(e);
}
});
In response , send json:
return response()->json(['message' => 'successfull'],200);
i created a form using AJAX because i have several alternatives concerning the fields.
I have correct information in my javascript and make tests... my first select use the following function and generates form
function setParentSector() {
$("#listSectors").html("");
$("#createSector").html("");
if($('#langname option:selected').val() !=0) {
var obj = { 'id': $('#langname option:selected').val() };
if (obj['id'] != 1) {
ajaxSectors(obj);
}
else
{
// another form generated here ...
$('#createSector').append("something else");
}
}
};
I use a "classical" ajax ..
function ajaxSectors(objSetParent) {
$.ajax({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
url: '/admin/ajax/sectorParent',
type: 'post',
dataType: 'json',
async: true,
success: function (result) {
$('#listSectors').append("<label for='langname'>label </label> " +
"<select class='form-control m-input m-input--air' id='sectors' onclick='setLangTranslation()'>" +
"<option value='0' selected>Select value</option></select>" +
"<span class='m-form__help' id='existsAlready'>");
var count = result.length;
for (var i = 0; i < count; i++) {
if (result[i].deleted_at === null) {
$('#sectors').append("<option value='" + result[i].sector_id + "'>" + result[i].sectname + "</option>");
}
else {
console.log("peanuts");
}
}
},
data:objSetParent,
error: function (result) {
},
complete: function (result) {
//console.log("complete");
}
});
}
This part of code works fine and i display what i want...
When I want to save into DB the form, I plan to use the store method and I create the $request->validate()
In the store method I have :
$request->validate([
'admin' => 'required',
'langname' => 'required',
'sectname' => 'required',
'sectshortname' => 'nullable',
]);
return view ('test')
The test view contains just in order to see what i post ..
If i keep the validate part, the page is just refreshed and not validated...
Without the request validate I display the view and i just see the value of the input with the token.
Thanks for your answers. Let's hope my question is "clear" enough
Use this code I hope this code works for you please use this Use Validator;
$rules = [
'admin' => 'required',
'langname' => 'required',
'sectname' => 'required',
'sectshortname' => 'nullable',
];
$data = $request->all();//or you can get it by one by one
$validator = Validator::make($data , $rules);
if ($validator->fails()) {
$error=[];
$errors = $validator->messages();
foreach($errors->all() as $error_msg){
$error[]= $error_msg;
}
return response()->json(compact('error'),401);
}
return view ('test')
I'm getting 422 Unprocessable Entity error even when I'm submitting my form via Ajax.
My javascript file
$.ajaxSetup({
headers: {
'X-XSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
$('.keywords-plan-form').submit(function(event) {
event.preventDefault();
$.ajax({
url: '/laravel/public/keywordsplans',
type: 'POST',
data: $(this).serialize(),
success: function(data){
alert(data);
// success logic
},
error: function(data){
// Error...
var errors = $.parseJSON(data.responseText);
console.log(errors);
$.each(errors, function(index, value) {
});
}
});
});
as you can see I added X-XSRF-TOKEN****strong text to ajax header.
This is my '' tag
<meta name="csrf-token" content="{{ csrf_token() }}">
my Form Data in chrome debuger
_token:5j6DGhhTytbIRB1GrW9Wml9XrOxmKjgE9RiGa4Gf
date:
keyword[0]:Lorem ipsum
keyword[1]:Is dolor amet
keyword[2]:plumber tampa
Request Headers
X-XSRF-TOKEN:5j6DGhhTytbIRB1GrW9Wml9XrOxmKjgE9RiGa4Gf
.....
am I doing something wrong or forgetting something?
I don't think that csrf token is the issue here. If it were you would get TokenMissmatchException and not Unprocessable Entity.
Do you happen to have a request validator in your Controller like this?
$validator = Validator::make($request->all(), [
'username' => 'required|max:30|min:6|unique:users',
'email' => 'required|email|max:50|unique:users',
'password' => 'required|confirmed|min:6',
]);
If so maybe you can do something like this:
if ($validator->fails()) {
if($request->ajax())
{
return response()->json(array(
'success' => false,
'message' => 'There are incorect values in the form!',
'errors' => $validator->getMessageBag()->toArray()
), 422);
}
$this->throwValidationException(
$request, $validator
);
}
After that you can catch validation errors in your ajax error handler like this:
$('.keywords-plan-form').submit(function(event) {
event.preventDefault();
$.ajax({
url: '/laravel/public/keywordsplans',
type: 'POST',
data: $(this).serialize(),
success: function(data){
alert(data);
// success logic
},
error: function(jqXhr, json, errorThrown){// this are default for ajax errors
var errors = jqXhr.responseJSON;
var errorsHtml = '';
$.each(errors['errors'], function (index, value) {
errorsHtml += '<ul class="list-group"><li class="list-group-item alert alert-danger">' + value + '</li></ul>';
});
//I use SweetAlert2 for this
swal({
title: "Error " + jqXhr.status + ': ' + errorThrown,// this will output "Error 422: Unprocessable Entity"
html: errorsHtml,
width: 'auto',
confirmButtonText: 'Try again',
cancelButtonText: 'Cancel',
confirmButtonClass: 'btn',
cancelButtonClass: 'cancel-class',
showCancelButton: true,
closeOnConfirm: true,
closeOnCancel: true,
type: 'error'
}, function(isConfirm) {
if (isConfirm) {
$('#openModal').click();//this is when the form is in a modal
}
});
}
});
});
And see the messages in the
modal message
Maybe someone will come in handy.
422 Unprocessable Entity
is default error by validator laravel
vendor/laravel/framework/src/Illuminate/Validation/Validator.php
If fails validate params, then throught Exception ValidationException
vendor/laravel/framework/src/Illuminate/Validation/ValidationException.php
where default status = 422
And therethore all your ajax responses with non validate forms will be with status = 422
I have solved this issue :
public function register(\Illuminate\Http\Request $request) {
if ($this->validator($request->all())->fails()) {
$errors = $this->validator($request->all())->errors()->getMessages();
$clientErrors = array();
foreach ($errors as $key => $value) {
$clientErrors[$key] = $value[0];
}
$response = array(
'status' => 'error',
'response_code' => 201,
'errors' => $clientErrors
);
} else {
$this->validator($request->all())->validate();
$user = $this->create($request->all());
$response = array(
'status' => 'success',
'response_code' => 200
);
}
echo json_encode($response);
}
Whoever is still looking for the answer, if you are using Lumen make sure the Request object is a type of Illuminate\Http\Request and not the default one from Lumen.
```function create(Request $request){