Using AJAX with ASP.NET MVC 1.0 on IIS 6 - asp.net-mvc

I am running into a problem trying to use AJAX and jQuery with ASP.NET MVC on IIS 6.0. I receive a 403.1 error when I attempt to invoke an action via jQuery. Is there anything I must add to the web.config in order to support this?
Client Code
<script src="../../Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="../../Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
<script src="../../Scripts/jquery-1.3.2.js" type="text/javascript"></script>
<script type="text/javascript">
function deleteRecord(recordId) {
// Perform delete
$.ajax(
{
type: "DELETE",
url: "/Financial.mvc/DeleteSibling/" + recordId,
data: "{}",
success: function(result) {
window.location.reload();
},
error: function(req, status, error) {
alert("Unable to delete record.");
}
});
}
</script>
<a onclick="deleteRecord(<%= sibling.Id %>)" href="JavaScript:void(0)">Delete</a>
Server Code
[AcceptVerbs(HttpVerbs.Delete)]
public virtual ActionResult DeleteSibling(int id)
{
var sibling = this.siblingRepository.Retrieve(id);
if (sibling != null)
{
this.siblingRepository.Delete(sibling);
this.siblingRepository.SubmitChanges();
}
return RedirectToAction(this.Actions.Siblings);
}
Error
You have attempted to execute a CGI, ISAPI, or other executable program from a directory that does not allow programs to be executed.
HTTP Error 403.1 - Forbidden: Execute access is denied.
Internet Information Services (IIS)
Update
Darin correctly pointend out that it helps if you add the DELETE verb to .mvc extension, however I an now running into the following issue:
[HttpException (0x80004005): Path 'DELETE' is forbidden.] System.Web.HttpMethodNotAllowedHandler.ProcessRequest(HttpContext context) +80 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication+IExecutionStep.Execute() +179 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Status: 405 - Method not allowed

When you register the .mvc extension with aspnet_isapi.dll in IIS you need to enable the DELETE verb:
alt text http://support.citrix.com/article/html/images/CTX104183-1.gif

This is how to change this in code:
class IISDirEntry
{
public void SetProperty(string metabasePath, string propertyName, string newValue)
{
// metabasePath is of the form "IIS://servername/path"
try
{
DirectoryEntry path = new DirectoryEntry(metabasePath);
PropertyValueCollection propValues = path.Properties[propertyName];
object[] propv = ((object[])propValues.Value);
int searchIndex = newValue.IndexOf(',');
int index = -1;
for (int i = 0; i < propv.Length; i++)
{
if (propv[i].ToString().ToLower().StartsWith(newValue.ToLower().Substring(0, searchIndex + 1)))
{
index = i;
break;
}
}
if (index != -1)
{
propv[index] = newValue;
}
else
{
List<object> proplist = new List<object>(propv);
proplist.Add(newValue);
propv = proplist.ToArray();
}
path.Properties[propertyName].Value = propv;
path.CommitChanges();
Console.WriteLine("IIS6 Verbs fixed.");
}
catch (Exception ex)
{
if ("HRESULT 0x80005006" == ex.Message)
Console.WriteLine(" Property {0} does not exist at {1}", propertyName, metabasePath);
else
Console.WriteLine("Failed in SetProperty with the following exception: \n{0}", ex.Message);
}
}
}
public void ChangeIIS6Verbs()
{
if (IISVersion < 7.0)
{
IISDirEntry iisDirEntry = new IISDirEntry();
string windir = Environment.GetEnvironmentVariable("windir");
iisDirEntry.SetProperty("IIS://localhost/W3SVC/" + SiteIndex + "/ROOT", "ScriptMaps",
#".aspx," + Path.Combine(windir, #"\microsoft.net\framework\v2.0.50727\aspnet_isapi.dll") + ",1,GET,HEAD,POST,DEBUG,DELETE");
}
}
Useful if need to configure on install

Related

Ajax call refreshes the client page

I'm using html2canvas to take screenshot of a div and then upload it to the server. After the image is saved on the server, the page on client refreshes. And that is something I don't want. This is the code, that saves the picture:
public void SaveImage(string imageString, int id)
{
string s = imageString.Replace("data:image/png;base64,", "");
byte[] imageB = Convert.FromBase64String(s);
var filePath = Path.Combine(Server.MapPath("~/screens/" + id));
Directory.CreateDirectory(filePath);
using (var ms = new MemoryStream(imageB))
{
using (var image = Image.FromStream(ms))
{
image.Save(filePath + "/screen.png", ImageFormat.Png);
}
}
}
This is the Javascript code
html2canvas(document.querySelector("#screen")).then(function (canvas) {
var data = canvas.toDataURL("image/png");
$.ajax({
url: '#Url.Action("SaveImage")',
type: 'POST',
data: { imageString: data, id: #ViewBag.id },
dataType: 'json'
});
});
I have tried some advices I found online like preventDefault() in jQuery. But that's about it. I feel like all the other issues and solutions does not affect me.
EDIT: Simple HTML that refreshes the page too
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Test</title>
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/html2canvas.js"></script>
<script>
$(document).ready(function () {
html2canvas(document.querySelector("#screen")).then(function (canvas) {
var data = canvas.toDataURL("image/png");
$.ajax({
url: '#Url.Action("SaveImage")',
type: 'POST',
data: { imageString: data, id: 2 },
dataType: 'json',
});
});
});
</script>
</head>
<body>
<div id="screen">Some text</div>
</body>
</html>
EDIT 1: The whole controller with SaveImage method
using System;
using System.Linq;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;
using BcVnc.Models;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
namespace BcVnc.Controllers
{
[Authorize(Roles = "Admin, User")]
public class ConnectionController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: Connection
public ActionResult Index(int id, bool viewOnly)
{
int access = checkUserAccess(id, viewOnly);
if(access != 0)
{
ViewBag.id = id;
if (access == 1 & viewOnly == true)
ViewBag.viewOnly = true.ToString().ToLower();
else if(access == 1 && viewOnly == false)
ViewBag.viewOnly = false.ToString().ToLower();
else
ViewBag.viewOnly = true.ToString().ToLower();
return View();
}
else
{
return View("Error");
}
}
private int checkUserAccess(int id, bool viewOnly)
{
var userId = User.Identity.GetUserId();
var userDevice = db.UserDevices.Where(ud => ud.UserId == userId).FirstOrDefault(ud => ud.DeviceId == id);
var device = db.Devices.FirstOrDefault(d => d.Id == id);
ViewBag.name = device.Name;
if (userDevice == null)
return 0;
else
{
if (userDevice.ViewOnly == false)
return 1;
else
return -1;
}
}
public void SaveImage(string imageString, int id)
{
string s = imageString.Replace("data:image/png;base64,", "");
byte[] imageB = Convert.FromBase64String(s);
var filePath = Path.Combine(Server.MapPath("~/screens/" + id));
Directory.CreateDirectory(filePath);
using (var ms = new MemoryStream(imageB))
{
using (var image = Image.FromStream(ms))
{
image.Save(filePath + "/screen.png", ImageFormat.Png);
}
}
}
}
}
Not sure how come I was not able to find this before, but the whole problem was in Visual Studio settings. The refresh probably would not happen outside the localhost: Refresh in browser when uploading mvc file c #
After changing that in settings, no refresh anymore.

Handling Ajax cal exceptions via Custom Action Filters

I am implementing an authorization mechanizm for my MVC application via Custom Action Filters.
I have provided the following Custom Action Filter for authorization:
[AttributeUsageAttribute(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class AuthorizationFilterAttribute : ActionFilterAttribute
{
public AuthorizationEntity Entity { get; set; }
public AuthorizationPermission Permission { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
AuthorizationResult result = null;
// Base actions (Authentication first)
base.OnActionExecuting(filterContext);
BaseController controller = filterContext.Controller as BaseController;
if (controller != null)
{ // Base actions (Authorizatioın next)
User usr = controller.currentUser;
AuthorizationResult ar = AuthorizationManager.GetAuthorizationResult(this.Entity, this.Permission, usr.UserId, usr.RoleId);
if (!ar.IsAuthorized)
{
throw new UnauthorizedAccessException(ar.Description);
}
// Authorized, continue
return;
}
}
}
And in my Base Controller class I am handling UnauthorizedAccessException type Exceptions and redirect them to a warning page via the following code
protected override void OnException(ExceptionContext filterContext)
{
if (filterContext.Exception is UnauthorizedAccessException)
{
if (!filterContext.HttpContext.Request.IsAjaxRequest())
{
Exception ex = filterContext.Exception;
filterContext.ExceptionHandled = true;
filterContext.Result = new ViewResult()
{
ViewName = "UnauthorizedAccess"
};
}
else
{
throw filterContext.Exception;
}
}
}
This mechanism is OK for actions which return ActionResult. But I also have some AJAX calls, which I don't want to redirect to a warning page but would ilke to display a warning pop-up instead. Thi is why I have checked if the request is an Ajax call is not.
I am using the following code to make Ajax calls:
$.ajax({
type: "POST",
url: "AjaxPostMethodName",
dataType: "json",
data:
{
postval: [some value here]
},
success: function (msg) {
// Do some good actions here
},
error: function (x, t, m, b) {
// Display error
alert(m);
}
})
which goes to the following method on the Controller
public JsonResult AjaxPostMethodName(string postval)
{
try
{
// Some cool stuff here
return Json(null);
}
catch (Exception ex)
{
Response.StatusCode = UNAUTHORIZED_ACCESS_HTTP_STATUS_CODE;
return Json(ex.Message);
}
}
But when I fail the authorization check it directly shows the "Internal Server Error" message instead of falling to the catch block of AjaxPostMethodName method and displaying the proper message.
How can I make such code display filterContext.Exception instead of static "Internal Server Error" message?
Regards.
I finally found the answer to my solution in another Stack Overflow post (Can I return custom error from JsonResult to jQuery ajax error method?). I should use JsonExceptionFilterAttribute 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 = 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
};
}
}
}
Your OnException method will get called when there is Unhandled exception in your code. And in your ajax method AjaxPostMethodName you have put your code in try catch blcok. So any exception in this method will not go to your OnException method.
I've just checked the Response.StatusCode behavior and for me it works.
Index.cshtml
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<script type="text/javascript">
$(document).ready(function () {
alert('doc ready');
$.ajax({
type: "POST",
url: '#Url.Action("AjaxPostMethodName")',
dataType: "json",
data:
{
test: '10'
},
success: function (msg) {
// Do some good actions here
alert('success');
alert(msg);
},
error: function (x, t, m, b) {
// Display error
alert('error');
}
});
});
</script>
HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public JsonResult AjaxPostMethodName(string postval)
{
Response.StatusCode = 401;
return Json("test");
}
}
}
When I set Response.StatusCode to 200 it calls success, when 401 it calls error.
Please verify whether other parts of your code don't interfere with it somehow.
You could try also following workaround - if AjaxPostMethodName throws exception returned JSON has a flag isValid and a message errorMessage, so in your ajax success method you can just check whether isValid is okay and handle error.

How to Catch An Ajax error while using Jquery UI tabs along with Custom Error handler

I am using Jquery UI tabs in my asp.net mvc web application. I have my tabs working good.
But, the problem is when ever an ajax errors happens, it should be caught and JSON response should be thrown back.
I am using an CustomError handler over riding MVC HandleError Attribute as follows:
public class CustomHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
{
return;
}
if (new HttpException(null, filterContext.Exception).GetHttpCode() != 500)
{
return;
}
if (!ExceptionType.IsInstanceOfType(filterContext.Exception))
{
return;
}
// if the request is AJAX return JSON else view.
if (filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
{
filterContext.Result = new JsonResult
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new
{
error = true,
message = filterContext.Exception.Message
}
};
}
else
{
var controllerName = (string)filterContext.RouteData.Values["controller"];
var actionName = (string)filterContext.RouteData.Values["action"];
var 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;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
}
So, if the error occurs and it is an ajax request , then the above method will throw the JSON response.
But,I am struggling to find out how to catch that JSON respnse and show it on Client Side.
Please help..I tried using ajaxoptions with UI Tabs as follows:
$(document).ready(function () {
$('#tabs').tabs({
activate: function (event, ui) {
ui.oldPanel.empty();
},
ajaxOptions: { success: Success, error: Failure }
});
$('#tabs').css('display', 'block');
$(function () {
$(this).ajaxStart(function () {
$("#ajaxLoading").show();
});
$(this).ajaxStop(function () {
$("#ajaxLoading").hide();
});
});
});
function Success(data) {
alert("Successfully loaded the tabs");
}
function Failure() {
alert("Some thing wrong had happened");
}
please help..on how to recieve that erronoeous JSON response and show an appropraite alert to the end user..
I found the solution as this:
$.ajaxSetup({
type: "GET",
cache: false,
error: function (e) {
var Error = e.responseText;
var ErrorCode= xx;
alert("Sorry, An Error has been occured while processing your request " + Error);
}
});
I have used ajaxSetup() to receive the response from Server Side.
Hope this helps...

ASP.NET MVC Ajax Error handling

How do I handle exceptions thrown in a controller when jquery ajax calls an action?
For example, I would like a global javascript code that gets executed on any kind of server exception during an ajax call which displays the exception message if in debug mode or just a normal error message.
On the client side, I will call a function on the ajax error.
On the server side, Do I need to write a custom actionfilter?
If the server sends some status code different than 200, the error callback is executed:
$.ajax({
url: '/foo',
success: function(result) {
alert('yeap');
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert('oops, something bad happened');
}
});
and to register a global error handler you could use the $.ajaxSetup() method:
$.ajaxSetup({
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert('oops, something bad happened');
}
});
Another way is to use JSON. So you could write a custom action filter on the server which catches exception and transforms them into JSON response:
public class MyErrorHandlerAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
filterContext.Result = new JsonResult
{
Data = new { success = false, error = filterContext.Exception.ToString() },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
}
and then decorate your controller action with this attribute:
[MyErrorHandler]
public ActionResult Foo(string id)
{
if (string.IsNullOrEmpty(id))
{
throw new Exception("oh no");
}
return Json(new { success = true });
}
and finally invoke it:
$.getJSON('/home/foo', { id: null }, function (result) {
if (!result.success) {
alert(result.error);
} else {
// handle the success
}
});
After googling I write a simple Exception handing based on MVC Action Filter:
public class HandleExceptionAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.Exception != null)
{
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
filterContext.Result = new JsonResult
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new
{
filterContext.Exception.Message,
filterContext.Exception.StackTrace
}
};
filterContext.ExceptionHandled = true;
}
else
{
base.OnException(filterContext);
}
}
}
and write in global.ascx:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleExceptionAttribute());
}
and then write this script on the layout or Master page:
<script type="text/javascript">
$(document).ajaxError(function (e, jqxhr, settings, exception) {
e.stopPropagation();
if (jqxhr != null)
alert(jqxhr.responseText);
});
</script>
Finally you should turn on custom error.
and then enjoy it :)
Unfortunately, neither of answers are good for me. Surprisingly the solution is much simpler. Return from controller:
return new HttpStatusCodeResult(HttpStatusCode.BadRequest, e.Response.ReasonPhrase);
And handle it as standard HTTP error on client as you like.
I did a quick solution because I was short of time and it worked ok. Although I think the better option is use an Exception Filter, maybe my solution can help in the case that a simple solution is needed.
I did the following. In the controller method I returned a JsonResult with a property "Success" inside the Data:
[HttpPut]
public JsonResult UpdateEmployeeConfig(EmployeConfig employeToSave)
{
if (!ModelState.IsValid)
{
return new JsonResult
{
Data = new { ErrorMessage = "Model is not valid", Success = false },
ContentEncoding = System.Text.Encoding.UTF8,
JsonRequestBehavior = JsonRequestBehavior.DenyGet
};
}
try
{
MyDbContext db = new MyDbContext();
db.Entry(employeToSave).State = EntityState.Modified;
db.SaveChanges();
DTO.EmployeConfig user = (DTO.EmployeConfig)Session["EmployeLoggin"];
if (employeToSave.Id == user.Id)
{
user.Company = employeToSave.Company;
user.Language = employeToSave.Language;
user.Money = employeToSave.Money;
user.CostCenter = employeToSave.CostCenter;
Session["EmployeLoggin"] = user;
}
}
catch (Exception ex)
{
return new JsonResult
{
Data = new { ErrorMessage = ex.Message, Success = false },
ContentEncoding = System.Text.Encoding.UTF8,
JsonRequestBehavior = JsonRequestBehavior.DenyGet
};
}
return new JsonResult() { Data = new { Success = true }, };
}
Later in the ajax call I just asked for this property to know if I had an exception:
$.ajax({
url: 'UpdateEmployeeConfig',
type: 'PUT',
data: JSON.stringify(EmployeConfig),
contentType: "application/json;charset=utf-8",
success: function (data) {
if (data.Success) {
//This is for the example. Please do something prettier for the user, :)
alert('All was really ok');
}
else {
alert('Oups.. we had errors: ' + data.ErrorMessage);
}
},
error: function (request, status, error) {
alert('oh, errors here. The call to the server is not working.')
}
});
Hope this helps. Happy code! :P
In agreement with aleho's response here's a complete example. It works like a charm and is super simple.
Controller code
[HttpGet]
public async Task<ActionResult> ChildItems()
{
var client = TranslationDataHttpClient.GetClient();
HttpResponseMessage response = await client.GetAsync("childItems);
if (response.IsSuccessStatusCode)
{
string content = response.Content.ReadAsStringAsync().Result;
List<WorkflowItem> parameters = JsonConvert.DeserializeObject<List<WorkflowItem>>(content);
return Json(content, JsonRequestBehavior.AllowGet);
}
else
{
return new HttpStatusCodeResult(response.StatusCode, response.ReasonPhrase);
}
}
}
Javascript code in the view
var url = '#Html.Raw(#Url.Action("ChildItems", "WorkflowItemModal")';
$.ajax({
type: "GET",
dataType: "json",
url: url,
contentType: "application/json; charset=utf-8",
success: function (data) {
// Do something with the returned data
},
error: function (xhr, status, error) {
// Handle the error.
}
});
Hope this helps someone else!
For handling errors from ajax calls on the client side, you assign a function to the error option of the ajax call.
To set a default globally, you can use the function described here:
http://api.jquery.com/jQuery.ajaxSetup.

The HTTP verb POST used to access path '/Documents/TestNote/Documents/AddNote' is not allowed

I am having two user control on a aspx page and one of the user control has a text area for notes. and i am trying to use JSON so that when they click the addnote button it does not reload the page.
Below is my java script , but it says that it is giving this error
The HTTP verb POST used to access path '/Documents/TestNote/Documents/AddNote' is not allowed.
<script type="text/javascript">
$(document).ready(function() {
$("#btnAddNote").click(function() {
alert("knock knock");
var gnote = getNotes();
//var notes = $("#txtNote").val();
if (gnote == null) {
alert("Note is null");
return;
}
$.post("Documents/AddNote", gnote, function(data) {
var msg = data.Msg;
$("#resultMsg").html(msg);
});
});
});
function getNotes() {
alert("I am in getNotes function");
var notes = $("#txtNote").val();
if (notes == "")
alert("notes is empty");
return (notes == "") ? null : { Note: notes };
}
</script>
My controller
[HttpPost]
public ActionResult AddNote(AdNote note)
{
string msg = string.Format("Note {0} added", note.Note);
return Json(new AdNote { Note = msg });
}
in the controller use
return Json(new AdNote { Note = msg },sonRequestBehavior.AllowGet);
I see two errors:
- var msg = data.Msg; should be var msg = data.Note;
- Use <%=Url.Action("AddNote","Documents")%> instead of "Documents\AddNote"

Resources