How to throw an ajax error with ASP.NET MVC3 - ajax

I want to handle all server-side errors with jQuery on client side. For this purpose a create axception handling attrbute for my MVC3 application like this:
public class JsonErrorHandlerAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
string result = jsSerializer.Serialize(new { error = filterContext.Exception.Message });
filterContext.HttpContext.Response.Write(result);
}
}
But with this approach it just returns normal json response with 200 OK result. A don't want to parse it on client side to determine if it has an error or not. So my question is what is the best way to throw an ajax error?

Add this to your function
HttpContext.Response.StatusCode = 500;
This will raise the error event if you use jQuery ajax, there you can react.

Related

Display server side Exception Message on DataTables.NET

I am developing a MVC application which has handles authorization and login information at a base controller class overriding OnActionExecuting event.
At AJAX calls when an exception arises I can handle this via attributes and display error messages with on custom model window.
My custom attribute is as follows :
public class JsonExceptionFilterAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = 500;
filterContext.ExceptionHandled = true;
string msg = HttpUtility.HtmlDecode(filterContext.Exception.Message);
if (filterContext.Exception.GetType() == Type.GetType("System.UnauthorizedAccessException"))
{
msg = "Unauthorized access";
}
filterContext.Result = new JsonResult
{
Data = new
{
errorMessage = msg
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
}
}
But when an Exception occurs while using DataTables.Net the only mesage I get is the DataTables.NET's own error mesaages saying "... please see http://datatables.net/tn/7"
But I want to display my own Exception message as I do in other AJAX calls. Basically I want to display the error message I provide in my custom attribute response.
I have Googled it, advising to use "fnServerData" but I can not find my Exception message in any of the parameters (sSource, aoData, fnCallback, oSettings) I get by this event handler.
How can I possibly get the Exception message that my base controller returns and display it?
Regards.
P.S. : Handling the Exception in the Action and returning it does not apply here, because I do not fall to Action method at all.

calling a spring controller from xhrPost doesnot redirect

I am calling a spring controller method from dojo xhrPost and I need to redirect to a new html from the controller method. But controller returns back to the dojo in javascript instead of moving to a new page.
my javascript:
var xhrArgs={
url:"/tradeIn/submitTradeIn",
postData:dojo.toJson(tradeInDetails),
handleAs:"text",
headers:{"Content-Type":"application/json"}
/*load:function(data){
console.log(data);
label=data;
if(data =="fail"){
location.reload(true);
window.location="/Fail";
}
else{
window.location="/success";
}
}*/
}
var deferred=dojo.xhrPost(xhrArgs);
my spring controller:
#RequestMapping(value="/tradeIn/submitTradeIn", method = {RequestMethod.POST})
public String submitTradeIn(#RequestBody TradeInDetails tradeDetails) throws UnsupportedEncodingException{
List<byte[]> labelList=new ArrayList<byte[]>();
ShippingInfo shippingInfo=new ShippingInfo();
shippingInfo.setAddress1(tradeDetails.getCustomerDetails().get(0).getAddress1());
shippingInfo.setAddress2(tradeDetails.getCustomerDetails().get(0).getAddress1());
shippingInfo.setCity(tradeDetails.getCustomerDetails().get(0).getCity());
shippingInfo.setCompany(tradeDetails.getCustomerDetails().get(0).getCompany());
shippingInfo.setDayPhone(tradeDetails.getCustomerDetails().get(0).getDayPhone());
shippingInfo.setEmail(tradeDetails.getCustomerDetails().get(0).getEmail());
shippingInfo.setEvePhone(tradeDetails.getCustomerDetails().get(0).getEvePhone());
shippingInfo.setFirstName(tradeDetails.getCustomerDetails().get(0).getFirstName());
shippingInfo.setLastName(tradeDetails.getCustomerDetails().get(0).getLastName());
shippingInfo.setState(tradeDetails.getCustomerDetails().get(0).getState());
shippingInfo.setZip(tradeDetails.getCustomerDetails().get(0).getZip());
shippingInfo.setCountry(tradeDetails.getCustomerDetails().get(0).getCountry());
List<ReturnRequestLabel> label;
List<TradeInClubs> tradeInList1= new ArrayList<TradeInClubs>();
for(ClubDetails cl: tradeDetails.getClubDetails()){
TradeInClubs tradeInclubs1=new TradeInClubs();
tradeInclubs1.setClubMaterial(cl.getShaftType());
tradeInclubs1.setClubType(cl.getClubType());
tradeInclubs1.setManufacturer(cl.getClubManufacturer());
tradeInclubs1.setModel(cl.getClubModel());
tradeInclubs1.setTradeInValue(cl.getTradeInPrice());
tradeInList1.add(tradeInclubs1);
}
try{
ReturnFedexLabel returnFedexLabel = new ReturnFedexLabel();
label=returnFedexLabel.fetchFedexLabel(tradeInList1, shippingInfo);
byte[] labelImageData;
String fedexLabelNumber=null;
for(ReturnRequestLabel rl: label){
labelImageData=rl.fedexReturnLabel.imageData;
labelList.add(labelImageData);
fedexLabelNumber=rl.trackingNumber;
}
File f=new File("label.jpg");
BufferedImage img=ImageIO.read(new ByteArrayInputStream(labelList.get(0)));
ImageIO.write(img,"JPEG",f);
int id=tradeInDao.insertQuery(shippingInfo,tradeInList1,fedexLabelNumber);
byte[] pdfData=fedexLabelToPdf.printFedexLabel(labelList);
emailTradeIn.emailTradeInDetails(pdfData,tradeDetails.getCustomerDetails().get(0).getEmail(),tradeInList1,id);
System.out.println("here");
} catch (Exception e){
logger.error(e.getMessage());
return "fail";
}
return "success";//Base64.encodeBase64String(labelList.get(0));
}
it is not moving to success page. it just stays in the current page
Check and see what deferred is, it is probably "success".
From http://dojotoolkit.org/reference-guide/1.7/quickstart/ajax.html:
dojo.xhrPost xhrPost will create an Ajax request using the HTTP POST
method and is usually used to submit data to a service. It returns
data to a callback. The callback is defined as a member of the object
used to create the request (the property-bag), or by using the
dojo.Deferred.then() method.
For complete details and examples, see the dojo.xhrPost documentation.
if you need to change the page from the controller it shouldn't be an ajax request. If you can change it from the client side then you can continue with the ajax request and respond accordingly.

Calling an XPage through ajax call

Scenario
Lotus Domino form with a button that made an Ajax call to an Xpage, that do some stuff (read a properties file).
Framework: prototype.js
Button code:
var now = new Date()
var n = $H({
........
now: now.getTime()
});
var url = "/" + $F("path") + "/myxpages.xsp";
var myAjax = new Ajax.Request(
url,
{
method: 'post',
parameters: n.toQueryString(),
onComplete: function(response) {
ajaxResult = response.responseText;
}
});
Xpage
myxpages.xsp has this SSJS code on afterPageLoad event
var request = facesContext.getExternalContext().getRequest();
var response = facesContext.getExternalContext().getResponse();
response.setHeader("Expires", -1);
response.setHeader("Cache-Control", "no-cache");
com.org.MyGetProperties.readProperties(request,response);
MyGetProperties class
This class is deployed in WebContent/WEB-INF/classes
public class MyGetProperties {
static PrintWriter out = null;
public static synchronized void readProperties(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
*(DO SOME STUFF HERE)*
out = new PrintWriter(response.getWriter());
// return result
out.println("OK");
} catch (Exception e) {..}
}
}
Sometimes ajaxResult variable in ajax response call is empty, sometimes is "OK", as expected (seems to be something related to cache, but i think i've managed it correctly).
The behavior is different on different production server, i don't know if depends on server configurations.
Could be a PrintWriter problem?
Short answer: don't. Long answer: use the Ajax control. You put that on your page and your URL changes to myxpages.xsp/nameyougavetheajaxcontrolproperty
This way you can be sure not to run foul of any cached result or pending operation. It also has a property where you can specify a Java class directly. That class extends (need to Google that - answered it on SO before) which gives you direct access to request/response
Update:
You need to tell that you are done:
facesContext.responseComplete();
See my original post on XAgents, the revision and some thought on testing.

MVC Generic error view and Ajax Post and error code 500

I have set up an mvc app with an _error.cshtml that is set to catch exceptions I throw in the controller.
I also have a few ajax posts on some pages that checks for errors and then it does something else.
On the server, I have a filter on all exceptions and then check if it is an ajax request and return something that can be deserialized on the client. The problem is that if I do not set the post response status code to 500 then ajax will not see this error and I can't show a nice message. If I set the status to 500 I get the default IIS error message stating something happened on the server.
I would like to handle some errors on the page in the ajax results but maintain the generic error handling. Is this an IIS setting to allow custom 500 message per site? The web.config Custom Error On|Off makes no difference in my case.
The filter you have on all exceptions that is checking if its an ajax request, is that a filter made on your own?
I had a slightly similar issue, and I had to make sure the flag TrySkipIisCustomErrors was set as true in order to avoid the standard IIS error.
This flag is located on the Response object of the HttpContext.
This is also done by the standard HandleError filter, pay attention to the last line in its implementation of the OnException method:
public virtual void OnException(ExceptionContext filterContext) {
if (filterContext == null) {
throw new ArgumentNullException("filterContext");
}
if (filterContext.IsChildAction) {
return;
}
// If custom errors are disabled, we need to let the normal ASP.NET exception handler
// execute so that the user can see useful debugging information.
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) {
return;
}
Exception exception = filterContext.Exception;
// If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
// ignore it.
if (new HttpException(null, exception).GetHttpCode() != 500) {
return;
}
if (!ExceptionType.IsInstanceOfType(exception)) {
return;
}
string controllerName = (string)filterContext.RouteData.Values["controller"];
string actionName = (string)filterContext.RouteData.Values["action"];
HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
filterContext.Result = new ViewResult {
ViewName = View,
MasterName = Master,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
TempData = filterContext.Controller.TempData
};
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = 500;
// Certain versions of IIS will sometimes use their own error page when
// they detect a server error. Setting this property indicates that we
// want it to try to render ASP.NET MVC's error page instead.
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}

Authorize attribute is letting ActionResult code execute

I have a custom attribute that I am using for Ajax requests to prevent the default FormsAuthentication workflow from happening (since it doesn't make sense for an Ajax request).
This is the custom authorize attribute:
public class AjaxAuthorize : AuthorizeAttribute {
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) {
UrlHelper urlHelper;
if (filterContext.HttpContext.Request.IsAjaxRequest()) {
urlHelper = new UrlHelper(filterContext.RequestContext);
filterContext.HttpContext.Response.StatusCode = 401;
//Don't let IIS7 be mean.
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
//Return JSON which tells the client where the login page exists if the user wants to authenticate.
filterContext.HttpContext.Response.Write(new JavaScriptSerializer().Serialize(
new {
LoginUrl = string.Format("{0}?ReturnURL={1}", FormsAuthentication.LoginUrl, urlHelper.Encode(filterContext.HttpContext.Request.Url.PathAndQuery))
}
));
filterContext.HttpContext.Response.End();
} else {
base.HandleUnauthorizedRequest(filterContext);
}
}
}
I have my ActionResult decorated with the custom authorize attribute like so:
[HttpPut]
[ValidateInput(false)]
[AjaxAuthorize]
public ActionResult Comment(string id, string comment) {
//stuff happens here.
}
An exception is thrown inside of my action result because I try to access information about a user that would only exist if you are logged in. But, I don't understand why this code is even being executed because I have my authorization attribute protecting the action.
protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
// Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs.
filterContext.Result = new HttpUnauthorizedResult();
}
This is default implementation of HandleUnauthorizedRequest. Probably you need to assign your custom ActionResult that contains JSON you need and valid status code. I'm not sure direct processing request object from HTTP context would work.
I ended up returning an HTTP 418 (I have been wanting to use this for awhile) so that the Forms Authentication module didn't intercept the request. Then in my script I look for that status code and handle it as an unauthorized request and do the proper redirect.
Try calling base.HandleUnauthorizedRequest(filterContext); inside the IF block. Or just remove the ELSE block and always call base.HandleUnauthorizedRequest(filterContext);.

Resources