How can I change a url with asp.net mvc? - c#

I am wondering if this can be done easily. I am doing some paypal stuff where when the user returns from the paypal site they go to a "success page" that you set before you send the user to paypal.
So now I give my customers 2 choices. They can do a one time payment or they can do a recurring payment.
Now with paypal express you have to do a call after they come back to you success page to finish the payment.
The thing though is a one time payment requires different fields then a recurring payment and paypal does not make it that easy to tell what kind of payment was chosen. You either have to pass your own custom field in and then do a if statement checks later or try to do what I am doing.
So a sample of what is happening is this.
Customer: chooses recurring or one time payment
MyCode: sets up all needed variables for each one
- If one time payment is selected then success url would be Http://www.mysite.com/Success1
- If recurring is select then success url would be Http://www.mysite.com/Success2
Customer: Logs into paypal account and pays
PayPal: sends them to my success url either Success1 or Sucess2 method in my controller.
So this is how my success1 view would look like
public actionresult Success1()
{
// some paypal stuff
ViewData["NameOfPartialView"] = "Success1";
return View("Success");
}
So basically what I did was I made a view called "Success" and stuck a partial view that looks like this in it
<% Html.RenderPartial(ViewData["NameOfPartialView"].ToString()); %>
So my thinking was this if I have a view called "Success" and I tell the 2 other views methods to load that view up then I will get what I want.
this would split my code apart into 2 views but to the customer they will always just see one url http://www.mysite.com/Success instead of http://www.mysite.com/Success1 or http://www.mysite.com/Success2
But this does not work 100% yet. It loads up the right partial view and stuff but the url does not change. I would have thought that since I am calling a different view it would change the url to that view.
It does not seem to work that way.

It is not the View you are choosing that determines the url but the controller action that has been executed. So for example if the Succcess1 action has been executed, no matter what view you rendered inside, the user will see http://www.mysite.com/controller/Success1 in her browser.
I would suggest you the following urls:
If one time payment is selected success url would be http://www.mysite.com/SomeController/success/1
If recurring payment is selected success url would be http://www.mysite.com/SomeController/success/2
And your controller action might look like this:
public ActionResult Success(string id)
{
// Maybe perform some logic based on the id parameter
TempData["paymentType"] = id;
return RedirectToAction("Index");
}
public ActionResult Index()
{
ViewData["paymentType"] = TempData["paymentType"];
return View();
}

Related

How can I get an some entity ID from view?

Can I run these get and post actions with just single button? I get an id from my view then I just wanna run my post action.
I've already tried to getting id with parameter and put it in session but couldn't do it with one submit button
[HttpGet]
public ActionResult JoinEvent(int id)
{
Session["EventID"] = id;
return View();
}
[HttpPost]
public ActionResult JoinEvent(EventPerson eventPerson)
{
Person per =(Person) Session["Login"];
eventPerson.PersonID = per.PersonID;
eventPerson.EventID = (int)Session["EventID"];
eventPerson.TotalPerson = eventPerson.TotalPerson + 1;
eventManager.Join(eventPerson);
return View();
}
and this is my view
<td class="buy_link">
#Html.ActionLink("Join","JoinEvent","Join",item.EventID,null)
</td>
As what you posted here, it looks like the EventId can be set freely (without any security restrictions) by the client by simply navigating to GET /JoinEvent?id=100.
If security isn't an issue, you can do it by many many ways
Passing the event id to the client, which in turn will give it back to the server.
How ? Here is one way
Passing it as part of EventPerson model, which you will need to add a new property inside of that model.
ActionResult should: return View(new EventPerson { EventId: id })
Decorating view top row (after import statments) with the following #model EventPerson, this will tell the view that now he represents this type. (Which was passed by the GET action)
Now we should decide if we want to pass it back to the server as query string parameter or a body parameter ? Because we're doing a semantic "Insert" action, in most cases the practice asks us to pass it as part of the body. thus, you should use an hidden text box to pass EventId parameter.
#Html.Hidden("EventId", #model.EventId))
"EventId" is the property we want to bind on our POST server action EventPerson newly property. (Point#1)
#model.EventId is the value we took from the server and passed it into the View (Point#2)
Technically you cannot. When you create an anchor tag using action link, it creates a link to the specified URL. When you click on that link that fires a GET request. That will not POST data to the action method. If you need to POST data, you need to either submit a form or you can use Javascript/jQuery to do that. You can collect the data and send it as a POST using Js/jQuery. But just the anchor won't send a POST method.
Refer this

How should a refresh be handled after a redirect on ModelState failure using the strict PRG pattern?

So I've always used the loose PRG pattern where you return a view on ModelState validation failure from a POST action. However, it's always bothered me that I have to build the model not only in the GET action, but also rebuild it again in the POST action on failure. I've used different methods of doing the rebuilding using "view model builders" or just a private function within the controller that builds the view model for both actions, but these still bother me as well.
After reading this article by Ben Foster (http://benfoster.io/blog/automatic-modelstate-validation-in-aspnet-mvc), it makes a lot more sense to just rely on the GET action to build your view model - keeping it one area of the code - and then use the necessary action filters to save the ModelState for rendering when you are redirected back to GET on a failed POST.
So I've implemented that using the filters Ben mentions in his article like below. However, I am curious what happens if the user refreshes after they have been redirected back to the GET on a ModelState failure? How would I differentiate between someone accessing the GET directly versus a ModelState failure? Currently the ModelState would be gone if a user refreshes at that point. Is that the correct action, or should the user continue to see the errors until they POST with valid data? Essentially, should they be seeing the data that is in the database, or should they continue to see the changes they made when POSTing?
[ImportModelStateFromTempData]
public ActionResult Edit(int id)
{
// in a real application this would be retrieved from the db
var editView = new EditView()
{
UserId = id,
Name = "John Doe",
Age = 20,
Message = "Hello world"
};
return View(editView);
}
[HttpPost]
[ValidateModelState]
public ActionResult Edit(EditCommand editCommand)
{
// save to db here in real application
return RedirectToAction("Success");
}
I use the same [ImportModelStateFromTempData] filter in a couple projects, and it works great.
In my opinion, if the user refreshes, you shouldn't preserve any model state errors. The user is asking for a fresh view of that page, and it'd be frustrating to never be able to get a clean view. In same vain that refreshing after a POST shouldn't resubmit a form, refreshing after a GET shouldn't preserve a POST.

ASP.NET MVC 4 Make a page not directly reachable

I have a ASP.NET MVC 4 Blog which is 90% done but i need one thing - i have a webpage lets say index/secretPage but i want to be able to navigate to this webPage only after i am redirected from another - lets say index/redirect . If the adress is hardcoded it should not navigate, if the visitor is coming from a different link like blog/post/24 it should not be able to navigate too.
I hope my question was clear, than you for all help.
You could also mask the secret page with an action that shows another page if direct called.
In this example there are 2 actions. 'Secret' for returning a bogus view and the 'Check' for the real call. In this action the bool variable 'allowSecret' ist checked an then the user sees the view 'secret.cshtml' if allowed or 'index.cshtml' if not.
Here's an example code for a simple controller with that functionality:
using System.Web.Mvc;
namespace Test.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View("Index");
}
public ActionResult Check()
{
// check if user is allowed to show secret page
if(allowSecret == true)
return View("Secret");
// Otherwise return view 'index.cshtml'
return View();
}
public ActionResult Secret()
{
// Always shows view 'index.cshtml' if url is ".../secret"
return View("Index");
}
}
}
You could also redirect to another action after the check fails instead of calling a 'fake-view':
return RedirectToAction("Index")
The difference is the url the user sees in the browser. Returning a view does not change the url, redirecting to another action changes the url to the changed route.
Of course you can place the check in another class behind the controller.
Another option is to use the 'NonAction' attribute:
[NonAction]
public ActionResult Check()
{
...
}
Hope that helps with kind regards,
DD
You can UrlReferrer to get to know who refred to this current page and throw and exception or redirect back
HttpContext.Current.Request.UrlReferrer
http://msdn.microsoft.com/en-IN/library/system.web.httprequest.urlreferrer.aspx
But for what ever reason you need this. It dosenot look like a good design to me.
Hope this helps

From action to action to _Layout

I have a layout page that displays simple messages to the user.
I have a People Controller with an Index action/view that displays a list of people and I have an Edit action/view for a person.
When the user successfully updates and saves a person's details, I want to redirect from the Edit action to the Index page but display a message such as "The person was successfully updated".
I've tried adding things to the ViewBag but it doesn't persist.
I have a ViewModel Base class, but don't know if/how to set the Index's ViewModel from the Edit action.
How can I achieve this where messages are passed from various places to be used in _Layout?
You can use TempData to accomplish this. Values stored in TempData will only persist across one Http request, making it ideal to store these types of messages.
Edit POST Action
[HttpPost]
public ActionResult Edit(EditPersonModel model)
{
// .. Your code to edit the person ..
TempData["message"] = "The person has been updated.";
return RedirectToAction("Index", "People");
}
Your View
#if (TempData["message"] != null)
{
<p>#TempData["message"]</p> // Displays the message
}
You can build an entire notification system that uses TempData and ViewData (controller extensions, etc), but this is the simplest way to do it.

MVC Form Submit - Redirecting to an action that is not accessible from the browser directly

I am learning MVC 3 after hours right now and last night I ran into an issue that seems like it would be very simple to solve but I can't seem to locate a solution for it.
I have a simple contact form. The end user fills out the form and submits it. When they submit the form I redirect the end user to a different action in the same controller which renders an "inquiry submitted" page/view which is basically a "Thank you page".
The controller is setup like so.
public ActionResult ContactUs()
{
return View();
}
[HttpPost]
public ActionResult ContactUs(ContactInfo contactInfo)
{
if (!ModelState.IsValid)
{
return View();
}
//perform some business logic
return RedirectToAction("InquirySubmitted",contactInfo);
}
public ActionResult InquirySubmitted(ContactInfo contactInfo)
{
return View(contactInfo);
}
The problem:
I do not want end users navigating directly to the InquirySubmitted action via the browser.
I only want the ContactUs action in the controller to be able to send users to the InquirySubmitted View.
I have attempted to make the InquirySubmitted action private so that only the controller can call it like so:
private ActionResult InquirySubmitted(ContactInfo contactInfo)
But this produces an error which I fully understand because I am forcing the browser to request InquirySubmitted by using RedirectToAction().
So my question is simply: What is the best "MVC 3 style" solution to this issue.
You will need to put logic in your InquirySubmitted ActionResult in order to prevent users from viewing the page if they are not supposed to.
You are already passing the InquirySubmitted method your model (ContactInfo). Could you simply inspect the data passed to the method and if it is absent then redirect the user to an error page (or some other page of your choice)?
An alternate solution would be to set a boolean in session that indicates that the user completed the "ContactUs" form. Then you could check for that session object within InquirySubmitted.
First, I would have to say.. Who cares if someone can navigate directly to the Inquiry submitted page? Is there any confidential information, or something sensitive there? If not, so what? What does it hurt?
However, if you're determined to do so. The answer to your question of "How to make an action not accessible directly from the browser" is that You can simply use Html.Action() to render the page, and then decorate the action method with a [ChildActionOnly] attribute.
This doesn't actually solve the problem though, since making the action indirectly accessible only answers your question, not solves your problem. Ultimately, you need to redirect the user to a url to load the page, so you will need some logic that determines if they can view the page or not. This is
Not sure if this still applies in MVC3, but in MVC2 it worked.
your global.asax file has your url structuring in it. You can add your InquirySubmitted to the list of urls that isn't accessible there.

Categories