I am trying to read from HTML form some data. I succeeded to do it in ASP.NET MVC but not in ASP.NET MVC CORE.
Any help will be appreciated!
Haim
This CSHTML code on ASP.NET MVC (worked fine):
#using (Html.BeginForm("Login", "Account")){
<form class="form-group" action="Login" method="post">
<input name="AccountAddressTxt" type="text" class="form-control" required="required" id="AccountAddress" placeholder="Enter Your Account Address" style="width:50%" onclick="SetOx()">
<br />
<input name="AccountPasswordTxt" type="password" class="form-control" required="required" id="AccountprivateKey" placeholder="Enter Your Account Private Key" style="width:50%">
<input id="loginBtn" type="submit" class="btn btn-success" style="width:50% ;" value=" Login">
</form>
And the data binding (worked fine):
namespace RealEstate-Web_app.ModelsBinders
{
public class AccountBinder: IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
HttpContextBase objContext = controllerContext.HttpContext;
String _accountAddress = objContext.Request.Form["AccountAddressTxt"];
String _accountPassword = objContext.Request.Form["AccountPasswordTxt"];
Account obj = new Account()
{
AccountAddress = _accountAddress;
AccountPassword = _accountPassword;
};
return obj;
}
}
}
Right into the controller (worked fine):
[HttpPost]
public IActionResult Login([ModelBinder(typeof(AccountBinder))] Account acc)
{
return View("Login", acc);
}
But in ASP.NET MVC CORE I am getting another method with no idea what to do:
...
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace RealEstate_Web_app.ModelBinders
{
public class AccountBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
//What am I suppose to do here?
}
}
}
Follow a similar format to what was done previously. There are some changes in the new version that should be noted.
namespace RealEstate_Web_app.ModelBinders {
public class AccountBinder : IModelBinder {
public Task BindModelAsync(ModelBindingContext bindingContext) {
if (bindingContext == null) {
throw new ArgumentNullException(nameof(bindingContext));
}
var modelName = bindingContext.ModelName;
HttpContext httpContext = bindingContext.HttpContext;
String _accountAddress = httpContext.Request.Form["AccountAddressTxt"];
String _accountPassword = httpContext.Request.Form["AccountPasswordTxt"];
Account model = new Account() {
AccountAddress = _accountAddress;
AccountPassword = _accountPassword;
};
//TODO: validate and update model state if not valid
//...
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
}
Reference Custom Model Binding in ASP.NET Core
Related
i have problem with form when i put in my Controller httpPost the Error http 405 it showing to me
every step i made its correct
my code
#model security.Models.ContactUs
<div class="contact_box">
<form id="Contact" asp-controller="Home" asp-action="Contact" method="POST">
<input type="text" asp-for="Name" placeholder="Your Name">
<input type="email" asp-for="Email" placeholder="Email">
<input type="text" asp-for="PhoneNumber" placeholder="Phone Number">
<input type="text" asp-for="Text" placeholder="Message">
<button type="submit">Contact Us</button>
</form>
</div>
and my Controller
namespace security.Controllers
{
public class HomeController : Controller
{
DBContext db;
public HomeController(DBContext context)
{
db = context;
}
//private readonly ILogger<HomeController> _logger;
//public HomeController(ILogger<HomeController> logger)
//{
// _logger = logger;
//}
[HttpGet]
public IActionResult Index()
{
CollectionsData model = new CollectionsData();
model.Offers = GetOffers();
model.Services = GetServices();
model.News = GetNews();
model.Team = GetSecurityTeam();
return View(model);
}
[HttpPost]
public IActionResult Contact(ContactUs model)
{
db.Contactus.Add(model);
db.SaveChanges();
return RedirectToAction("Index");
}
public IActionResult Services( Sevices model)
{
var ModelServices = db.Sevices.ToList();
return View(ModelServices);
}
//[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
//public IActionResult Error()
//{
// return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
//}
public IEnumerable<Offers> GetOffers()
{
return db.Offers.ToList();
}
public IEnumerable<Sevices> GetServices()
{
return db.Sevices.ToList();
}
public IEnumerable<ourNews> GetNews()
{
return db.News.ToList();
}
public IEnumerable<SecurityTeam> GetSecurityTeam()
{
return db.Team.ToList();
}
}
}
But when I delete httppost everything works fine except sending the form to the database
i don't why the page its not load its give HTTP ERROR 405
thanks
you have to place a submit button inside of the form
<form asp-controller="Home" asp-action="Contact" method="POST">
....your code
<button type="submit">Contact Us</button>
</form>
or for some reason sometimes this works better
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
....your code
<button type="submit">Contact Us</button>
}
and you need Contact get action to create a contact view
[HttpGet]
public IActionResult Contact()
{
var model= new ContactUs();
return View(model)
}
try this
<form id="contact" method="POST" asp-action="Contact">
...
</form>
return View() doesn’t make any sense from an HTTP Verb perspective. Your form is POSTing correctly, but when you’re returning the view you need to change it to redirect to your GET action. I don’t know know what your entire controller looks like but I suspect this is the issue with naming.
Try
return RedirectToAction(“VIEWNAME”)
I need to do 2 savechanges. But first of them does not work. Ready method has simple view that says Thank you, #Viewbag.OrderName
Сontroller
public class Some : Controller
{
private AppDbContext context;
public Some(AppDbContext ctx)
{
context = ctx;
}
[Httpost]
public ActionResult Buy(int? id, string Price,Order order)
{
if (id == null) return RedirectToAction("Index");
using (var transaction = context.Database.BeginTransaction())
{
context.Orders.Add(order)
context.SaveChanges();
if(Price!=null)
{var ty=context.Phones.Where(p =>
p.PhoneId==id).FirstOrDefault();
ty.Price1=Price+150;
}
context.SaveChanges();
transaction.Commit();
}
return View("Buy");
}
public ActionResult Ready(int? id, string OrderName)
{
ViewBag.PhoneId = id;
ViewBag.OrderName = OrderName;
return View();
}
}
}
Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(Configuration["Data:AppDb:ConnectionString"]));
services.AddTransient<Some>();}
Context class
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AviaAppDbContext> options)
: base(options)
{
}
public DbSet<Phone> Phones { get; set; }
public DbSet<Order> Orders { get; set; }
appsettings
"AllowedHosts": "*",
"Data": {
"AppDb": {
"ConnectionString": "Server=(localdb)\\MSSQLLocalDB;Database=AppDb;Trusted_Connection=True;MultipleActiveResultSets=true"
}
Added view for more information
View 1
foreach p in Model
<form method="post">
<td class="pricelight"><input type="radio" name="price"value="#p.PriceLight.ToString("")"/>#p.PriceLight.ToString("")</td>
<td class="fromtabletime"><input type="radio" name="price"value="#p.PriceOk.ToString("")/>#p.PriceOk.ToString("")</td>
<td class="totable"><input type="radio" name="price"value="#p.PriceHigh.ToString("")/>#p.PriceHigh.ToString("")</td>
<td class="button13"><button type="submit" asp-action="Buy" asp-route-id="#p.PhoneId" asp-controller="Home">Next</button></td>
</form>
View 2
#using System.Linq;
#using System;
#model List<Searchphone>
#{
ViewBag.Title = "PhoneId";
}
<h2 class="orderinformation">Order Data</h2>
<form method="post" class="formpass" asp-action="Ready" asp-controller="Some">
<input type="hidden" id="PhoneId"value="#ViewBag.PhoneId" name="PhoneId">
<label for="OrderSurName">Surname</label><br>
<input type="text" placeholder="Enter Surname" name="OrderSurName" required><br>
<label for="OrderName">Имя</label><br>
<input type="text" placeholder="Enter Name" name="OrderName" required><br>
<button type="submit" class="button7">Next</button>
</form>
I have checked it without Transaction. It works the same. I can not make changes in two tables,one context. What is my mistake?
Your AppDbContext doesn't have a parameterless constructor, and you are trying to invoke one in the using statement. Also, you already are injecting the DbContext with DI so its already initialized and ready to use. With that said you don't need to create a new instance of the context, just use the existing one that you already passed in trough DI.
Hope it helps
If you want to edit Phones data which PhoneId equals to the id parameter, you need change
context.Price1=Price+150; to ty.Price1=Price+150;
More code as follow:
[Httpost]
public ActionResult Buy(int? id, string Price,Order order)
{
if (id == null) return RedirectToAction("Index");
using (var transaction = context.Database.BeginTransaction())
{
context.Orders.Add(order)
context.SaveChanges();
if(Price!=null)
{var ty=context.Phones.Where(p =>
p.PhoneId==id).FirstOrDefault();
ty.Price1=Price+150;
}
context.SaveChanges();
transaction.Commit();
}
return View("Buy");
}
I have an id received in my action parameter. From where I got my scheme details. By this details I found my desired scheme name. And now I want to show this name in my .cshtml as an input field where I want to show the value in "read-only" format (not changeable). So in my controller I sent it inside a ViewData instead of SelectList as shown below:
ViewData["SchemeNum"] = schemeInfo.SchemeNum;
instead of
ViewData["SchemeInfoId"] = new SelectList(_context.PackagewiseSchemeInfo.Where(p => p.Id == id), "Id", "SchemeNum", schemeInfo.SchemeNum);
I know if I use a selectlist, it would have been easier for me to catch the Key after form submitting like mentioned above -- "Id", "SchemeNum" ...
HTML:
<input class="form-control" value="#ViewBag.SchemeNum" readonly />
Now, I'm getting SchemeNum instead of Id after form submission. I want to know how to catch the KEY instead of value by an input tag? Please help.
You probably wanna disable your input add a hidden field for the id.
<input class="form-control" value="#ViewBag.SchemeNum" disabled />
<input type="hidden" value="#ViewBag.SchemeId" />
Besides,you could custom Model Binder to get the matched key of the value.
Here is a working demo like below:
Model:
public class PackagewiseSchemeInfo
{
public int Id { get; set; }
public string SchemeNum { get; set; }
}
View:
#model PackagewiseSchemeInfo
<form asp-action="Test">
<input asp-for="SchemeNum" class="form-control" value="#ViewBag.SchemeNum" readonly />
<input type="submit" value="create" />
</form>
Custom Model Binder:
public class CustomModelBinder : IModelBinder
{
private readonly YourDbContext _context;
public CustomModelBinder(YourDbContext context)
{
_context = context;
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var data = bindingContext.ActionContext.HttpContext.Request.Form["SchemeNum"];
var model = _context.PackagewiseSchemeInfo.Where(a => a.SchemeNum == data.ToString())
.Select(a=>a.Id).FirstOrDefault();
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
Controller:
public IActionResult Index()
{
var schemeInfo = new PackagewiseSchemeInfo()
{
Id = 1,
SchemeNum = "aaa"
};
ViewData["SchemeNum"] = schemeInfo.SchemeNum;
return View();
}
[HttpPost]
public IActionResult Test([ModelBinder(typeof(CustomModelBinder))]int Id)
{
//do your stuff...
}
Result:
I'm building a dynamic form creator in .net core. A "form" will consist of many different form elements. So the form model will look something like this:
public class FormModel {
public string FormName {get;set;}
public List<IElements> Elements{get;set;}
}
I have classes for TextBoxElement, TextAreaElement, CheckBoxElement that all implement the IElemets interface. And I have EditorTemplates for each element. The code to render the form works great. Though posting the form does not work because of the List of Interfaces.
I've been looking on how to implement a custom model binder, and seen some few examples on the web but I did not get anyone to work.
I would appreciate if someone could show me how to implement a custom model binder for this example.
Plan B:
Post form as json to a web api and let JSON.Net covert it. I have tried it and it worked. In startup.cs i added:
services.AddMvc().AddJsonOptions(opts => opts.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto);
It returns type when it has to, eg. the objects in the Elements-list but not on the FormModel. But i really would like to know how to solve it with a custom model binder instead.
Ok, this works for me. I'm still getting to grips with the new model binding so I may be doing something silly but it's a start!
TEST FORM
<form method="post">
<input type="hidden" name="Elements[0].Value" value="Hello" />
<input type="hidden" name="Elements[0].Type" value="InterfacePost.Model.Textbox" />
<input type="hidden" name="Elements[1].Value" value="World" />
<input type="hidden" name="Elements[1].Type" value="InterfacePost.Model.Textbox" />
<input type="hidden" name="Elements[2].Value" value="True" />
<input type="hidden" name="Elements[2].Type" value="InterfacePost.Model.Checkbox" />
<input type="submit" value="Submit" />
</form>
INTERFACE
public interface IElement
{
string Value { get; set; }
}
TEXTBOX IMPLEMENTATION
public class Textbox : IElement
{
public string Value { get; set; }
}
CHECKBOX IMPLEMENTATION
public class Checkbox : IElement
{
public string Value { get; set; }
}
MODEL BINDER PROVIDER
public class ModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(IElement))
{
return new ElementBinder();
}
// else...
return null;
}
}
MODEL BINDER
public class ElementBinder : IModelBinder
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext.ModelType == typeof(IElement))
{
var type = bindingContext.ValueProvider.GetValue($"{bindingContext.ModelName}.Type").FirstValue;
if (!String.IsNullOrWhiteSpace(type))
{
var element = Activator.CreateInstance(Type.GetType(type)) as IElement;
element.Value = bindingContext.ValueProvider.GetValue($"{bindingContext.ModelName}.Value").FirstValue;
bindingContext.Result = ModelBindingResult.Success(element);
}
}
}
}
HOOK UP MODEL BINDER PROVIDER
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.ModelBinderProviders.Insert(0, new ModelBinderProvider());
});
}
}
FORM MODEL
public class FormModel
{
public string FormName { get; set; } // Not using this
public List<IElement> Elements { get; set; }
}
ACTION
Notice the three types, Textbox, Textbox and Checkbox.
CheckBoxFor is not bounded when a property is defined in an object nested in the model?
Here is an example. I have a SearchOptions model that contains a List<Star> property. Each Star has a number, a name and a bool property that should be bounded:
public class SearchOptions
{
public SearchOptions()
{
// Default values
Stars = new List<Star>()
{
new Star() {Number=1, Name=Resources.Home.Index.Star1,
IsSelected=false},
new Star() {Number=2, Name=Resources.Home.Index.Star2,
IsSelected=false},
new Star() {Number=3, Name=Resources.Home.Index.Star3,
IsSelected=true},
new Star() {Number=4, Name=Resources.Home.Index.Star4,
IsSelected=true},
new Star() {Number=5, Name=Resources.Home.Index.Star5,
IsSelected=true},
};
}
public List<Star> Stars { get; set; }
}
In my strongly typed View (of SearchOptions) i loop over Stars property:
#using (Html.BeginForm("Do", "Home"))
{
<fieldset>
<legend>#MVC3TestApplication.Resources.Home.Index.Search</legend>
#{
foreach (Star s in Model.Stars)
{
#Html.CheckBoxFor(m => s.IsSelected)
<label>#s.Name</label>
}}
</fieldset>
<input type=submit value="Invia" />
}
The (relevant part of) controller is:
public ActionResult SearchOptions()
{
return View(new SearchOptions());
}
[HttpPost]
public ActionResult Do(SearchOptions s)
{
// Do some stuff
return View("SearchOptions", s);
}
It's because of how you're accessing the properties in the CheckBoxFor expression.
#for (int i = 0; i < Model.Stars.Count(); i++) {
#Html.CheckBoxFor(m => m.Stars[i].IsSelected)
<label>#Model.Stars[i].Name</label>
}
This should work for you.
Here's the output from the different methods:
//using the for loop
<input id="Stars_2__IsSelected" name="Stars[2].IsSelected" type="checkbox" value="true" />
//using the foreach
<input checked="checked" id="s_IsSelected" name="s.IsSelected" type="checkbox" value="true" />
You'll notice that the for foreach doesn't contain the proper name for it to match to when doing model binding.