MVC3 Dynamic Return URL - asp.net-mvc-3

I want to have a View that has a link that will be set to the url of whatever page the user navigated to this view from.
Let's say I have a View and relative Action named InfoPage, and on this page I want a link that simply says 'Return'.
If the user is on PageA and navigates to InfoPage, clicking the 'Return' link returns the user to PageA.
If the user is on PageB and navigates to InfoPage, clicking the 'Return' link returns the user to PageB.
I'm thinking the easiest means to do this will be to add the 'ReturnUrl' as a property of the model used in InfoPage.
My question this is how do I get that return url.
public ViewResult InfoPage(){
var model = new InfoPageModel();
//Set my model's other properties here...
model.ReturnUrl = ''//Where do I get this?
return view(model);
}
And then in my view
Return

The most robust way to do this is to pass a query-string parameter to your page from the caller page. Every link to this page will be required to pass its own URL. (Request.Url).
You could also use Request.UrlReferrer in your control, but not all browsers send Referer headers.

To dynamically construct a returnUrl within any controller action:
var formCollection =
new FormCollection
{
new FormCollection(this.HttpContext.Request.Form),
new FormCollection(this.HttpContext.Request.QueryString)
};
var parameters = new RouteValueDictionary();
formCollection.AllKeys
.Select(k => new KeyValuePair<string, string>(k, formCollection[k])).ToList()
.ForEach(p => parameters.Add(p.Key, p.Value));
var returnUrl =
this.Url.Action(
this.RouteData.Values["action"].ToString(),
this.RouteData.Values["controller"].ToString(),
parameters
);
Related: How do I redirect to the previous action in ASP.NET MVC? (Same but from within any View)

Related

Post a form mvc3

I have a link and search button. Clicking on search button posts the page to a predefined action. Now clicking on a link should post the page to another action and should post all the values hidden varible values to another action. Can it be done.
Typically A link will generate an anchor tag and it usually gives you an HTTP GET request. Not the post request. You can supply parameters in your link which will be accepted as the parameters of the action method
#Html.ActionLink("Search","Search","Items",new { #id="nokia" },null);
This will generate a link with a querystring key called id with value nokia.
../Items/Search/nokia
or
../Items/Search?id=nokia
And your action method with id parameter can handle this GET request
public ActionResult Search(string id)
{
//Do whatever you want to do with the value in id. return a view with results
}
IF you really want to do an HTTPPost from a link, You can grab the click event of the link in the javascript and make an httppost call. The below script does that with jQuery library.
$(function(){
$("a").click(function(e){
e.preventDefault();
$.post($(this).attr("href"),function(result){
//do whatever with the results
});
});
});
But make sure you have an HttpPost version of the ActionMethod in your controller to handle this request
[HttpPost]
public ActionResult Search(string id)
{
//This is a POST request.Do whatever you want to do with the value in id. return a view with results
}
You can't use #Html.ActionLink for HTTP POST (edited: unless you use javascript function to submit the form via specifying onClick HtmlAttribute) . You can use submit buttons instead and style them as hyperlinks using jQuery. In this case you should be able to post your model with whatever values.
Alternatively you can use #Ajax.ActionLink and specify AjaxOptions { HttpMethod = "POST" }

mvc action name in url

UPDATE:
My model going into the save method is PartialViewModel, which in the save method, is pushed into the index's ContactViewModel and sent back. This wasn't clear.
I am playing around with MVC3, and have a contact controller with a SaveDetails action. The index cshtml has a partial with a form whose action is pointing to this controller.
When I submit the form not having completed it fully, thereby firing the validation, the url now contains the SaveDetails action name (http://localhost:7401/Contact/SaveDetails).
The form code is:
#using (Html.BeginForm("SaveDetails", "Contact")) {
...
}
The controller action looks like this:
public ActionResult SaveDetails(Models.PartialsViewModel pvm)
{
return View("Index", new ContactViewModel{ PartialsViewModel = pvm } );
}
What am I doing wrong?
The form has the action attribute set to SaveDetails action, so after submit it redirects the browser to this action.
I don' think you are doing anything wrong but I don't think you are able to do what you are tying to achieve. A request has to go somewhere and in mvc the url is used to identify which action you want to perform. If you are not submitting a post back then the url is going to change.
One way to submit the forms to different actions would be using some ajax.
Submitting the form is a POST. You can use an attribute to identify what request method an action should respond to. This means that you can create another action also called Index but give it the [HttpPost] attribute.
[HttpPost]
public ActionResult Index(Models.ContactViewModel cvm)
{
return View();
}
This way it won't display the action in the url.

Refreshing parent view when a partial view's form is submitted

I'm looking into using partial views in MVC3 using Razor, and I get my partial view to render and it works fine.
What I'd like to do, though, is refresh the parent view when the partial view is submitted.
Code in my parent view to render partial view
<div id="mydiv">
#{ Html.RenderAction("Add", "Request"); }
</div>
Action for parent view is simple,
public ActionResult Index()
{
List<obj> reqs = //some query
return View(reqs);
}
In my partial view's get action I have:
public ActionResult Add()
{
AddRequestViewModel vm = new AddRequestViewModel();
//set some stuff on the VM here
return PartialView(vm);
}
In the post action called by the partial view, if modelstate isn't valid, return PartialView(vm)
If it is valid, I'd like the parent and partial views to refresh.
I tried RedirectToAction, but this can't be called in an action called by a partial, apparently, and I tried return Index();, but this causes an issue with the code used to render the partial view,
Exception Details: System.InvalidOperationException: The model item passed into the dictionary is of type 'System.Collections.Generic.List'1[DatRequests.Models.ReqRequest]', but this dictionary requires a model item of type 'DatRequests.ViewModels.AddRequestViewModel'.
Any suggestions on how to do this would be appreciated. The purpose of the page is to show a list of elements, and the partial contains a form to add a new element to the list.
Edit: The partial's model is different, as it contains data for selection, which is from a db, which is why I tried RenderAction, but I'm not sure if there are other ways of doing this.
When the partial view is submitted normally you submit it to some controller action. You could either submit it using a normal request or an AJAX request. If you use a normal request you could perform a standard redirect to the Index inside the POST controller action that will handle the form submission. If you use AJAX, you could return a JSON result pointing to the url that you want to redirect:
[HttpPost]
public ActionResult Foo(MyViewModel model)
{
if (!ModelState.IsValid)
{
return PartialView(model);
}
return Json(new { url = Url.Action("Index") });
}
and inside your AJAX success callback:
success: function(result) {
if (result.url) {
// we have a success
window.location.href = result.url;
} else {
// invalid modelstate => refresh the partial
$('#mydiv').html(result);
}
}
Probably RenderAction should not be used this way.
When using Html.RenderAction, a new/seperate request would be sent to the server. And you got another chance to load some data from db or somewhere else to display to the client. Also, you could apply OutputCache to this action. this is usually the way doing global cache.
Here you are doing a POST to the server. Either directly put a element here or using a partial view to do the Post. And in the corresponding action, do a RedirectToAction.
Do it with ajax or not isn't the point. my opinion is more about the right way using RenderAction

How do you perform a 302 redirect using MVC3 and ASP.Net?

I'm using Razor, HTML5, MVC3 with C# in an application, where after a user clicks on a link, I open a new window, do some processing, then want to redirect that window with a 302 status code to a link.
Thanks.
The correct way to do this in ASP.NET MVC is by having a controller action that returns a redirect ActionResult. So inside the controller action you are invoking in this window and doing the processing simply perform a redirect by returning a proper ActionResult:
public ActionResult Foo()
{
// ... some processing
return RedirectToAction("SomeAction", "SomeController");
}
When the Foo action is invoked (presumably inside the new window) it will do the processing and return a 302 HTTP status code to the client with a new location of /SomeController/SomeAction.
If you wanted to redirect to some external url of your application you could do the following:
public ActionResult Foo()
{
// ... some processing
return Redirect("http://someotherdomain.com/somescript");
}
As far as creating a link that will open in a new window/tab is concerned you could append the target="_blank" attribute on the anchor:
#Html.ActionLink(
"Some link", // linkText
"Foo", // action
"SomeController", // controller
null, // routeValues
new { target = "_blank" } // htmlAttributes
)

Need Help with MVC workflow - post to another server?

I have a form where I'm collecting contact information (name, address, etc) for an e-commerce app. When the user clicks the Buy button, I want to parse the form, grab a couple of values, and generate a crypto fingerprint.
Then I want to take all of the form values (name, address, etc) from the posted form and redirect that to a new server with the same form values. I may need to inject a few new ones behind the scenes.
No problem capturing the info after the click. I'm just using a Buy action on my controller. The part I can't figure out is posting to the other server with the desired parameters.
[HttpPost]
public ActionResult Buy(BuyModel model)
{
var fingerprint = GenerateFingerprint(.....);
return Redirect("https://some.other.url.com/");
}
EDIT: To clarify. I don't need to just post the data, I actually need to display the response in the browser.
You can use the HttpWebRequest class to make the post to the other server and then redirect as normal.
var httpRequest = (HttpWebRequest)WebRequest.Create("http://example.com/mypage/;
httpRequest.Method = "POST";
httpRequest.ContentType = "application/x-www-form-urlencoded";
For the post data, you'll want to convert this to a byte[] and then add it to a stream.
string postData = "key=value&key2=value2";
byte[] dataArray = Encoding.UTF8.GetBytes(postData);
You'll then be able to set the ContentLength of the request correctly:
httpRequest.ContentLength = dataArray.Length;
Write this to a Stream and we're good to do the post:
using(Stream requestStream = httpRequest.GetRequestStream())
{
requestStream.Write(dataArray, 0, dataArray.Length);
var webResponse = (HttpWebResponse)httpRequest.GetResponse();
}
Assuming you don't need the user to be redirected to the posted page, this will work well. You can look in the webResponse object to find out what happened with the post if that's useful to you.
OK guys, here is what I ended up doing, and it works great.
For the purposes of this discussion, there are 2 views. Membership collects info from the user, and Buy executes the post to the 3rd-party server.
When the user clicks the OK button on the Membership view, the first block of code below gets executed - the HttpPost on the Membership action. Here the info is manipulated if necessary and then saved in TempData so that it can be passed to the Buy view. It then redirects control to the Buy view.
In the Buy action, the second block of code below gets executed. Here, I can grab the stuff collected in Membership and do whatever I need to do. Then copy all of the form data into ViewData[] values. You could use the model instead of ViewData, but I have other stuff in the model. Probably could have combined them, but this works.
Finally, in the Buy view, set all of the form field values using the ViewData values. The key, though is using the jQuery .ready handler so that the form gets submitted automatically when the page is done loading.
So this basically copies the form values from Membership view into the Buy view, adds a few new values and then posts it all to a 3rd-party URL.
BuyController.cs
[HttpPost]
public ActionResult Membership(OtherModel model)
{
// do some stuff with the model
// ...
TempData["OtherModel"] = model;
return RedirectToAction("Buy");
}
...
public ActionResult Buy()
{
// do some stuff to generate crypto key
var fingerprint = DoSomeStuff();
ViewData["x_fp_hash"] = fingerprint;
var otherstuff = (OtherModel) TempData["OtherModel"];
ViewData["x_login"] = otherstuff.login;
return View(model);
}
Buy.cshtml
#{
Layout = "~/Views/Shared/_LayoutBlank.cshtml";
ViewBag.Title = "Validating ...";
}
<script type='text/javascript'>
$(function () {
$("form#simForm").submit();
});
</script>
#model My.Namespace.MyModel
<form id="simForm" runat="server" method='post' action='https://xxxxxx.net/url/transact.dll'>
<input type='hidden' runat="server" name='x_login' id='x_login' value="#ViewData["x_login"]" />
<input type='hidden' runat="server" name='x_fp_hash' id='x_fp_hash'value="#ViewData["x_fp_hash"]"/>
etc.
</form>
The only way you can post the values to another url and redirect in the same post (that is, return the user to the resulting page of the post) is to actually make the users browser do the post. You're also doing the post to an HTTPS, so the whole point is to prevent man-in-the-middle kinds of things.
The only real way to do this is to return a new form to the user, and programmatically tell the browser to submit it using javascript with the new values.

Resources