Good practice when ajax and static page use same partialView? - ajax

When user comes on my site by url, controller generates _Layout + partialView inside.
Then when user walks by site, controller generates same partialView and sends by ajax.
Is there a good solution for this case?
P.S.: My workaround:
public class HomeController : Controller
{
public ActionResult Index(int id, bool ajax = false)
{
var model = GetModelById(id);
return ajax ? (ActionResult) PartialView(model) : View(model);
}
}
Where /Home/Index/42 - for static page and /Home/Index/42?ajax=true - for ajax.

To identify ajax request:
var isAjax = Request.IsAjaxRequest()
Or alternative in Asp.Net Core:
var isAjax = Request.Headers["X-Requested-With"] == "XMLHttpRequest";

Related

Model is null on postback in ajax loaded partial view

I'm using the following pattern https://github.com/filamentgroup/Ajax-Include-Pattern
to load partial views through ajax.
View:
#using(Html.BeginUmbracoForm("PostContactInformation", "JoiningSurface", null, new Dictionary<string, object> { { "class", "joinform" } })) {
#Html.AntiForgeryToken()
<div data-append="#Url.Action("RenderJoiningContactInformation", "JoiningSurface", new { ContentId = CurrentPage.Id })"></div>
}
With Action:
public ActionResult RenderContactInformation(int ContentId)
{
var viewModel = ContactViewModel();
viewModel.Content = Umbraco.TypedContent(ContentId);
return PartialView("RenderContactInformation", viewModel);
}
Loads partial view perfectly.
// No need to add partial view i think
Post action works correctly as well:
public ActionResult PostContactInformation(ContactViewModel model)
{
//code here
return RedirectToUmbracoPage(pageid);
}
The problem is, that i need to add model error to CurrentUmbracoPage if it exists in post...
For example:
public ActionResult PostContactInformation(ContactViewModel model)
{
ModelState.AddModelError(string.Empty, "Error occurred");
return CurrentUmbracoPage();
}
In this case i get null values for current model. And this happens only when i use ajax.
If i load action synchronously like that:
#using(Html.BeginUmbracoForm("PostJoiningContactInformation", "JoiningSurface", null, new Dictionary<string, object> { { "class", "joinform" } })) {
#Html.AntiForgeryToken()
#Html.Action("RenderContactInformation", "JoiningSurface", new { ContentId = CurrentPage.Id })
}
everything works like it should.
But i need to use ajax. Is there a correct way to pass values on postback in this case? I know that i can use TempData, but i'm not sure that this is the best approach.
Thanks for your patience
The problem is that Umbraco context is not accessible when you're trying to reach it through ajax call. Those calls are a little bit different.
Check my answer in this thread: Umbraco route definition-ajax form and I suggest to go with WebAPI and UmbracoApiControllers to be able to access those values during the Ajax call.

Redirection in ASP.NET MVC to a controller method after the post AJAX call

Present set up -
url: configMap.sitePath + "api/Quiz/" + quizResponse.quizId,
data: JSON.stringify(quizResponse),
success: function (data) {
// Display the data on UI
}
the above post is to a .NET api controller which returns quizResponse.
[ValidateModelState, HttpPost]
public HttpResponseMessage Post(int id, QuizResponse value)
Now as per a new requirement , Once the api has the response I should redirect to another page(a MVC controller and then view). How do I achieve this -
public ActionResult ScptResult()
{
return View();
}
I also thought of redirect after ajax success but not sure if its a correct way.
Also, How do I pass data to the scptresult controller method after post?
In the success function of your JQuery, redirect the page to your MVC page:
window.location = '{controller}/{action}/{quizid}';
Assuming you use the standard MVC routing, and assuming you change your ScptResult function to accept a parameter, so you can pass data to it.
public ActionResult ScptResult(int quizid)
{
return View();
}
Use in java-script after complete your work in success method or anywhere
window.location = '#Url.Action("ScptResult", "YourControllerName")';
I should warn, I'm not very familiar with ajax responses but hopefully this gets close.
API
[ValidateModelState, HttpPost]
public HttpResponseMessage Post(int id, QuizResponse value)
{
//Other stuff...
var redirect = string.Format(#"/controller/ScptResult/{0}", id);
return new HttpResponseMessage()
{
Content = new StringContent(redirect)
};
}
Javascript
url: configMap.sitePath + "api/Quiz/" + quizResponse.quizId,
data: JSON.stringify(quizResponse),
success: function (data) {
window.location = data;
}
Controller
// This follows the default route.
// "/{controller}/{action}/{id}" where id is optional.
// window.location = "/ControllerName/ScptResult/OtherData"
public ActionResult ScptResult(string id)
{
return View();
}

Post selected dropdown to controller MVC using AJAX or other way

I need to post the selected item's id from dropdown to post method of same controller and the render the content in same view
PLease help me fast :(
My View
#Html.DropDownListFor(x => x.hotelmodel.SelectedHotelId, Model.hotelmodel.DrpHotelList)
<div>
<p>#Model.percentageoccupied</p>
<p>#Model.Revenue</p>
<p>#Model.UnSold</p>
<div>
MY HttpGetMethod
[HttpGet]
public ActionResult Counter()
{
var personid = ((PersonModel)Session["PersonSession"]).PersonId;
var model = new CounterforModel();
model.hotelmodel.DrpHotelList = iCommonservice.GetHotelListByPersonId(personid);
return View(model);
}
My HttpPostMethod
[HttpPost]
public ActionResult Counter(int id)
{
var result = iCommonservice.LoadCounter(id);
model.percentageoccupied = Convert.ToInt32(result[0].percentageoccupied);
model.Revenue = Convert.ToDecimal(result[0].Revenue);
model.UnSold = Convert.ToInt32(result[0].UnSold);
return View(model);
}
In your POST method pass your View Model as parameter. Then you can use the posted hotelModel.SelectedHotelId for what you need, update the model values and pass your updated model to the View.
public ActionResult Counter(CounterforModel model)
{
var result = iCommonservice.LoadCounter(model.hotelmodel.SelectedHotelId);
model.percentageoccupied = Convert.ToInt32(result[0].percentageoccupied);
model.Revenue = Convert.ToDecimal(result[0].Revenue);
model.UnSold = Convert.ToInt32(result[0].UnSold);
return View(model);
}
If you want, you can use #Ajax.BeginForm() or jQuery .ajax() to make an ajax call to your action. You can look here.

POSTing to my ASP.NET MVC app from 3rd party site

I'm testing a payment provider (SagePay) and as part of a process, their server POSTs to my site and expects a response. I can't get this to work using MVC.
I set up a classic asp test reponse page and added it to my MVC app:
<%
Response.Buffer = True
response.Clear()
response.contenttype="text/plain"
response.write "Status=OK" & vbCRLF
response.write "RedirectURL=http://www.redirectsomewhere.co.uk" & vbCRLF
response.End()
%>
This work fine.
However, when I try to do the same with MVC, it doesn't work:
Controller:
[HttpPost]
public ActionResult TestCallback()
{
return View();
}
View:
#{
Response.Buffer = true;
Response.Clear();
Response.ContentType = "text/plain";
Response.Write("Status=OK" + System.Environment.NewLine);
Response.Write("RedirectURL=http://www.redirectsomewhere.co.uk" + System.Environment.NewLine);
Response.End();
}
The error message is a generic error from the payment provider so is no real help, but I have narrowed the error down to the point at which the page renders.
I can browse to both pages fine (i need remove the HttpPost attribute from the MVC controller method for this), and both pages display identical data.
This is the MVC url that the payment provider is POSTing to:
http://myipaddress/CA_UAT/Token/TestCallback
This is the classic asp URL that works fine:
http://myipaddress/CA_UAT/Token/TestCallback.asp
I created a 'Token' directory for the asp page so the urls would match for testing purposes.
What am I doing wrong?
UPDATE
In response to Hari's comment, I installed a Firefox plugin called 'Header Spy' which gives me this information:
Response HTTP/1.1 200 OK
Source: Response
HttpHeader:Server
Request:User-Agent Cookie
Response:Response Date Set-Cookie
Both pages show the same info.
You don't need to return an action result in order to send just plain text back to the screen. The simplest way of accomplishing this is to return a string value. Replace the code in your controller with what is below.
[HttpPost]
public string TestCallback()
{
string result = "Status=OK";
result += System.Environment.NewLine;
result += "RedirectURL=http://www.redirectsomewhere.co.uk";
result += System.Environment.NewLine;
return result;
}
This will return no other response that what you have in the string. By using an ActionResult and View you are likely returning markup from the master view.
Instead of writing the response in the view, I would write it in the action method like this:
[HttpPost]
public ActionResult TestCallback()
{
Response.Buffer = true;
Response.Clear();
Response.ContentType = "text/plain";
Response.Write("Status=OK" + System.Environment.NewLine);
Response.Write("RedirectURL=http://www.redirectsomewhere.co.uk" + System.Environment.NewLine);
Response.Flush();
return new EmptyResult();
}
When returning EmptyResult you will ensure that MVC doesn't append anything to the response.
Try like this:
[HttpPost]
public ActionResult TestCallback()
{
var sb = new StringBuilder();
sb.AppendLine("Status=OK");
sb.AppendLine("RedirectURL=http://www.redirectsomewhere.co.uk");
return Content(sb.ToString(), "text/plain");
}
or in a more MVCish way:
View model:
public class ResponseViewModel
{
public string Status { get; set; }
public string RedirectUrl { get; set; }
}
and then a custom action result:
public class StatusActionResult : ContentResult
{
private readonly ResponseModel _model;
public StatusActionResult(ResponseModel model)
{
_model = model;
}
public override void ExecuteResult(ControllerContext context)
{
var response = context.HttpContext.Response;
response.ContentType = "text/plain";
response.Write(string.Format("Status={0}{1}", _model.Status, Environment.NewLine));
response.Write(string.Format("RedirectURL={0}", _model.RedirectUrl));
}
}
and finally your controller action:
[HttpPost]
public ActionResult TestCallback()
{
var model = new ResponseModel
{
Status = "OK",
RedirectUrl = "http://www.redirectsomewhere.co.uk"
};
return new StatusActionResult(model);
}
I wonder if sagepay is expecting a file extension..ie doing some kind of URL validation on heir side. Do you know if your Action is being invoked?
Also try adding a route that makes your mvc URL look like "TestCallback.asp".

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