ValidateAntiForgeryToken return status code 400 - c#

The problem is that if I try and use ValidateAntiForgeryToken to my action return status code 400. Why couldn't find my view page?
Controller:
public IActionResult Edit(int id) {
var model = this.categoryService.GetById(id);
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(CategoriesViewModel model) {
if (ModelState.IsValid) {
this.categoryService.Update(model);
return RedirectToAction(nameof(Category));
}
return this.View(model.Id);
}
View:
#using CakeStore.App.Areas.Admin.Models.Categories;
#model CategoriesViewModel
#{
ViewData["Title"] = "Edit";
Layout = "~/Areas/Admin/Views/Shared/_AdminLayout.cshtml";
}
<h1 class="text-center text-header-page">Edit Category</h1>
<hr class="hr-admin-divider" />
<form class="mx-auto w-50 form-horizontal" method="post" action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<input asp-for="#Model.Id" type="hidden" name="id" value="#Model.Id"/>
</div>
<div class="form-group">
<label asp-for="#Model.Name"></label>
<input asp-for="#Model.Name" type="text" value="#Model.Name" class="form-control" id="name" name="name">
<span asp-validation-for="#Model.Name" class="text-danger"></span>
</div>
<div class="button-holder d-flex justify-content-center">
<button type="submit" class="btn button-black-white">Edit</button>
</div>
</form>
Model:
public class CategoriesViewModel {
public int Id { get; set; }
[Required]
[StringLength(22,MinimumLength =3,ErrorMessage =AdminConstants.NameRange)]
public string Name { get; set; }
}

You did not inform the form to contain the token. Try this in your form:
<form asp-antiforgery="true" class="mx-auto w-50 form-horizontal" method="post" action="Edit">
NOTE: remember you must use the #addTagHelper directive in order to use "asp-antiforgery"

Related

ASP.NET Core MVC - posting to a different action name does not bind the values

I'm using a regular html form instead of #html.BeginForm and I have these 2 form tags in my Create.cshtml view file.
I was experimenting with routing, but my post doesn't seem to get the values even if I bind the properties. I've tried in vain but I can't seem to make this work, and can't find the answer from googling.
Create.cshtml
#model Actor
#{
ViewData["Title"] = "Add";
}
<section class="container-xl justify-content-center col-lg-5 col-md-8 col-sm-10">
<div class="row">
<span class="text-center mb-3">
<h5>Add a new record</h5>
</span>
<div>
<form>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
</form>
<div class="form-group">
<label class="form-label" asp-for="ProfilePictureUrl">Profile Picture</label>
<input class="mb-2 form-control" type="text" asp-for="ProfilePictureUrl" placeholder="Profile picture" />
</div>
<div class="form-group">
<label class="form-label" asp-for="FullName">Full Name</label>
<input class="mb-2 form-control" type="text" placeholder="Full name" asp-for="FullName" />
</div>
<div class="form-group">
<label class="form-label" asp-for="Bio">Biography</label>
<input class="form-control" type="text" placeholder="Bio" asp-for="Bio" />
</div>
<form>
<div class="form-group mt-3">
<a class="btn btn-outline-secondary" asp-action="Index">Show All</a>
<input asp-action="Create2" class="float-end btn btn-outline-success" type="submit" value="Create" />
</div>
</form>
</div>
</div>
</section>
Actor.cs
using System.ComponentModel.DataAnnotations;
namespace MovieProject.Models
{
public class Actor
{
[Key]
public int ActorId { get; set; }
[Display(Name ="Profile Picture")]
public string ProfilePictureUrl { get; set; }
[Display(Name ="Full Name")]
public string FullName { get; set; }
[Display(Name ="Biography")]
public string Bio { get; set; }
}
}
ActorController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using MovieProject.Data;
using MovieProject.Data.Services;
using MovieProject.Models;
namespace MovieProject.Controllers
{
public class ActorController : Controller
{
private readonly IActorService _service;
public ActorController(IActorService service)
{
_service = service;
}
[HttpPost]
public IActionResult Create2([Bind("ProfilePictureUrl,FullName,Bio")] Actor actorItem)
{
return View("Create");
}
public IActionResult Create()
{
return View();
}
}
}
The methods are getting hit but the post data is null.
Another question is, instead of using MVC convention, can I use a different method name for get and post that is not the same as the view name? How can I get initially load the page for GET using routing that would work in a different view name?
Thanks
can I use a different method name for get and post that is not the
same as the view name?
Yes, you can.
How can I get initially load the page for GET using routing that would
work in a different view name?
return to this view.
public IActionResult Create()
{
return View("aa");
}
Below is a work demo, you can refer to it.
In controller:
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create2(Actor actorItem)
{
return View();
}
Create view:
#model nnnn.Models.Actor
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Actor</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create2">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ActorId" class="control-label"></label>
<input asp-for="ActorId" class="form-control" />
<span asp-validation-for="ActorId" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProfilePictureUrl" class="control-label"></label>
<input asp-for="ProfilePictureUrl" class="form-control" />
<span asp-validation-for="ProfilePictureUrl" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="FullName" class="control-label"></label>
<input asp-for="FullName" class="form-control" />
<span asp-validation-for="FullName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Bio" class="control-label"></label>
<input asp-for="Bio" class="form-control" />
<span asp-validation-for="Bio" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
result:

The value is not set after POST if use asp-for attribute in MVC Core 3.1

I have simple view model:
public class GogViewModel
{
[Required]
public string Name { get; set; }
public bool IsBeta { get; set; }
public string Result { get; set; }
}
and controller:
[HttpGet]
public IActionResult Index()
{
return View(new GogViewModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(GogViewModel model)
{
if (ModelState.IsValid)
{
model.Result = DoSomething();
}
return View(model);
}
and View:
#model GogViewModel
<div>
<form method="post">
#Html.AntiForgeryToken()
<div class="form-group row">
<label class="col-sm-2 col-form-label" asp-for="Name">Release name</label>
<div class="col-sm-10">
<input type="text" class="form-control" asp-for="Name" placeholder="paste release name here" />
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label" asp-for="Result">Result</label>
<div class="col-sm-9">
<input type="text" class="form-control-plaintext" readonly="readonly" asp-for="Result" />
<p>#Model.Result</p> <!-- it works here means the value is printed -->
</div>
<div class="col-sm-1">
<button type="button" class="btn btn-outline-info copy float-right" data-target="Result"><i class="far fa-copy"></i></button>
</div>
<div class="clearfix"></div>
</div>
<input type="submit" class="btn btn-success" value="Fix it" />
</form>
</div>
Somehow the result is not output after POST request.
Why ? Where's my mistake ?
It will work in the following way:
Set private setter to Result property => public string Result {get; private set;}
Add method which set value for Result: internal void SetResult(string result) { Result = result; }
In controller, call SetResult like: model.SetResult(...);
and will display in readonly input. I don't know why it works and I don't understand why the value is cleared when put it on input.
I used input with readonly attribute in order to copy to clipboard the output value with javascript.
maybe you need to add something in the input tag like value="#Model.Result"?

Put Form contents into list for MVC

I have a page containing one main form and multiple smaller duplicate forms. I am trying to post them both using one button with JQuery, and handle them in the order of: main form first then smaller forms second. However, when I attempt to access the smaller forms, I am getting data from only the last one. I was thinking that the best way to do it would be to put the smaller forms into a list, pass that to my handler, and then do a foreach loop through the list. How would I put the form information into a list before my handler uses it?
Here is my model:
public class SubmitModel
{
[BindProperty]
public Timelineinfo Timelineinfo { get; set; } //main form
[BindProperty]
public Media Medias { get; set; }//small form
}
My controller:
[HttpPost]
[Route("/Submit/Submit")]
[ValidateAntiForgeryToken] //Main form handler
public async Task<IActionResult> Submit(SubmitModel model)
{
if (ModelState.IsValid)
{
//Removed for brevity, but
//Fill out some more values in main form then submit main form to Database,
}
return View("Pages/Submit.cshtml", model);
}
[Route("/Submit/MediaAdd")]
[ValidateAntiForgeryToken] //Add media form handler
public async Task<IActionResult> MediaAdd(SubmitModel model)
{
if (ModelState.IsValid)
{
//Removed for brevity, but
//Fill out some more values in small form then submit small form to Database
}
return View("Pages/Submit.cshtml", model);
}
And a truncated version of my small form (main form looks similar but with with Submit instead of MediaAdd:
#using (Html.BeginForm("MediaAdd", "Timeline", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="container">
<div class="form-row">
<label asp-for="Medias.Blurb" class="control-label">Blurb<span class="text-danger ml-1">*</span> <span class="text-muted ml-1">Explain what is in the media</span></label>
<input asp-for="Medias.Blurb" class="form-control" />
<span asp-validation-for="Medias.Blurb" class="text-danger"></span>
</div>
<div class="form-row">
<div class="ml-3 col-4">
<label class="form-check-label">Is there any blood or gore in the video?<span class="text-danger ml-1">*</span></label>
<div class="form-check m-2 d-inline">
<input type="radio" asp-for="Medias.Gore" class="form-check-input" value="0">
<label class="form-check-label">No</label>
</div>
<div class="form-check m-2 d-inline">
<input type="radio" asp-for="Medias.Gore" class="form-check-input" value="1" />
<label class="form-check-label">Yes</label>
<span asp-validation-for="Medias.Gore" class="text-danger"></span>
</div>
</div>
</div>
</div>
}
Finally, how I am currently submitting my forms:
$('#submitButton').click(function () {
$('form').submit();
});
I made an example based on your requirement, you may refer to it:
Model:
public class SubmitModel
{
[BindProperty]
public Timelineinfo Timelineinfo { get; set; }
[BindProperty]
public List<Media> Medias { get; set; }
}
public class Timelineinfo
{
public int TimeId { get; set; }
public string Description { get; set; }
}
public class Media
{
public string Blurb { get; set; }
public string Gore { get; set; }
}
Main form View(Index):
#model SubmitModel
<button id="AddMedia" type="button" class="btn btn-primary">Add Media</button>
#using (Html.BeginForm("MediaAdd", "Timeline", FormMethod.Post))
{
<div class="container">
<div class="form-row">
<label asp-for="Timelineinfo.TimeId" class="control-label">TimeId</label>
<input asp-for="Timelineinfo.TimeId" class="form-control" />
</div>
<div class="form-row">
<label asp-for="Timelineinfo.Description" class="control-label">Description</label>
<input asp-for="Timelineinfo.Description" class="form-control" />
</div>
</div>
<div id="subformsArea">
</div>
<div>
<input type="submit" value="submit" class="btn btn-primary" />
</div>
}
#section scripts{
<script>
var count = 0;
$("#AddMedia").on('click', function () {
$.ajax({
url: '/Timeline/Media',
method: 'get',
data: {data : count},
success: function (result) {
$("#subformsArea").append(result);
count++;
}
})
})
</script>
}
Subform View(Partial View):
#model SubmitModel
#{
int i = ViewBag.Count;
}
<div class="container">
<div class="form-row">
<label asp-for="Medias[i].Blurb" class="control-label">Blurb<span class="text-danger ml-1">*</span> <span class="text-muted ml-1">Explain what is in the media</span></label>
<input asp-for="Medias[i].Blurb" class="form-control" />
<span asp-validation-for="Medias[i].Blurb" class="text-danger"></span>
</div>
<div class="form-row">
<div class="ml-3 col-4">
<label class="form-check-label">Is there any blood or gore in the video?<span class="text-danger ml-1">*</span></label>
<br />
<div class="form-check m-2 d-inline">
<input type="radio" asp-for="Medias[i].Gore" class="form-check-input" value="0">
<label class="form-check-label">No</label>
</div>
<div class="form-check m-2 d-inline">
<input type="radio" asp-for="Medias[i].Gore" class="form-check-input" value="1" />
<label class="form-check-label">Yes</label>
<span asp-validation-for="Medias[i].Gore" class="text-danger"></span>
</div>
</div>
</div>
</div>
Controller:
public class TimelineController : Controller
{
public IActionResult Index()
{
return View();
}
[Route("/Submit/MediaAdd")]
[ValidateAntiForgeryToken] //Add media form handler
public IActionResult MediaAdd(SubmitModel model)
{
if (ModelState.IsValid)
{
//Removed for brevity, but
//Fill out some more values in small form then submit small form to Database
}
return View("Pages/Submit.cshtml", model);
}
public IActionResult Media(int data)
{
ViewBag.Count = data;
return PartialView();
}
}
Result:
This is how I would do it. The simpler the better:
<form action='myAction' method='postOrGet'>
//subform 1
<input name='inputA' subform='s1'.../>
//subform 2
<input name='inputX' subform='s2'.../>
...
<button type='submit'>submit</button>
</form>
Then you can easily separate submitted data based on the "subform" attribute (s1, s2. etc)

Is my ViewModel flow right? How to send it back to controller and refresh it on a controller action?

Is my ViewModel flow right? The 'SubObjectViewModel' class usage is a bad or a good practice? Or should I try other option, like creating a ViewModel only to this class?
And also, how should I return the ViewModel 'ObjectViewModel' to the controller with all its values, change them and refresh values from page view?
My ViewModel
Public class ObjectViewModel{
public string name { get; set; }
public int value { get; set; }
public bool IsCameraPermited { get; set; }
public List<SubObjectViewModel> choosenSubObjects{ get; set; } // need to get it back on controller;
public class SubObjectViewModel
{
public int IdObject { get; set; }
public string Name { get; set; }
public string Config { get; set; }
}
public List<Object> listSub{ get; set; } //list that will be filled on 'Create' Controller
}
My Controller
public IActionResult Create(int id)
{
List<Object> listSubObject = new List<Object>();
listSubObject = _getAllSubObjectsDAO.ListByID(id);
List<Object> choosenObjects= new List<Object>();
choosenObjects = _getChoosenObjectsDAO.ListByID(id);
List<SubObjectViewModel> listSubObject = new List<SubObjectViewModel>();
foreach (Object item in choosenObjects )
{
string config = _configurationDAO.GetConfigurationById(item.configId);
ObjectViewModel .SubObjectViewModel SubObject = new ObjectViewModel .SubObjectViewModel { IdObject = item.Id, Name = item.Name , Config = config };
listSubObject.Add(setorVM);
}
ObjectViewModel objVM = ObjectViewModel{
name ="test",
value = 2,
IsCameraPermited =true,
listSub = listSubObject,
choosenSubObjects = listSubObject
};
return View(objVM);
}
My View
#model Project.Models.ViewModels.ObjectViewModel
... more code
<form asp-action="Create">
<div class="row">
<div class="col-sm-6 ctnFullHeight">
<div class="shadowBoxForm formCreateLeft position-absolute col-sm-11 ctnFullHeight">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<input asp-for="name " class="form-control" />
<span asp-validation-for="name " class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="IsCameraPermited" class="control-label"></label>
<input asp-for="IsCameraPermited" type="checkbox" />
</div>
<div class="form-group float-right position-relative">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
<div class="col-sm-6">
<div class="shadowBoxForm formCreateLeft position-absolute col-sm-11 ">
#foreach (var item in listSub)
{
<div class="txt">
#item
</div>
}
</div>
</div>
</div>
</form>
Thanks in advance.
how should I return the ViewModel 'ObjectViewModel' to the controller with all its values
To achieve above requirement, you can refer to the following code snippet.
#model ObjectViewModel
#{
ViewData["Title"] = "Create";
var choosenSubObjects = Model.choosenSubObjects;
}
<form method="post">
<div class="row">
<div class="col-sm-6 ctnFullHeight">
<div class="shadowBoxForm formCreateLeft position-absolute col-sm-11 ctnFullHeight">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<input asp-for="name " class="form-control" />
<span asp-validation-for="name " class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="IsCameraPermited" class="control-label"></label>
<input asp-for="IsCameraPermited" type="checkbox" />
</div>
<div class="form-group float-right position-relative">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
<div class="col-sm-6">
<div class="shadowBoxForm formCreateLeft position-absolute col-sm-11 ">
#for (var i = 0; i < Model.choosenSubObjects.Count; i++)
{
<div class="txt">
<div class="form-group">
<label asp-for="#choosenSubObjects[i].IdObject" class="control-label"></label>
<input asp-for="#choosenSubObjects[i].IdObject" type="text" />
</div>
<div class="form-group">
<label asp-for="#choosenSubObjects[i].Name" class="control-label"></label>
<input asp-for="#choosenSubObjects[i].Name" type="text" />
</div>
<div class="form-group">
<label asp-for="#choosenSubObjects[i].Config" class="control-label"></label>
<input asp-for="#choosenSubObjects[i].Config" type="text" />
</div>
</div>
}
</div>
</div>
</div>
</form>
Controller Actions
[HttpGet]
public IActionResult Create(int id)
{
//code logic here
//...
return View(objVM);
}
[HttpPost]
public IActionResult Create(ObjectViewModel objectViewModel)
{
//code logic here
//...
Test Result

Can't see validation errors from partial view

Have a partial view containing a form, but can't seem to get validation errors to show up.
Models
public class HomeModel
{
public LoginModel LoginModel { get; set; }
}
public class LoginModel
{
[Required]
[Display(Name = "Email")]
public string Username { get; set; }
[Required]
public string Password { get; set; }
}
Index.cshtml:
#model MyApp.Models.HomeModel
#{
await Html.RenderPartialAsync("_LoginPartial", Model.LoginModel);
}
HomeController
public IActionResult Index()
{
return View(new HomeModel()
{
LoginModel = new LoginModel()
});
}
[HttpPost]
public IActionResult Login(LoginModel model)
{
return View("Index", new HomeModel()
{
LoginModel = model
});
}
_LoginPartial
#model MyApp.Models.LoginModel
<form asp-action="Login" asp-controller="Home" method="post" role="form" style="width:70%;margin: 0 auto;">
<div style="display:none" asp-validation-summary="ValidationSummary.All" class="alert alert-danger fade in">
×
<strong>Error!</strong> A problem has been occurred while submitting your data.
</div>
<div class="form-group" style="text-align:left">
<label asp-for="Username" style="font-size:22px;" class="control-label"></label>
<input asp-for="Username" type="text" class="form-control" />
</div>
<div class="form-group" style="text-align:left">
<label asp-for="Password" style="font-size:22px;" class="control-label"></label>
<input asp-for="Password" type="password" class="form-control" />
</div>
<div class="spacer-15"></div>
<div class="form-group" style="font-size:22px;">
<button type="submit"
style="background-color: Transparent;
background-repeat:no-repeat;
border: none;
cursor:pointer;
overflow: hidden;
outline:none;" value="Login">
<img src="~/images/flower.png" />Next
</button>
</div>
</form>
It looks like you're missing a few things. Each of your inputs will need a corresponding validation element that uses asp-validation-for.
<div class="form-group" style="text-align:left">
<label asp-for="Username" style="font-size:22px;" class="control-label"></label>
<input asp-for="Username" type="text" class="form-control" />
<span asp-validation-for="Username" class="text-danger"></span>
</div>
Likewise, if you're planning on using jQuery validation, you should add the following to the environment elements in your _Layout.cshtml file.
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>

Categories

Resources