I'm trying to build a simple page with a simple form with ASP.NET using a Razor page, and can't work out how to handle a particular post-back. In particular (through factors outside of my control) I'm getting a post-back with a single query parameter that is lowercase and kebab-case, in a regular MVC page I could use the FromQuery attribute, but it doesn't appear to work in this instance as with or without the attribute I get null passed to OnPostAsync each time. An example to illustrate this issue follows:
Example.cshtml
#page
#model my_namespace.Pages.ExampleModel
#{
ViewData["Title"] = "Example Title";
}
<h2>Example</h2>
<form method="post">
<!--- In actual code I don't have control of the name, so this is for illustrative purposes. --->
<input type="text" name="kebabbed-name"/>
<input type="submit" />
</form>
Example.cshtml.cs
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace my_namespace.Pages
{
public class ExampleModel : PageModel
{
public async Task<IActionResult> OnGetAsync()
{
return Page();
}
// This method is hit as expected, but the parameter is always null.
// Changing the input name to something like "sample" and this parameter to match works however.
public async Task<IActionResult> OnPostAsync(string kebabbedName)
{
// Handling of the Post request
return Page();
}
}
So what I'm looking for is some way to process the postback with that kebabbed-name as a parameter - any solution would be welcomed.
It doesn't seem like Razor pages could process kebabbed-names automagically, however you can create a property in your PageModel class with a custom name that should bind to the postback value.
// For GET request
// [BindProperty(Name = "kebabbed-name", SupportsGet = true)]
// For POST request
[BindProperty(Name = "kebabbed-name")]
public string kebabbedName { get; set; }
Related
i'm creating a new website with umbraco8 i have a problem how to load an Action in Controller from a view of Homepage
Homepage view
#inherits Umbraco.Web.Mvc.UmbracoViewPage<ContentModels.HomePage>
#using ContentModels = Umbraco.Web.PublishedModels;
#{
Layout = "webBase.cshtml";
}
#{ Html.RenderAction("RenderHomeSlider", "HomePage"); }
Homepage Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Umbraco.Web.Mvc;
using System.Web.Mvc;
using Umbraco.Core.Models;
using HAWELE.Models;
using Archetype.Models;
using Umbraco.Web;
using System.Threading;
using Umbraco.Web.Mvc.SurfaceController;
public class HomePageController : Umbraco.Web.Mvc.SurfaceController
{
public ActionResult Index(RenderModel model)
{
return base.Index(model);
}
private const string PARTIAL_VIEW_HOME_FOLDER = "~/Views/Partials/HomePage/";
public ActionResult RenderHomeSlider(this HtmlHelper html)
{
return PartialView(PARTIAL_VIEW_HOME_FOLDER + "_HomeSlider.cshtml");
}
}
The issue isn't related to Umbraco. [ChildActionOnly] attribute is missing on the calling method:
RenderAction(HtmlHelper, String, String)
Invokes the specified child action method using the specified controller name and renders the result inline in the parent view.
https://learn.microsoft.com/en-us/dotnet/api/system.web.mvc.html.childactionextensions.renderaction?view=aspnet-mvc-5.2
Try the following:
Remove the index method from your controller. It is not relevant in a surfacecontroller.
Modify the RenderHomeSlider Action to:
public ActionResult RenderHomeSlider()
{
return PartialView(PARTIAL_VIEW_HOME_FOLDER + "_HomeSlider.cshtml");
}
This line in your view
#{ Html.RenderAction("RenderHomeSlider", "HomePage"); }
should change to
#Html.Action("RenderHomeSlider", "HomePage")
This should render whatever html is in _HomeSlider.cshtml
It's not clear whether your _HomeSlider.cshtml partial requires any model to be passed into so I am assuming it doesn't.
To test that the above works, you can just put some simple text in your partial and see if it rendered on the page.
I don't know what the rest of your code looks like so can't say exactly whether what you are trying to achieve here is the most expedient way to do it.
Good luck
I'm trying to create a web application using ASP.NET MVC (though I'm still a beginner so I don't know much about C#, I only used VB.net). I want my ASP.NET MVC application to store the content of the HTML input tag like username and password to sign in to my application but it didn't work out.
(PS : I'm still a beginner so please make it as simple as it can get)
I already tried many tutorials before coming here, but none of them seems to work.
Here's is the code I tried
// in the Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MoviesApp.Models
{
public class Movie
{
public string id { get; set; }
public string name { get; set; }
}
}
// in the controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MoviesApp.Models;
namespace MoviesApp.Controllers
{
public class MoviesController : Controller
{
// GET: Movies
public ActionResult Random(string movieid, string moviename)
{
var movie = new Movie();
movie.id = movieid;
movie.name = moviename;
return View(movie);
}
}
}
//in views :
#model MoviesApp.Models.Movie
#{
ViewBag.Title = "Random";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<form method="post" action="Random">
<input type="text" name="movieid"/>
<input type="text" name="moviename"/>
<input type="submit" value="save" />
</form>
I think action should look like this. And [HttpPost] in the controller
<form method="post" action="/Movies/Random">
Please try adding this to your controller method (you use POST instead of GET in your client side code):
[HttpPost]
public ActionResult Random(string movieid, string moviename)
{
var movie = new Movie();
movie.id = movieid;
movie.name = moviename;
return View(movie);
}
The default MVC controller route defined in App_Start/WebApiConfig.cs is
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Hence, call controller method as following
https://localhost:44397/Movies/Random?movieid=Shrek&moviename=first
Get vs Post
I think it worked since my link changed to :
https://localhost:44397/Movies/Random?movieid=Shrek&moviename=first
but my method is now get and the attribute is httpget insted of httppost would you please explain what post and get are and when should i use post or get ?
PS: when i added httppost to the controller and set the method to post and the action to /MoviesController/Random it gave me the error ressouce cannot be found
I spent a good margin of time searching and working through this problem yesterday without coming up with a solution. Here is the guide I used as reference.
The problem is that the data from the form is not reaching my controller. My goal is to take the form data and pass it to my controller/ model so that I can use the values throughout my code and store them in the database. Below is what I have so far...
In my Browse.cshtml (View)
#model Collect
<form asp-action="Collect" asp-controller="Collect" method="post">
<input type="hidden" asp-for="GameId" name="#game.id"/>
<button type="submit" class="dropdown-item btn btn-block">Default</button>
</form>
In my CollectController.cs (Controller)
using System;
using GameLibrary.me.Models;
using Microsoft.AspNetCore.Mvc;
namespace GameLibrary.me.Controllers
{
public class CollectController : Controller
{
[HttpGet]
public IActionResult Collect()
{
return View();
}
[HttpPost, ValidateAntiForgeryToken]
public IActionResult Collect(Collect model)
{
Console.WriteLine("**********\n"+model.GameId+"\n**********");
return Content($"Hello {model.GameId}");
}
}
}
In my Collect.cs (Model)
namespace GameLibrary.me.Models
{
public class Collect
{
public int GameId { get; set; }
}
}
EDIT: Here is what my IDE is telling me...
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 POST https://localhost:5001/browse?game=eevee application/x-www-form-urlencoded 7
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 1.0139ms 200
info: Microsoft.AspNetCore.Server.Kestrel[32]
Connection id "0HLJHIUOU6AKO", Request id "0HLJHIUOU6AKO:00000003": the application completed without reading the entire request body.
Any guidance on what I am doing wrong would be great appreciated... Also can I send multiple values through the hidden field type, or should I make a new hidden field type for each value?
There was a lot of different help here, thanks especially to Kirk Larklin! There were three issues that was preventing my controller from picking up the data.
Browse.cshtml was missing the #addTagHelpers... I added the following:
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#addTagHelper *, AuthoringTagHelpers
My CollectController.cs was missing a route... I added the following:
[HttpPost, ValidateAntiForgeryToken]
[Route("Index/Collect")]
Finally, I renamed my controller post method from 'Collect' which conflicting with another method to Index and updated the asp-action in my Browse.CSHTML file to match.
public IActionResult Index(Collect model)
Thanks for all the help!
-Travis W
First I would pass in the model to the view with the Id initialised:
public IActionResult Collect()
{
return View(new Collect { GameId = "5"});
}
Inside your view update the form to the following:
<form asp-action="Collect" asp-controller="Collect" method="post">
#Html.HiddenFor(m => m.GameId)
<button type="submit" class="dropdown-item btn btn-block">Default</button>
</form>
The Html helper will create the html coded for the field. Once you click submit the value will be correct.
In my case, I updated the login view with this line of code. that's fixed.
<form method="post" asp-controller="account" asp-action="login">
Based on your example I do not believe that you want custom actions, you should be able to achieve your goal with the default framework mapping.
public class CollectController : Controller
{
[HttpGet]
public IActionResult Index() => View();
[HttpPost]
public IActionResult Index([FromBody]CollectionModel model) => Content(...);
}
To clarify the magic, the declared Post method I utilize a HttpBody attribute, otherwise the framework will expect the parameter contents to be defined via Query String in the URL. The framework by default looks for Index when a Controller has been hit, if you do not need the URL to represent that, then do not use it.
The routing pattern usually follows:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
URL:
http://www.sample.com/Collect ---> Initial index Get would be hit
Thanks in advance for any help
I am working in aspnet core 2.1 razor pages. I need to repopulate form data when validation fails or ModelState is Invalid.
In MVC, we can use return View(model) but how to do that in aspnet core 2.1 razor page.
I tried return Page(), but that fires server side validation but does not repopulate data in form
Need Help...
Repopulation of the form values occurs automatically if you
Use the [BindProperty] attribute on the relevant PageModel properties,
Use the asp-for attribute in your input tag helpers to establish two-way binding in the UI (Razor content page)
Call return Page() in the event that ModelState.IsValid == false.
Here are the minimal steps required to demonstrate this:
A form:
<form method="post">
<input asp-for="FirstName"/><span asp-validation-for="FirstName"></span><br />
<input type="submit" />
</form>
And a PageModel:
public class FormValidationModel : PageModel
{
[BindProperty, StringLength(5)]
public string FirstName { get; set; }
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
return RedirectToPage("index");
}
}
I am a junior programmer trying to write a simple little bit of code to test out FluentValidation, but without manually calling the validator and adding the results to the modelstate with .AddToModelState, I cannot get the ModelState.IsValid to recognize there are errors in the validation. Am I missing integration somewhere?
This is my Value Model, just a string array with two preset values.
using FluentValidation.Attributes;
using Playground2.Validators;
namespace Playground2.Models
{
[Validator(typeof(ValueValidator))]
public class Value
{
public string[] values = { "value1", "" };
}
}
This is my Validator, looking for two values between 5 and 10 characters.
using FluentValidation;
using Playground2.Models;
namespace Playground2.Validators
{
public class ValueValidator : AbstractValidator<Value>
{
public ValueValidator()
{
RuleFor(x => x.values[0]).Length(5, 10);
RuleFor(x => x.values[1]).Length(5, 10);
}
}
}
In the ValuesController, I am simply creating a value object and running a check to see if it passes validation before being output.
using FluentValidation.AspNetCore;
using Microsoft.AspNetCore.Mvc;
using Playground2.Models;
using System.Collections.Generic;
namespace Playground2.Controllers
{
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
var value = new Value();
if (!ModelState.IsValid)
{
return new string[] { "Not valid" };
}
else
{
return value.values;
}
}
But when run, the ModelState.IsValid is always evaluating as true, though the information fed into values is by default invalid.
FluentValidation follows MVC's/HTML's convention in regard to GETs and POSTs. In this case it's not expecting any validation to be done an a page's initial GET as a user wouldn't necessarily have performed any action. They're instead requesting the page to start doing something - they haven't gotten around to supplying data.
Once the user fills out information the convention is to submit the data in a HTML <form> using a <button> or <input type="submit"/> to submit the data to the controller via a HttpPost marked method. At this point validation has triggered and you'll be able to correctly interrogate the ModelState.IsValid.