I have read a few posts on fail parameters for a JQuery Ajax call, but none that have directly answered my question. If you want to read up on my jumping off point here, this post would be a good start:
jquery: is there a fail handler for $.post in Jquery?
My problem is that there are a handful of things that may cause my application to fail - if my script returns false the above method would work just fine for me (if I understand it correctly), but most of the time my script will fail by kicking out an exception that I handle using the Zend Framework. I would prefer to return the exception so that I can provide the user with a more detailed message. Is it possible to have my PHP script return a value while still letting the Ajax call know that it was a failure?
Sure you can. First of all you need to categorize you errors, for example:
Fatals
Exceptions
false / error status
I would advise you to take as a return value for correct and with no errors processing - 0. In all other case that would be an error.
Another useful advise would be to use JSON as a client-server conversation.
In PHP it would be:
function prepareJSONResponse($code, $message, array $extra = array())
{
return json_encode(array_merge(
$extra, array(
'code' => (int) $code,
'message' => $message)));
}
In this case you could pass error code and message, and additional params in $extra, for example, for this call:
prepareJSONResponse(1, 'Not enough data passed', array('debug' => true));
Response from server side would be:
{code:1,message:'Not enough data passed','debug': true}
For the client side you need a wrapper function for $.ajax:
// calback(result, error);
function call(url, params, callback)
{
if (typeof params == 'undefined') {
params = {};
}
$.ajax({
'type' : "POST",
'url' : url,
'async' : true,
'data' : params,
'complete' : function(xhr) {
if (xhr.status != 200) {
if (typeof callback == 'function') {
callback(xhr.responseText, true);
}
} else {
if (typeof callback == 'function') {
callback(xhr.responseText, false);
}
}
}
});
}
and function to validate JSON, in order if the corrupted format comes.
function toJSON(data){
try {
data = JSON.parse(data);
} catch (err) {
data = { 'code' : -999, 'message' : 'Error while processing response' };
}
if (typeof data.debug != 'undefined') {
console.log(data.debug);
}
return data;
}
Wrap in try-catch you code, and in catch statement do something like:
try {
...
} catch (Exception $e) {
exit(prepareJSONResponse(1, $e->getMessage(), array(
'debug' => 'There was an error while I were processing your request')));
}
The result would be that you receive debug information in browser console, and could handle errors / exception (prepareJSONResponse()) and fatals (by reading HTTP-status header, if it's not 200, then there was error).
Hope thats what you asked about.
Related
I am trying to make a simple Twilio application, I am broadly following this tutorial : https://www.twilio.com/docs/voice/tutorials/click-to-call-php
I have slightly tweaked and simplified it to suit my needs however, although the problem element doesn't seem any different.
Ajax :
$('#twilio_click_form').on('submit', function(e) {
// Prevent submit event from bubbling and automatically submitting the form
e.preventDefault();
// Call our ajax endpoint on the server to initialize the phone call
$.ajax({
url: '[...]/twilio_click_call',
method: 'POST',
dataType: 'json',
data: {
userPhone: $('#userPhone').val()
}
}).done(function(data) {
// The JSON sent back from the server will contain a success message
alert(data.message);
}).fail(function(error) {
alert('error');
alert(JSON.stringify(error));
});
});
PHP:
public function twilio_click_call()
{
$twilio_creds = array(
'TWILIO_ACCOUNT_SID' => 'xxxx',
'TWILIO_AUTH_TOKEN' => 'xxxx',
'TWILIO_NUMBER' => 'xxxx'
);
$userPhone = $this->input->post('userPhone');
// Create authenticated REST client using account credentials
$client = new Twilio\Rest\Client(
$twilio_creds['TWILIO_ACCOUNT_SID'],
$twilio_creds['TWILIO_AUTH_TOKEN']
);
try
{
$client->calls->create($userPhone, $twilio_creds['TWILIO_NUMBER'],
array("url" => 'http://demo.twilio.com/docs/voice.xml')
);
}
catch (Exception $e)
{
// Failed calls will throw
return $e;
}
return array('message' => 'Call incoming!');
}
The call initiate and runs through perfectly, however the Ajax response always triggers the .fail(), rather than the .done() method - cannot establish why.
There are two things wrong in your code.
1. In ajax call you have defined the datType as json so it will expect the contentType too as json but you're returning the array that is first issue
2. You're calling url inside the url
Use this code:
public function twilio_click_call()
{
$twilio_creds = array(
'TWILIO_ACCOUNT_SID' => 'xxxx',
'TWILIO_AUTH_TOKEN' => 'xxxx',
'TWILIO_NUMBER' => 'xxxx'
);
$userPhone = $this->input->post('userPhone');
// Create authenticated REST client using account credentials
$client = new Twilio\Rest\Client(
$twilio_creds['TWILIO_ACCOUNT_SID'],
$twilio_creds['TWILIO_AUTH_TOKEN']
);
try
{
$client->calls->create($userPhone, $twilio_creds['TWILIO_NUMBER'],
array("url" => 'http://demo.twilio.com/docs/voice.xml')
);
}
catch (Exception $e)
{
// Failed calls will throw
echo json_encode(array($e));
}
echo json_encode(array('message' => 'Call incoming!'));
}
I have a datatables with some inputs in certain columns that are editable. I want to save the edited values in db.
This is my datatables:
The serialized datatables from values looks like this (if you print_r($_POST) for debugging):
javascript - I post the datatables form via ajax request to my laravel api controller method like this:
// Serialize the datatable table into post string
var poDetailUpdates = create_po_details_table.$('input').serialize();
// Call po api to save changes
$.ajax({
type: "POST",
data: poDetailUpdates,
url: '/api/po/update-create-lines/',
complete: function(response) {
if (response.status === 200) {
alert(response.responseText); // success
} else {
alert(response.responseText); // error
}
}
});
php - this is how I handle my form post in laravel:
public function postUpdateCreateLines() {
DB::beginTransaction();
try {
foreach ($_POST as $column_name => $values) {
foreach ($values as $line_id => $new_value) {
DB::table('purchase_order_details')
->where('id', '=', $line_id)
->update(array($column_name => $new_value));
}
}
DB::commit();
Response::make('Purchase order details updated', 200);
} catch (Exception $ex) {
DB::rollback();
Response::make('Purchase order details not updated - '. $ex->getMessage(), 500);
}
}
When the code runs, I get an empty alert() in javascript once the ajax request completes.
I thought the issue might be with the DB transaction related code, so I tried commenting out the following:
// DB::beginTransaction();
// DB::commit();
// DB::rollback();
But this also yields the same result (empty alert). Any idea what might be wrong? how to debug this? Something must be going wrong somewhere...
Your postUpdateCreateLines() method is not actually returning anything; you are making a response, but you need to return it:
return Response::make('Purchase order details updated', 200);
However, there are some other improvements you could make. It is best to return a JSON response to your $.ajax call, and then rely on jQuery's ajax .done and .fail handlers to deal with the response appropriately (rather than using complete):
return Response::json(
['status' => 'success',
'msg' => 'Purchase order details updated']
, 200)
...and add the response dataType to your ajax call:
dataType: "json"
I've been searching for a simple, clean and flexible way to return errors to the browser after a POST or GET request.
Example AJAX request with jQuery:
$.ajax({
data : {
method : 'example',
input : 1
},
success : function(i){
if(i != 'succes'){
alert(i);
}
}
});
Server side response:
switch($_POST['method']){
case 'example':
if($_POST['input'] == 1) {
echo 'succes';
} else {
echo 'error';
// or
die('error');
}
break;
}
This just doesn't feel solid enough. Does anyone know a good way to capture an error combined with a message?
#Esailija is correct; return HTTP status codes so that jQuery's ajax error handler can receive the error.
if you want your AJAX call to be compliant with HTTP standards send the proper status codes defined in HTTP using the header function in PHP. In the future you can call the same PHP page from other apps without forcing specific error-handling.
If the error is application-specific, then return a json-encoded message as said before.
Use JSON:
$error = array(
'error' => 'SOME_ERROR_DESCRIPTION',
'message' => 'You have an error blabla'
);
echo json_encode($error);
From your JavaScript you can just use the JSON object to handle the error as you like.
NOTE: I've pasted more code than just the ajax calls, on the off chance that code is (part of) what's causing the problem. I don't think it is, however, so you're probably better off focussing on the ajax and jAjax functions a bit further down. Also note that, since there's a comment (with upvote) on this question saying my code is hard to decipher, I'd happily clarify what needs clarifying if that could prove to be the key in finding the problem. Thanks.
Here's the thing. I'm trying to ditch jQuery, since the only thing I use is the $.ajax() method, and including an entire lib like jQuery for just 1 feature is IMO crazy. I don't even need the full functionality of the $.ajax method anyway, hence I wrote my own ajax function.
The problem is: it's not working, and I can't seem to figure out why. I'm trying to send objects to the server (specifically: ajaxAction in the controller - using Zend FW). Below is the javascript code, and a summary of what the firebug console tells me.
if (!String.prototype.trim)
{
String.prototype.trim = function()
{
"use strict";
return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
};
}
function getUrl(action,controller)
{
var base,uri;
base = window.location.href.replace('http://'+window.location.host,'');
if (base.length > 1)
{
base = base.substring(1,base.length).split('/');
controller = controller || base[0];
base[0] = controller || base[0];
base[1] = action || base[1];
return '/'+base.join('/');
}
controller = controller || 'index';
action = action || 'ajax';
return base+controller+'/'+action;
}
function formalizeObject(obj,recursion)
{
recursion = recursion || false;
if (typeof obj !== 'object')
{
throw new Error('no object provided');
}
var ret = '';
for (var i in obj)
{
if (!obj.hasOwnProperty(i) || typeof obj[i] === 'function')
{
continue;
}
if (recursion)
{
ret +='['+i+']';
}
else
{
ret += (ret.length > 0 ? '&' : '') + i.toString();
}
if (typeof obj[i] === 'object')
{
ret += formalizeObject(obj[i],true);
continue;
}
ret += '='+obj[i].toString();
}
if (recursion)
{
return ret;
}
return encodeURI(ret);
}
function success()
{
if (this.readyState===4 && this.status===200)
{
console.log(this.responseText);
}
}
function ajax(str,url,method,json)
{
var ret;
json = json || false;
str = str || {};
method = method || 'POST';
url = url || getUrl();
str =
str = (typeof str === 'object' ? str : {data:str});
try
{
ret = new XMLHttpRequest();
}
catch (error)
{
try
{
ret= new ActiveXObject('Msxml2.XMLHTTP');
}
catch(error)
{
try
{
ret= new ActiveXObject('Microsoft.XMLHTTP');
}
catch(error)
{
throw new Error('no Ajax support?');
}
}
}
if (typeof ret !== 'object')
{
throw new Error('No Ajax, FFS');
}
ret.open(method, url, true);
ret.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
ret.setRequestHeader('Content-type', (json ? 'application/json' : 'application/x-www-form-urlencode'));
ret.onreadystatechange = success;
ret.send((json ? JSON.stringify(str) : formalizeObject(str)));
return true;
}
function jAjax(str,url)
{
$.ajax(
{
url : url,
data: str,
type: 'POST',
success: function(res)
{
console.log(res);
}
});
}
Four ways in which I've tried to make the Ajax request:
jAjax({data:{foo:'bar'}},getUrl());//1
jAjax({data:{foo:'bar'}},getUrl(),true);//2
ajax({data:{foo:'bar'}},getUrl());//3
ajax({data:{foo:'bar'}},getUrl(),true);//4
jAjax({data:{foo:'bar'}},getUrl());: This works just fine:
[]{"ajax":true,"controller":"index","action":"ajax","module":"default","identity":{},"data":{"foo":"Bar"}}
Parameters: data[foo] 'bar' And Source: data%5Bfoo%5D=Bar (from POST tab in FB console)
Header: application/x-www-form-urlencoded; charset=UTF-8
All of this was sent to the following url: http://www.foo.bar/index/ajax?data%5Bfoo%5D=bar
This doesn't work, however:
[]{"ajax":true,"controller":"index","action":"ajax","module":"default","identity":{}} is the response
POST tab in FB: JSON data: {foo:'Bar'} source: {"data":{"Foo":"Bar"}} (but same url is case 1)
Header: json; charset=UTF-8
This is the big one: the full request url is identical to url from case 1, as are the headers BUT when I look at the POST tab in the FB console (inspect the request) This is the only difference I can find:
case 1: Parameters: data[foo] 'bar' Source: data%5Bfoo%5D=Bar
In this case, I can't see the Parameters section, only: Source: data%5Bfoo%5D=Bar
Identical to case2, except for the url, which I think I forgot to pass through encodeURI. This case is less important for now. I think/hope I'll get this working the moment I figure out what's wrong with case 3.
In all 4 cases, the request is sent, and received. The controller action is as follows:
public function ajaxAction()
{
$this->_helper->layout->disableLayout();
$this->getHelper('viewRenderer')->setNoRender();
$this->_helper->getHelper('AjaxContext')->addActionContext( 'ajax' , 'json' )
->initContext('json');
if($this->getRequest()->isPost() && $this->getRequest()->isXmlHttpRequest())
{
echo json_encode(array_merge(array('ajax'=>true),$this->_getAllParams()));
}
else
{
throw new Exception('no ajax call made??');
}
}
Since I'm receiving a JSON string, I'm sure the request is posted, and has the correct XMLHttpRequest header. Why then, can't I post JSON objects? Even more to the point: why is case 3 not working? What is jQuery doing that I'm not aware of? What is it, that makes case 1 to work, but not case 3?
PS: It might be irrelevant, but in a moment of madness I tried adding this: ret.setRequestHeader('Connection','close'); to the ajax function, but I noticed that, in the header that got sent out, Connection was set to keep-alive all the same. Perhaps this gives someone a clue as to what went wrong?
Thanks in advance
In case anybody wonders what was wrong:
ret.setRequestHeader('Content-type', 'application/x-www-form-urlencode');
Should have been "x-www-form-urlencoded", with a "d" in the end:
ret.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
sending a formalized object is now working, and I can get rid of jQuery :-)
I have the following:
YUI().use("io-form",
function(Y) {
var cfg = {
method: 'POST',
form: {
id: 'subscribe-form',
useDisabled: false
}
};
function login() {
Y.io('process.php', cfg);
Y.on('io:success', onSuccess, this);
Y.on('io:failure', onFailure, this);
};
function onSuccess(id,response,args) {
document.getElementById('myformmsg').innerHTML = response.responseText;
document.forms['myform'].reset();
};
function onFailure(id,response,args) {
document.getElementById('myformmsg').innerHTML = "Error, retry...";
document.forms['myform'].reset();
};
Y.on('click', login, '#myformbutton', this, true);
});
How does yui know whether to go into onSucces of onFailure. What do I have to return from PHP?
It depends on the header returned http status code.
let say status code 200, it will goes to onSuccess.
let say status code 500 (internal server error), it will goes to onFailure.
List of HTTP status code here: http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
If you have some fatal error in php, it will still returns status 200 because the request is successful.
If you would like to handle php errors, I would suggest you to have an json return like everytime on success:
{
status: 0, // Let say 0 for OK, -1 for Error, you can define more by yourself
results: <anything you want here>,
errors: <errors message/errors code for your ajax handler to handle>
}
it can be done in php like this:
$response = array(
'status' => 0,
'results' => 'something good ...',
'errors' => 'error message if status is -1'
);
echo json_encode($response);
In your javascript, you will be handling like this:
function onSuccess(id,response,args) {
var responseObj = Y.JSON.parse(response);
if (responseObj.status === 0) {
// Request and process by php successful
}
else {
// Error handling
alert(responseObj.errors);
}
};
Remember, if you wanted to use Y.JSON, you need to include 'json-parse', example:
YUI().use('json-parse', , function (Y) {
// JSON is available and ready for use. Add implementation
// code here.
});