handle the exception while ajax call and redirect to error page - ajax

I have a JSP page with textbox,drop down list and submit button. on-change of first dropdown list, items belonging to second drop down list should be populated dynamically in the dro down list.Using ajax call i'm calling spring controller where i have written logic to populate the list of items in second dropdown list. Requirement is i need to handle the exception in the spring controller and if any exception occurs redirect the entire page to error page which is other jsp page.
javascript function to get dynamic records to populate 2nd dropdown list records on change of 1st dropdownlist.
function showSID(Id)
{
var xmlHttp;
if (window.XMLHttpRequest)
{
xmlHttp= new XMLHttpRequest();
}
else if (window.ActiveXObject)
{
xmlHttp= new ActiveXObject("Microsoft.XMLHTTP");
}
var url = contextPath+"/getAllSID.htm?Id="+Id;
xmlHttp.onreadystatechange = function() {
handleServerResponse(xmlHttp);
};
xmlHttp.open("GET", url, true);
xmlHttp.send(null);
function handleServerResponse(xmlHttp)
{
if (xmlHttp.readyState==4 || xmlHttp.readyState=="complete")
{
if (xmlHttp.responseText != "null")
{
//handled the response..
}
}
}
}
spring controller to populate records in 2nd dropdown list on change of first dropdown list:
#RequestMapping(value = "/getAllSID", method = RequestMethod.GET)
public void getAllSIDs(HttpServletRequest request,
HttpServletResponse response,
#ModelAttribute SIDDTO dto, BindingResult beException,
#RequestParam("SelectedList") String selList)
throws IOException {
try {
SIDDTO dynamicData = myService.getSIDDynamicData();//Database call
//logic..
response.setContentType("text");
response.resetBuffer();
response.getWriter().print(SID);
}
catch (Exception e)
{
LOGGER.error("Exception occured", e);
}
}
In the above controller, myService.getSIDDynamicData() retrieves data from database, sometimes database may be down or for any other reasons i may get some exceptions.So when some exception occurs i have to redirect to myErrorPage.jsp.
I have tried using response.sendRedirect("/myErrorPage.jsp"); but could not able to redirect to errorpage, may be the reason is my page is already loaded and only when i change the dropdown control hits the above controller and as page is already loaded it could not able to redirect to error page. Please suggest how to handle the exceptions in this scenario and redirect to JSP page(error page) whenever error occurs.Thanks.

Consider adding an exception-handler method to your Spring controller. This is a method that has an #ExceptionHandler annotation. In the exception handler you can set the HTTP status to something suitable. For example:
#ExceptionHandler(value = DataAccessException.class)
public final void handleException(final DataAccessException exception,
final HttpServletResponse response) {
if (exception instanceof RecoverableDataAccessException
|| exception instanceof TransientDataAccessException) {
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
response.setIntHeader("Retry-After", 60);
} else {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}

Related

Custom Async Action Filter for Web API 2

I have a web api to consume the data coming from android mobile. This web api will consume the multi part file from along with the form data the web api request. I followed this article to archive.
[CustAuthAsync]
public async Task<HttpResponseMessage> SaveEHSInspectionData()
{
try
{
string root = HttpContext.Current.Server.MapPath("~/App_Data");
MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(root);
//do stuff
var res = await Request.Content.ReadAsMultipartAsync(provider);
// DO SOME STUFF
}
catch (Exception exp)
{
}
return Request.CreateResponse(HttpStatusCode.OK, result);
}
I wanted to do the custom access validation for this web api, so implemented a filter to validate the request.
I have the filter like below
public class CustAuthAsyncAttribute : ActionFilterAttribute
{
public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
InternalOnExecutingAsync(actionContext);
}
}
The internal method like this
protected void InternalOnExecutingAsync(HttpActionContext actionContext)
{
var authValue = actionContext.Request.Headers;
if (authValue.Contains("CustomAccessToken"))
{
string token = authValue.GetValues("CustomAccessToken").First();
var result = // doing some decription
if (result != null)
{
bool validationResult = // validation with database
if (!validationResult)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{ ReasonPhrase = "Invalid token" };
}
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{ ReasonPhrase = "Invalid token" };
}
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{ ReasonPhrase = "Unauthorized Request" };
}
These implementations are working fine in API Client Tools (Example: Postman) if the validation passes, allows the request to the method.
Postman Response screen shot
This is not working in mobile app, Saying the response message as Unauthorized Access. and not allowing the request to the method even the custom access validations are passed.
FYI : This method is working fine in mobile without filter
Help me to get this works in mobile app also.
Thanks in advance.
Your using the wrong type of filter to manage access. You should use an authorization filter. Besides you can't have an async method to authorize. You have to make the calling client wait for clearance. This may cause the side effects you're experiencing.
I'm not sure this has any to do with fact that it's a mobile application, however the authorization phase ir prior to the processing of the request. Verify that your are not using any other form of authorization in your project.
You should implement an authorization filter by inheriting AuthorizeAttribute and overriding IsAuthorized(HttpActionContext actionContext) method:
public class CustAuthAsync : AuthorizeAttribute
{
public CustAuthAsync()
{
///Some initialization if required. Otherwise, not necessary to declare the constructor..
}
protected override bool IsAuthorized(HttpActionContext actionContext)
{
var authValue = actionContext.Request.Headers;
if (authValue.Contains("CustomAccessToken"))
{
string token = authValue.GetValues("CustomAccessToken").First();
var result = // doing some decription
if (result != null)
{
return //database validation
}
else
{
return false;
//No need to create special unauthorized response. You should not hint the reason at this point. You can do this in the HandleUnauthorizedRequest method.
}
}
else
{
return false;//No need to create special unauthorized response.
}
}
}
You can use this attribute to decorate your controllers. You can even pass parameter in the constructor for more granular control on access management, like a required role to access de controller.

Multiple form submition in spring mvc 3.0

i want to show entered data of user in a registration form (like preview page) to confirm correctness of entered data and if they accept, then that data should go into the database.
here is my controller code:
#RequestMapping( value="/catalogue/FormPreview.action", method=RequestMethod.POST)
public ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command,CatalogueBase catalogueBase) throws Exception {
if(catalogueBase.getTitleNumber()!= null)
{
request.setAttribute("titleNo", catalogueBase.getTitleNumber());
request.setAttribute("title", catalogueBase.getTitle());
request.setAttribute("name", catalogueBase.getName());
request.setAttribute("address", catalogueBase.getAddress());
request.setAttribute("email", catalogueBase.getEmail());
.....
return new ModelAndView("catalogue/catalogueFormPreview","catalogueBase",catalogueBase);
}
else
{
return create(catalogueBase);
}
}
#RequestMapping( value="/catalogue/create.action", method=RequestMethod.POST)
public ModelAndView create(#ModelAttribute CatalogueBase catalogueForm) throws Exception {
ModelAndView mvc = null;
try{
List<CatalogueBase> catalogueBases = new ArrayList<CatalogueBase>(); //getCatalogueBase(request);
catalogueBases.add(catalogueForm);
List<CatalogueBase> catalogueBaseList = catalogueService.create(catalogueBases);
mvc = new ModelAndView("catalogue/catalogueList");
} catch (Exception e) {
e.printStackTrace();
}
return mvc;
}
and I show the preview page as jsp using EL like:
Title NO : ${titleNo}
Title : ${title}
......
......
<a onclick="doAjaxPost();">Confirm Data<span class="icon icon44"></a>
and in the head section of the jsp I am calling ajax like:
<script>
function doAjaxPost() {
var name = $('#name').val();
var education = $('#education').val();
var str = $("#form").serialize();
$.ajax({
type: "POST",
url: "../catalogue/create.action",
data: str,
success: function(response){
alert("Record Added Successfully");
},
error: function(e){
alert('Error: ' + e);
}
});
};
it is showing data on preview page, but after clicking on confirm data, (hyperlink in preview page)
it sends null values to the create method(Second method) please can anyone tell why it's sending nulls and how I can solve this
thanks.
In Preview Page, you are only displaying the text, you need to get your data there as well in preview page either as hidden(or by any other means, like saving in session if much entries are there then etc). so that when you submit after confirmation, you can read all parameters.

Getting different data in ajax response froms second time

I am encountering a problem which I am not able to find out why.
I am using spring mvc and I am sending ajax request to one of my controller.
$.get("<c:url value="/createcomment" />", {id: pageid , newcomment : newcomment})
.done(function(data){
$("#newcomment"+data.pageId).val('');
var html = '<tr><td>'+
'<div class="pull-left">'+
'<img class="img-rounded" src="resources/profile-pics/male/small.jpg" alt="">'+
'</div><div class="span4"><ul class="nav nav-stacked">'+
'<li><font size="2"><i class="icon-user"></i>'+data.account.firstName+' '+data.account.lastName+'</font></li>'+
'<li><font size="2">'+data.text+'</font></li><li><font size="1">'+data.postingDate+
'</font></li></ul></div></td></tr>';
$(html).inserAfter($("#tr"+data.pageId));
}
When i refresh the page and send the request i get the following desired object.
and when I send it second time again i get Some Document Object.
I don't understand what is happening wrong.
#RequestMapping(value="/createcomment",method=RequestMethod.GET)
public #ResponseBody Comment createComment(#RequestParam(value="id")final String pageId,#RequestParam(value="newcomment")final String text,
final HttpServletRequest request ,final WebRequest req){
final Comment comment = new Comment();
comment.setId(GenerateUID.generate());
comment.setText(text);
comment.setPostingDate(new Date());
comment.setPageId(Long.valueOf(pageId));
try {
return comment;
} catch (NumberFormatException e) {
return null;
} catch (SignInNotFoundException e) {
return null;
}
}
Just for additonal information i am using jQuery JavaScript Library v1.7.1
You might want to check if your method throws a NumberFormatException or SignInNotFoundException, in which case it returns null. Your network log shows that 0 bytes of data have been transferred.

MVC3 Custom error pages gives blank result

Using the blog posted here and a topic here on SO i've created a controller which should handle all my error pages.
In my Global.asax.cs I've got the following piece of code:
protected void Application_Error()
{
var exception = Server.GetLastError();
var httpException = exception as HttpException;
var routeData = new RouteData();
Response.Clear();
Server.ClearError();
routeData.Values["controller"] = "Error";
routeData.Values["action"] = "General";
routeData.Values["exception"] = exception;
Response.StatusCode = 500;
if (httpException != null)
{
Response.StatusCode = httpException.GetHttpCode();
switch (Response.StatusCode)
{
case 403:
routeData.Values["action"] = "Http403";
break;
case 404:
routeData.Values["action"] = "Http404";
break;
}
}
// Avoid IIS7 getting in the middle
Response.TrySkipIisCustomErrors = true;
IController errorsController = new ErrorController();
HttpContextWrapper wrapper = new HttpContextWrapper(Context);
var rc = new RequestContext(wrapper, routeData);
errorsController.Execute(rc);
}
My ErrorController looks like this:
public class ErrorController : BaseController
{
/// <summary>
/// Returns a default view for not having access.
/// </summary>
public ActionResult Unauthorized()
{
BaseModel viewModel = new BaseModel
{
LoginModel = new LogonModel(),
ProfessionsTopX = GetTopXProfessions()
};
return View(viewModel);
}
public ActionResult General(Exception exception)
{
return View("Exception", exception);
}
public ActionResult Http404()
{
//This line works
//return Content("Not found", "text/plain");
//This line presents a blank page
return View("404","_Layout");
}
public ActionResult Http403()
{
return View("403", "_Layout");
}
}
And my Razor View only contains the piece of html below;
#{
ViewBag.Title = "404";
}
<h2>404</h2>
This is a 404 page!
When I use the Return Content i'm getting a plain textoutput telling me i'm looking at a 404-page. However, I want the 404 page to fit the rest of my design, so I want to use my own Views. However as soon as I use Return View I'm getting a blank page. I expect to be missing something very obvious, but I don't see it.
I was having the same problem, and finally found the solution that worked for me. The breakthrough came when I placed a breakpoint on the errorsController.Execute(rc); line, and used 'step into' until I came across this exception:
The view 'Detail' or its master was not found or no view engine supports the
searched locations. The following locations were searched:
~/Views/Errors/Detail.aspx
~/Views/Errors/Detail.ascx
~/Views/Shared/Detail.aspx
~/Views/Shared/Detail.ascx
~/Views/Errors/Detail.cshtml
~/Views/Errors/Detail.vbhtml
~/Views/Shared/Detail.cshtml
~/Views/Shared/Detail.vbhtml
The exception was being swallowed, I assume because it was happening inside the Application_Error method and because I had set Response.TrySkipIisCustomErrors = true.
After seeing this error, I quickly realized my problem was simply one of mismatched names: My controller is actually named ErrorController with no 's', not ErrorsController. The problem for me was that I had set routeData.Values["controller"] = "Errors";, which is wrong. Switching it to routeData.Values["controller"] = "Error"; fixed the problem.
Note that you won't catch the error right away, because you directly instantiate the controller, and it won't compile if you have that part spelled wrong. But inside the controller, calling View() will look for the view using the RouteData instance we constructed and passed to the RequestContext object. So if the controller name is spelled wrong there, MVC doesn't know where to look for the view, and since IIS custom errors are skipped, it fails silently.
Long story short: Check your controller and view names. I assume something similar would happen if you have the controller name correct, but the file name of the view doesn't match.
Please check it out this. It is a best way to implement exception handling in mvc.
I have implemented same exception handling but I am facing some issue but still you can refer this.

Return an other action result as string

In my MVC website, I am creating a small forum. For a single post I am rendering my "Single(Post post)" action in my "PostController" like below
<% Html.RenderAction<PostController>(p => p.Single(comment)); %>
Also When a user reply a post I am sending reply as an ajax request to my "CreatePost" action then return "Single" view as result of this action like below
public ActionResult CreatePostForForum(Post post)
{
//Saving post to DB
return View("Single", postViewData);
}
When I do like that only the view is being rendered, Codes in "Single" Actions body isn't beig executed.
What is the best way to do this?
Also I want to return "Single" action result as string in my JsonObject like below
return Json(new{IsSuccess = true; Content= /*HERE I NEED Single actions result*/});
You can use something like this, but be very careful with this. It can actually cause badly traceable errors (for example when you forget to explicitly set view name in Single method).
public ActionResult Single(PostModel model) {
// it is important to explicitly define which view we should use
return View("Single", model);
}
public ActionResult Create(PostModel model) {
// .. save to database ..
return Single(model);
}
Cleaner solution would be to do the same as if it was post from standard form - redirect (XMLHttpRequest will follow it)
For returning ajax views wrapped in json I use following class
public class AjaxViewResult : ViewResult
{
public AjaxViewResult()
{
}
public override void ExecuteResult(ControllerContext context)
{
if (!context.HttpContext.Request.IsAjaxRequest())
{
base.ExecuteResult(context);
return;
}
var response = context.HttpContext.Response;
response.ContentType = "application/json";
using (var writer = new StringWriter())
{
var oldWriter = response.Output;
response.Output = writer;
try
{
base.ExecuteResult(context);
}
finally
{
response.Output = oldWriter;
}
JavaScriptSerializer serializer = new JavaScriptSerializer();
response.Write(serializer.Serialize(new
{
action = "replace",
html = writer.ToString()
}));
}
}
}
It is probably not the best solution, but it works quite well. Note that you will need to manually set View, ViewData.Model, ViewData, MasterName and TempData properties.
My recommendation:
Post your forum reply (and whatever options) via Ajax.
Return your JSONResult, using this method: ASP MVC View Content as JSON to render your content.
In the OnSuccess handler of your ajax call, check if IsSuccess is true. If successful, append the content to the appropriate container using JQuery

Resources