How to prevent HttpGet method being called? - c#

So i have a regular method and a HttpGet method:
//Create a new note
public ActionResult EditNote()
{
return View();
}
//Edit a selected note
[HttpGet]
public ActionResult EditNote(int id)
{
var model = NotesProcessor.LoadNote(id);
return View(model);
}
They both use the same views page, but only the HttpGet method will fill up the page with details since the user will be editing an existing note here.
So the first method should open up a page that is not filled with data.
My issue is that i don't know how to call the non HttpGet method from my views page since it will automatically call the HttpGet method and the page will give me an error:
The parameters dictionary contains a null entry for parameter 'id'
This is how I'm trying to call the regular method: (Which worked fine before adding the other one)
#Html.ActionLink("Create New", "EditNote")
And this is for the HttpGet method:
#Html.ActionLink("Edit", "EditNote", new { id = Model.Id })
Honestly i thought it would detect the non overloaded syntax and call the right method but it doesn't.
I could make another views page for creating a blank note but that's not very 'DRY'...
What should i do?

You can rename the method with no parameters:
//Create a new note
public ActionResult CreateNote()
{
return View("EditNote");
}
//Edit a selected note
[HttpGet]
public ActionResult EditNote(int id)
{
var model = NotesProcessor.LoadNote(id);
return View(model);
}
And then in your view:
#Html.ActionLink("Create New", "CreateNote")
#Html.ActionLink("Create New", "EditNote", new { id = Model.Id })

Try adding HttpGetAttribute with route template without parameters:
[HttpGet("[controller]/[action]")]
public ActionResult EditNote()
{
return View();
}
But I would argue that it is worth considering renaming EditNode() into CreateNote()

Related

How to add multiple HttpPost method in one controller?

I'm creating a simple ASP.NET MVC web app. In my controller I have two HttpPost methods and I call two different stored procedures. Everything worked well until I added a second HttpPost method. Now I have a problem with the second HttpPost method - when I click on it ("Go to view") I get a message
Unable to find matching view
My controller:
//...
public class OrderController : Controller
{
[ActionName("Index")]
public ActionResult Index(OrderListOfClass ttt)
{
//code
}
[HttpPost]
[ActionName("Index")]
public ActionResult Index(OrderListOfClass ttt, string send)
{
//calling stored procedure 1
}
[ActionName("Tank")]
public ActionResult Tank(OrderListOfClass ttt)
{
//code
}
[HttpPost]
[ActionName("Tank")]
public ActionResult Tank(OrderListOfClass ttt, string sendBatch)
{
//calling stored procedure 2
}
}
My view:
#model SlurryOrderTest.Models.OrderListOfClass
//...
#using (Html.BeginForm("Index", "Order", FormMethod.Post))
{
//textbox to be filled by user - input parameter for stored procedure 1
}
#using (Html.BeginForm("Index", "Order", FormMethod.Post))
{
//textbox which are filled by stored procedure 1
}
#using (Html.BeginForm("Tank", "Order", FormMethod.Post))
{
//textbox to be filled by user - input parameter for stored procedure 2
}
#using (Html.BeginForm("Tank", "Order", FormMethod.Post))
{
//textbox which are filled by stored procedure 2
}
Why doesn't the "Tank" action go into a view?
I'm probably making some stupid mistake but C# is a new language for me :(
Sounds like you need to tell it which view to use. By default it will look for one called tank.cshtml in the Views\Order folder. So I think you need this for your tank method
[HttpPost]
[ActionName("Tank")]
public ActionResult Tank(OrderListOfClass ttt, string sendBatch)
{
//calling stored procedure 2
return View("Index", ttt);
}
Alternatively if you want it to go to its own specific view then create a tank view by creating a file call Tank.cshtml in the order folder.
There are few things you need to understand when calling controller methods.
In order to go to the view from the controller by right click you probably need to have a view within the views folder with the same name as that method.
In a controller if there are two methods with the same name and if one has an [HttpPost] attribute then it will be called from Form.Post in view. If the other method has no attribute then it will automatically be the get method.

RedirectToAction for index to go to example.com/controller/id

We are using a controller which has an "Index" action:
[Route("")]
[Route("Index/{id:int?}")]
[Route("{id:int?}")]
public ActionResult Index(int? id)
{
var viewModel = new GroupViewModel();
....
return View("Index", viewModel);
}
We can get to this action by using example.com/my/ or example.com/my/index or example.com/my/index/1. This works as we want. Now we want to redirect to this using the example.com/my/index/1 syntax.
When we execute this line:
return RedirectToAction("Index", "My", new { id = 3 });
It will redirect using this url:
example.com/my?id=3
We want it to use example.com/my/index/1 instead.
Does anybody know of a way to force RedirectToAction to use this convention without the question mark?
Updated 5/2/17 to correct controller names per comments below
You need to tell which route template you need to use for generating the URL for the redirection. For the moment you haven't an URL template that can generate a URL like /my/index/1 which is "My/Index/{id:int?}"
First,add that route template to your action and set the Name property of that route like this:
[Route("")]
[Route("Index/{id:int?}")]
[Route("My/Index/{id:int?}", Name = "MyCompleteRouteName")]
[Route("{id:int?}")]
public ActionResult Index(int? id)
{
var viewModel = new GroupViewModel();
....
return View("Index", viewModel);
}
Second, you must RedirectToRoute instead of RedirectToAction. RedirectToRoute let you choose which template you want by giving its name.
So you must call this line :
RedirectToRoute("MyCompleteRouteName", new { id = 3 });
Instead of
RedirectToAction("Index", "My", new { id = 3 });

Alternative to Response.Redirect with Request.Querystring

I've made a mvc4 project in Visual Studio Express 2012 for web. And there I've made a search function. And a view to show the result.
So normally I would have added this to the _Layout.cshtml.
if (Request["btn"] == "Search")
{
searchValue = Request["searchField"];
if (searchValue.Length > 0)
{
Response.Redirect("~/Views/Search/Result.cshtml?searchCriteria=" + searchValue);
}
}
And that doesn't work. What whould be the alternative to Response.Redirect in mvc4, which still allows me to keep the searchCriteria to be read with Request.Querystring at the Result.cshtml page.
You should be definetly doing this in your controller, making it return an ActionResult and returning a RedirectResult, i.e.:
public ActionResult Search(string searchCriteria) {
return Redirect("~/Views/Search/Result.cshtml?searchCriteria="+searchCriteria);
}
Btw, I'd also say don't use neither of the Request stuff (or even Redirects), but actions with parameters that MVC will bind automagically from POST or GET parameters. E.g, "www.something.com/search?searchCriteria=hello" will automatically bind the searchCriteria parameter to the Action handling /search. Or, "www.something.com/search/hello" will bind to the parameter defined into your Routing config.
A simple example would be something like this:
Index.cshtml:
#using (Html.BeginForm("Results", "Search", FormMethod.Get))
{
#Html.TextBox("searchCriteria")
<input type="submit" value='Search' />
}
Then the controller:
public class SearchController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Results(string searchCriteria)
{
var model = // ... filter using searchCriteria
return View(model);
}
}
model could be of type ResultsViewModel, which would encase everything you need to display the results. This way, your search is setup in a RESTful way - meaning it behaves consistently each time.

Redirection error

Taking first steps with ASP.NET MVC, I am trying to create a simple (and typical) article-with-comments page: under an article itself there should be a form enabling an user to post a comment to the article.
I created a partial view for the submit form and CommentController with following methods:
public ActionResult Add(int entryId);
[HttpPost]
public ActionResult Add(Comment comment);
Then, under the article in a view of HomeController:
<div class="add-comment">
#{ Html.RenderAction("Add", "Comment", new { entryId = Model.EntryId }); }
</div>
The form renders properly and the adding procedure actually works (comment gets saved into database), but after redirecting back to the article InvalidOperationException is thrown, with Html.RenderAction (the one shown above) highlited in debugger:
System.InvalidOperationException: Child actions are not allowed to perform redirect actions.
Why does it happen?
Here's the code for CommentController methods:
public ActionResult Add(int entryId)
{
var comment = new Comment { EntryId = entryId };
return PartialView(comment);
}
[HttpPost]
public ActionResult Add(Comment comment)
{
if (ModelState.IsValid)
{
comment.Date = DateTime.Now;
var entry = db.Entries.FirstOrDefault(e => e.EntryId == comment.EntryId);
if (entry != null)
{
entry.Comments.Add(comment);
db.SaveChanges();
return RedirectToAction("Show", "Home", new { id = entry.EntryId });
}
}
return PartialView(comment);
}
Or maybe should I even take a diffrent approach?
Add HttpGet on the other Add action
You should/could be using RenderPartial instead of RenderAction:
Html.RenderPartial("YourPartialView", new Comment { EntryId = Model.EntryId });
There appears to be no need to use your action method if all you are doing is instantiating a model that you already have the ID too.

How do I pass the number in the route through the view to my contreller

How do I pass the number in the route throught the view to my contreller
I call a route like
(localhost)/MyThing/Create/3
What I want to do is use that 3 param and set it in the MyThing object when I insert it into the database.
I created an Edit view that is strongly typed to my thing. So by default the view asks the user for this value (but I don' want to do that).
I have these methods in my controller
public ActionResult Create()
{
var thing= new MyThing();
return View(thing);
}
and
[HttpPost]
public ActionResult Create(MyThing newThing, int thingID)
{
if (ModelState.IsValid)
{
try
{
newThing.ThingID= thingID;
DB.MyThing .AddObject(newThing);
DB.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception ex)
{
ModelState.AddModelError("", "Error creating new Thing");
}
}
return View(newThing);
}
I tried commenting out the thingID code in the view but the I get an error that it can't be null. I also tried changing the first method here to take the param and set it when newing up the object but the route did work.
If I use the code above with the default view the number is editable and is zero on the screen. And when you get back to the controller the param is reset to 0.
What would be ideal is for the view to get this value (3 in my example) and show it but be read only. How would I do that?
I am using ASP.net 2 if it matters.
You have a problem with your post action as it has the thingId parameter twice: once as a property of the MyThing object and once as a separate parameter meaning that the following line is useless as the default model binder will already assign the property:
newThing.ThingID = thingID;
Now as far as your view is concerned you didn't show it but you could include this id parameter either as part of the form or as a hidden field.
For example:
<% using (Html.BeginForm(new { thingID = ViewContext.RouteData.Values["thingID"] })) { %>
...
<% } %>
And if you wanted to include it as readonly textbox:
<% using (Html.BeginForm()) { %>
<%= Html.TextBoxFor(x => x.ThingId, new { #readonly = "readonly" }) %>
...
<% } %>
Finally your post action would become:
[HttpPost]
public ActionResult Create(MyThing newThing)
{
if (ModelState.IsValid)
{
try
{
DB.MyThing.AddObject(newThing);
DB.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception ex)
{
ModelState.AddModelError("", "Error creating new Thing");
}
}
return View(newThing);
}
I think you can just setup the route correctly and it will pass through when you call
(localhost)/MyThing/Create/3
So in your Global.asax or Area route registration file, you would need to do something like this:
context.MapRoute("thing_route",
"MyThing/Create/{thingID}",
new
{
controller = "MyThing",
action = "Create"
}
);

Categories

Resources