Ajax.ActionLink failing - asp.net-mvc-3

Right now I'm learning about MVC's implementation of Ajax and I'm having trouble getting it to work correctly. Here's what I have:
#Ajax.ActionLink("Click here to get a title", "Yo",
new AjaxOptions { OnSuccess = "alert(\"YES!\")", OnFailure = "alert(\"WHY?!\")" })
And here are the two controller methods:
public PartialViewResult GetThatTitle()
{
var titular = new TitleDataEntity { };
titular.TitleName = "Inception!";
titular.PublishDate = DateTime.Now;
titular.Id = 2;
return PartialView("_testView", titular);
}
public JsonResult Yo()
{
var titular = new TitleDataEntity { };
titular.TitleName = "Inception!";
titular.PublishDate = DateTime.Now;
titular.Id = 2;
if(Request.IsAjaxRequest())
{
return Json(titular);
}
return Json(titular);
}
When I call the function "Yo", the browser gives me the "WHY?!" alert box. But when I call GetThatTitle, it gives me the success alert. Why is it failing when I try and return a Json result?

You need to allow GET requests like this when returning JSON which are disabled by default:
return Json(titular, JsonRequestBehavior.AllowGet);
Also I would strongly recommend you using FireBug. It shows all AJAX requests in its console and you see the requests and responses. If you have used it you would have seen the following:
InvalidOperationException: This
request has been blocked because
sensitive information could be
disclosed to third party web sites
when this is used in a GET request. To
allow GET requests, set
JsonRequestBehavior to AllowGet.]
which would have put you on the right track of course.

Related

What is the difference between a Web application and Web API?

It sounds naive on hearing but how different are web application and Web API when someone mentions it in their paper?
How different are they from their functionalities?
Very short: web application, it's a web site, which you see in your browser, and web api, it's a service, which you use in a web application.
See also Difference between ASP.NET MVC and ASP.NET Web API:
Asp.Net MVC is used to create web applications that returns both views
and data but Asp.Net Web API is used to create full blown HTTP
services with easy and simple way that returns only data not view.
Web Applications are meant for their human interactions through views whereas Web API aka Web Services are meant for system-to-system interactions (information exchange programatically ). They exchange data.
Web Application:
It is an end-to-end solution for a user. Which means, User can:
Open it using a browser
Interact with it. He can click on something and after some processing, its result will be reflected in the browser screen. Human-System interaction
Web API
With Web APIs alone, a user can not interact with it, because it only returns data, not views.
It is a system which interacts with another system
It does not return views, it returns data
It has an endpoint set, which can be hit by other systems to get data which it provides.
Explanation using an ANALOGY
Web Application:
Suppose we have a cook. We can ask him to cook us anything, anytime!Suppose we ask our cook to cook us a burger. He'll process our request and will provide us a burger. ( This is like a Web Application; a complete solution. )
Web API
Now if we ask him to make us 'McDonalds' burger, can he cook and bring us that? No!
Here comes the concept of APIs! (for this example, lets suppose McDonalds only give takeaways to cooks only)
McDonalds-Takeaways is like an API. Which allows other systems (cooks) to hit it and bring back desired data. So we can ask our solution (our cook) to
go to McDonalds Takeaway
Buy the burger and bring us that
So what happened is, we asked our "System" to talk to this McDonalds-takeaways (API System) and bring back the result we desired.
Web app is a website that is running in your browser and web Api is service
Web API is back-end application(server side) where actual functionality to call service/database call is happening to store and retrieve the data.
Web Application is front end application(client side)which is calling web API to present the data retrieved from back-end.
eg: To check the account balance in your mobile banking app, you are able to see your account details in front end. But all the calculations of interest /balance is happening in the back end.
In simplest word, a web application's response to requests are html, css, javascript and anything that a browser can render (graphical), whereas a web api returns non-graphical "data". Having said that, I think we can make a web api work like a web application because html is still data.
Create :
public IHttpActionResult GetAllProduct()
{
IList<product> pro = null;
using (var ctx = new TestMVCEntities())
{
pro = ctx.products.ToList();
}
if (pro.Count == 0)
{
return NotFound();
}
return Ok(pro);
}
public IHttpActionResult PostNewProduct(product pro)
{
using (var ctx = new TestMVCEntities())
{
ctx.InUPProduct(pro.pid,pro.pname,pro.pprice);
ctx.SaveChanges();
}
return Ok();
}
public IHttpActionResult PutOldProduct(product pro)
{
using (var ctx = new TestMVCEntities())
{
product c = (from x in ctx.products
where x.pid == pro.pid
select x).First();
if (c != null)
{
c.pname = pro.pname;
c.pprice = pro.pprice;
ctx.SaveChanges();
}
else
{
return NotFound();
}
}
return Ok();
}
public IHttpActionResult Delete(int id)
{
using (var ctx = new TestMVCEntities())
{
var pro = ctx.products
.Where(s => s.pid == id)
.FirstOrDefault();
ctx.Entry(pro).State = System.Data.Entity.EntityState.Deleted;
ctx.SaveChanges();
}
return Ok();
}
Consume :
public JsonResult GetProductsData()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:44350/api/");
//HTTP GET
var responseTask = client.GetAsync("product");
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsAsync<IList<product>>();
readTask.Wait();
var alldata = readTask.Result;
var rsproduct = from x in alldata
select new[]
{
Convert.ToString(x.pid),
Convert.ToString(x.pname),
Convert.ToString(x.pprice),
Convert.ToString(x.pimage),
Convert.ToString(x.pisdemand),
Convert.ToString(x.pcname),
Convert.ToString(x.psupply)
};
return Json(new
{
aaData = rsproduct
},
JsonRequestBehavior.AllowGet);
}
else //web api sent error response
{
var pro = Enumerable.Empty<product>();
return Json(new
{
aaData = pro
},
JsonRequestBehavior.AllowGet);
}
}
}
public JsonResult InupProduct(string id,string pname, string pprice)
{
try
{
product obj = new product
{
pid = Convert.ToInt32(id),
pname = pname,
pprice = Convert.ToDecimal(pprice)
};
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:44350/api/product");
//HTTP POST
var postTask = client.PostAsJsonAsync<product>("product", obj);
postTask.Wait();
var result = postTask.Result;
if (result.IsSuccessStatusCode)
{
return Json(1, JsonRequestBehavior.AllowGet);
}
else
{
return Json(0, JsonRequestBehavior.AllowGet);
}
}
/*context.InUPProduct(Convert.ToInt32(id),pname,Convert.ToDecimal(pprice));
return Json(1, JsonRequestBehavior.AllowGet);*/
}
catch (Exception ex)
{
return Json(0, JsonRequestBehavior.AllowGet);
}
}
public JsonResult deleteRecord(int ID)
{
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:44350/api/product");
//HTTP DELETE
var deleteTask = client.DeleteAsync("product/" + ID);
deleteTask.Wait();
var result = deleteTask.Result;
if (result.IsSuccessStatusCode)
{
return Json(1, JsonRequestBehavior.AllowGet);
}
else
{
return Json(0, JsonRequestBehavior.AllowGet);
}
}
/* var data = context.products.Where(x => x.pid == ID).FirstOrDefault();
context.products.Remove(data);
context.SaveChanges();
return Json(1, JsonRequestBehavior.AllowGet);*/
}
catch (Exception ex)
{
return Json(0, JsonRequestBehavior.AllowGet);
}
}

Web API Routing changes after DB Refresh

This is an exceedingly strange problem and I'm only including it as an oddity in case someone's seen it before.
I am on C#, ASP.Net web api, Fluent Nhibernate and SQL Server 2012
I have a Meal object that I pass to a web api controller method :
[HttpPost]
[ActionName("PostMeal")]
public HttpResponseMessage PostMeal(Meal mealToPost)
{
var helper = new Datahelper();
try
{
var fshelper = new FoodServiceHelper(helper);
fshelper.SaveMeal(mealToPost);
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (Exception ex)
{
var response = Request.CreateErrorResponse(HttpStatusCode.NotAcceptable, ex);
return response;
}
}
The method that posts is similarly very simple:
public void SaveMeal(Meal mealToPost)
{
var url = _connectionString + "food/PostMeal";
var client = new HttpClient();
var formatter = new JsonMediaTypeFormatter
{
SerializerSettings = { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }
};
var clientTask = client.PostAsync(url, mealToPost, formatter)
.ContinueWith(posttask => posttask.Result.EnsureSuccessStatusCode());
clientTask.wait();
}
The meal object is pretty straightforward. Some elementaries, some generic collections.
I am able to post normally for several (usually about 20) meals, at which point it is somehow no longer able to find the route. I verified this by adding breakpoints to the server side. It hits it the first several times, then it won't even find the controller method.
Here's the weird part. If I drop and recreate the database, it's ok again for another 20 or so meals.
I suspect that this is some kind of mapping issue, but I cannot prove it.
Curious.

httpmessagehandler - reading content

I created a message handler which will log the request and the response. ideally I want to
public class LoggingMessageHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
LogRequest(request);
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
var response = task.Result;
LogResponse(response);
return response;
});
}
private void LogRequest(HttpRequestMessage request)
{
var writer = request.GetConfiguration().Services.GetTraceWriter();
var content = request.Content;
(content ?? new StringContent("")).ReadAsStringAsync().ContinueWith(x =>
{
writer.Trace(request, "request", System.Web.Http.Tracing.TraceLevel.Info, t =>
{
t.Message = x.Result;
});
});
}
private void LogResponse(HttpResponseMessage response)
{
var request = response.RequestMessage;
var writer = request.GetConfiguration().Services.GetTraceWriter();
var content = response.Content;
(content ?? new StringContent("")).ReadAsStringAsync().ContinueWith(x =>
{
writer.Trace(request, "response", System.Web.Http.Tracing.TraceLevel.Info, t =>
{
t.Status = response.StatusCode;
t.Message = x.Result;
});
});
}
}
and here is my client code.
public ActionResult Index()
{
var profile = Client.GetAsync("Vendor").Result.EnsureSuccessStatusCode().Content.ReadAsAsync<VendorProfileModel>().Result;
return View(profile);
}
Logging appears to be working. However, when this handler is registered my client code returns an empty object. If I remove this handler the model is successfully read from the response and displayed on screen.
Is there a way to read the content and display the results on the client?
after a few more days for digging around on the net I finally found the root problem and a solution. First the problem:
everything in webapi is async
my action uses Controller.User which in turn is calling Thread.CurrentPrinciple
I am using ITraceWriter as my logging abstraction
apparently there is a bug in the ITraceWriter mechanicism where the current profile is not propagated across threads. therefore, i loose the principle when i get to my controller action. therefore, my query returns an empty result, rather than a fully populated result.
solution: don't use ITraceWriter to log messages. It would have been nice to use the built in mechanics, but that doesn't work. here is the link to the same issue which provides more detail/context.
https://aspnetwebstack.codeplex.com/workitem/237

Dissabling Session in MVC3 to allow multiple AJAX calls

When doing multiple simultaneous Ajax calls cause my MVC web application to block. I have been reading and I found two topics with the same problem
Why would multiple simultaneous AJAX calls to the same ASP.NET MVC action cause the browser to block?
Asynchronous Controller is blocking requests in ASP.NET MVC through jQuery
The solution for them is disabling the session using ControllerSessionStateAttribute .I have try using the attribute but my code is still blocking. You can reproduce the problem creating a new MVC3 web application with the following code
#{
ViewBag.Title = "Home Page";
}
<h2>#ViewBag.Message</h2>
<p>
Example of error when calling multiple AJAX, try click quickly on both buttons until the server get blocked.
</p>
<button onclick="cuelga++;SetCallWaiting(cuelga);">Set a call waiting in the server</button><button onclick="libera++;ReleaseEveryone(libera);">Release all calls in the server</button>
<div id="text"></div>
<script type="text/javascript">
var cuelga = 0;
var libera =0;
function ReleaseEveryone(number) {
var url = "/Home/ReleaseEveryone/";
$.post(url, { "id": number },
ShowResult1, "json");
};
function SetCallWaiting(number) {
var url = "/Home/SetACallWaiting/";
$.post(url, { "id": number },
ShowResult, "json");
};
function ShowResult (data) {
$("#text").append(' [The call waiting number ' + data[0] + ' come back ] ');
/* When we come back we also add a new extra call waiting with number 1000 to make it diferent */
SetCallWaiting(1000);
};
function ShowResult1(data) {
$("#text").append(' [The release call number ' + data[0] + ' come back ] ');
};
</script>
and this is the HomeController
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Threading;
using System.Web.SessionState;
namespace ErrorExample.Controllers
{
[SessionState(SessionStateBehavior.Disabled)]
public class HomeController : Controller
{
private static List<EventWaitHandle> m_pool = new List<EventWaitHandle>();
private static object myLock = new object();
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
[HttpPost]
public JsonResult SetACallWaiting()
{
EventWaitHandle myeve;
lock (myLock)
{
myeve = new EventWaitHandle(false, EventResetMode.ManualReset);
m_pool.Add(myeve);
}
myeve.WaitOne();
var topic = HttpContext.Request.Form[0];
return Json(new object[] { topic });
}
[HttpPost]
public JsonResult ReleaseEveryone()
{
try
{
lock (myLock)
{
foreach (var eventWaitHandle in m_pool)
{
eventWaitHandle.Set();
}
m_pool.Clear();
}
var topic = HttpContext.Request.Form[0];
return Json(new object[] { topic });
}
catch ( Exception )
{
return Json( new object[] { "Error" } );
}
}
}
}
Thank you very much in advance.
I don't think this issue is actually related to the SessionState at all.
Every browser makes only a limited number of concurrent requests to a domain - see this article for details.
I think the issue is caused by the fact that if you start multiple "SetACallWaiting" requests, you run into the situation where the browser won't even send the request to the server until the previous requests are unanswered - so the request to "ReleaseEveryone" is not sent by the browser. Therefore, you get the locking behaviour.
Also there may be an issue with the code sample you posted - the "SetCallWaiting(1000);" line in the function ShowResult. With this call, you won't actually reduce the number of waiting requests, as each time a request is released, it will be recreated again (I'm not sure if this is what you wanted or not).
To test this behaviour, look at the requests being sent by the browser and set breakpoints in the controller's actions, and see how it behaves for various umber of requests.

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