I have a link which uses a query string, and it's always going to the [HttpPost] method instead of the [HttpGet] method.
The error I'm getting is a NullReferenceException on TempData["surveytype"], so I know it's going to Post instead of Get. I have no idea why though.
I found a couple of similar questions here but nothing that resolved my problem. I thought maybe MVC was interpreting this as a form submission and sending to HttpPost because I'm styling the link as a "btn btn-primary" class, but removing that changed nothing.
My link:
Start Response
Controller:
[HttpGet]
public ActionResult Create(int SurveyId)
{
TempData["SurveyId"] = SurveyId;
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = ...)] Response response)
{
if (ModelState.IsValid)
{
response.Userid = User.Identity.GetUserId();
response.Id = Guid.NewGuid();
db.response.Add(response);
db.SaveChanges();
TempData["ResponseId"] = response.Id;
int? surveyid = response.SurveyId;
var surveytype = db.surveys.Find(surveyid).surveytype;
TempData["surveytype"] = surveytype;
...
}
}
I think your [HttpGet] makes the routing to confuse - Remove it
public ActionResult Create(int SurveyId)
{
TempData["SurveyId"] = SurveyId;
return View();
}
Now based on the action name the route will take care either it's a get or post - just a solution try it out and let me know if you face same issue
Thanks - Happy coding :)
For starters, you shouldn't manually create links like that. It's very error prone, and you run into situations like this often. You should use the helpers to create the link. For example:
#Html.ActionLink("Start Responses", "Create", "Controllername", new { SurveyId = item.id }, new { #class = "btn btn-primary" })
Next, you should make sure you don't have any custom routing that might be interfering, either with Attribute based routing or by using MapRoute.
The biggest reason to use the helpers is that MVC can access pages from different actual url's, and hard coding a path like that is almost impossible to get right.
For instance, let's say you can access a page at http://exmample.com, http://example.com/Home or http://example.com/Home/Index.
Using a hard coded "../whatever" means that this will translate to the corresponding url's http://example.com/../whatever (obviously not what you want), http://example.com/Home/../Whatever (might be what you want, might not), or http://example.com/Home/Index/../Whatever (this is probably what you want want, but it won't get there unless the users's browser url has this third url to access it).
If you must hard code a link, then you should always use a full root relative url link ("/Home/whatever") instead of relative ("../whatever"), but even that has issues.. what happens if you decide to move your site into a subdirectory of your site, now all your hard coded urls are wrong.
Using Url helpers is always the best way to go in MVC.
Related
I am trying to add a single link in my navbar that redirects the user to a different webpage depending on their account type. I am having issues doing this and could use some help.
The Controller code that I am calling looks like this:
public IActionResult Attendance(char accountType)
{
if (accountType.Equals("m") || accountType.Equals("a"))
{
return RedirectToAction("FacultyAttendance");
}
else
{
return RedirectToAction("StudentAttendance");
}
}
public IActionResult StudentAttendance()
{
// More functionality will be added later
return View();
}
public IActionResult FacultyAttendance()
{
// More functionality will be added later
return View();
}
Following this answer for calling the Controller method, I have this code snippet in the View file:
Attendance
This gives me the following error:
Bad Request - Invalid URL
HTTP Error 400. The request URL is invalid.
I also tried following this answer by removing the <%: and %>.
Attendance
If I do this, I just get blank webpage.
My first problem lies in which style I should use for this method call within the View file. Are either of these correct, or should I use something else entirely? Might the issue be with the way I have the Controller code set up?
Any help would be appreciated, as I am new to the MVC framework for ASP.NET.
Edit: The solution I found is a bit different than what I originally posted. I used this tag in my View and got it to work:
<a asp-controller="Home" asp-action="Attendance" asp-route-accountType='s'>Attendance</a>
I also followed ThisGuy's suggestions for improving the code since I had mismanaged some variables and that may have been part of the problem.
accountType is a char, but you are passing a string:
new {accountType = "m"}
Change the Controller to take a string instead of char for accountType.
public IActionResult Attendance(string accountType)
Also, I'd write it like this:
public IActionResult Attendance(string accountType) =>
RedirectToAction(
accountType.Equals("m") ||
accountType.Equals("a")
? nameof(FacultyAttendance)
: nameof(StudentAttendance));
I'm using default route configuration of domain/controller/action.
For instance, www.domain/Appointment/RequestAppointment calls the Appointment controller and RequestAppointment ActionResult.
I've been often asked by our Marketing folks to build redirects for shorter versions of our URLs for use in social media or on billboards.
So, they would like to see www.domain/Appointment/RequestAppointment as www.domain/appointment.
I don't have an issue doing that, in this case I am just:
//Appointment Controller
public ActionResult Index()
{
return RedirectToAction("RequestAppointment");
}
Now, our SEO person is telling me the new URL (www.domain/appointment) is returning a 302 redirect (which I understand) and that is bad for SEO. They would like a 301.
So, I understand I can return the (desired from SEO viewpoint) 301 redirect by using:
//Appointment Controller
public ActionResult Index()
{
return RedirectPermanent("http://www.domain.com/Appointment/RequestAppointment");
}
It works, but is there a better way or a best practice to handle this? I figure it can't be that uncommon, but I can't find anything.
I will also have to do the same thing with about 10 other unique URLs that fall into the www.domain/controller/action/id format that would be redirected to www.domain/promotion.
You don't need to set up redirects for these. You can set up the Routing engine to automatically map a given URL to a specific controller action, even if it doesn't match the given default structure. Keep in mind, these need to be BEFORE the default route. You can add as many of these as you want, and they will return a 200, not even a 301.
routes.MapRoute("Request Appointment",
"appointment", // <= What you want the URL after "www.domain" to be
new { controller = "Appointment", action = "RequestAppointment" });
Bear in mind, with this in place, you would need to have the URL be "appointment/index" if you want to get to the index action. However, the same holds true with any other sort of mapping. But, you can get to the same page with both of the following URLs with no server or client redirects occurring:
www.domain.com/appointment
www.domain.com/appointment/requestappointment
I've got a controller named "TafelController.cs" and a view named "Berekenen.cshtml". (the names aren't made up by me.)
the url "http://localhost:5181/tafel/berekenen" somehow doesn't work, even when adding extensions to berekenen like ".cshtml".
Decapitalizing the names of the controller and the view also doesn't work.
The thing is, I get the proper view when I make the Index() method the following.
public ActionResult Index()
{
return View("berekenen");
}
which is weird, because that's what
http://localhost:portnum/tafel/berekenen
is.
when setting that page as the startpage the URL differs a bit.
Then it becomes
http://localhost:5181/Views/tafel/berekenen.cshtml
Does anyone have any idea what might be happening?
http://localhost:portnum/tafel/berekenen is trying to navigate to a method named Berekenen on TafelController. You need to add the following method
public ActionResult Berekenen()
{
return View();
}
I've got two controller actions at the moment: one that displays a strongly typed view, based on an unique identifier and another one that changes the model. Here's some code to visualize what I mean:
[HttpGet]
[ActionName("Edit")]
public ActionResult UpdateDevice(string code)
{
// Request the device based on the code.
var device = GetDeviceModel(code);
// Present the device in a view.
return View(device);
}
[HttpPost]
[ActionName("Edit")]
public ActionResult UpdateDevice(DeviceModel model)
{
}
The code identifies the device, but it is also possible to change it. And that's my problem: Inside the post method I can access the new code using model.Code, but I also need to know the old code, to be able to change it.
I tried several alternatives, but none of them did satisfy my requirements:
ViewData does not get persisted until the post.
TempData is based on Sessions or Cookies – none of them I want to use at the moment.
Hidden fields and model bindings are not an option, because they can be manipulated on client side.
Finally I tried requesting data from the query string like this:
[HttpPost]
[ActionName("Edit")]
public ActionResult UpdateDevice(DeviceModel model)
{
var oldCode = Request.QueryString["code"];
}
And this works! But I haven't found any resources on this around the web. So my question is: Is using the query string inside a post action safe against modifications? And what (if there are any) are the conditions for this to work?
If this is not a valid way to "remember" the code, are there any alternatives?
Based on the requirements you have mentioned in your question, it seems that you are looking for safety in the case. So I would say that QueryString is not safe. In my opinion using Session variables is the appropriate method that keeps your critical data in a safe location. You can use the method like this
[HttpGet]
[ActionName("Edit")]
public ActionResult UpdateDevice(string code)
{
Session["code"] = code;
....
}
[HttpPost]
[ActionName("Edit")]
public ActionResult UpdateDevice(DeviceModel model)
{
if (Session["code"] == null)
throw new Exception("Error Message.");
var code = Session["code"].ToString();
....
Session["code"] = null;
}
I think you can make another property inside DeviceModel name OldCode.
and in your view you can save this value in hiddenfield like this
#Html.HiddenFor(m=>m.OldCode)
Now in controller post method you can get both the values like this.
[HttpPost]
[ActionName("Edit")]
public ActionResult UpdateDevice(DeviceModel model)
{
var oldcode=model.OldCode;
var newcode=model.Code;
}
Hope this helps...
Nothing submitted via a GET or a POST request is safe from modifications. While a GET query string is obviously easy to modify, it doesn't take much effort to spoof POST variables either. It sounds to me like you need to re-think your approach.
Optimally, you would do permission checking server-side to determine if the user is allowed to update the device with the passed (old) code. If they do not have permission, return an error such as an HTTP 550 code.
If you truly can't support this approach, I would suggest adding an "OldCode" field to the DeviceModel class as suggested by others, but encrypting it before sending it to the client. Then you can safely write it to a hidden field, and decrypt back on the server without fear of the user changing the value.
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