We are developing a REST API and we're allowing all four of the standard verbs. In the case of an POST/PUT
what is better in best practice c# rest api.
this is my Model
public class UserModel
{
public Int64 ID { get; set; }
[Display(Name ="First Name")]
public string FirstName { get; set; }
[Display(Name="Last Name")]
public string LastName { get; set; }
public string Address { get; set; }
[Display(Name="User Name")]
public string UserName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
[Display(Name ="Added Date")]
public DateTime AddedDate { get; set; }
}
Exemple 1
[HttpPost]
public ActionResult CreateEditUser(UserModel model)
{
if (model.ID == 0)
{
User userEntity = new User
{
//....
}
}
}
Exemple 2
[HttpPost]
public ActionResult CreateEditUser(int id,UserModel model)
{
if (id == 0)
{
User userEntity = new User
{
//.....
}
}
}
what is the better Exemple 1 Or Exemple 2
According the REST guidelines (https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md)
PUT: Replace an object, or create a named object, when applicable
POST: Create a new object based on the data provided, or submit a command
PATCH: Apply a partial update to an object
In your case is it better to split the endpoints into a POST and PUT.
[HttpPost]
public ActionResult CreateUser(UserModel model)
{
userService.Create(model);
return...
}
[HttpPut]
public ActionResult EditUser(UserModel model)
{
userService.Update(model);
return...
}
Related
I'm having difficulty understanding how to create relationships with the entity framework. (I'm using the MVC architecture, and code first migrations )for example, if I have a class
public class Employee
{
public int Id { get; set; }
public string PIN { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
and I have another class, let's say for example I wish to track employees hours
public class EmployeeHours
{
public int Id { get; set; }
public DateTime? ClockIn { get; set; }
public DateTime? ClockOut { get; set; }
public Employee emplyee { get; set; }
}
I'm having a difficult time understanding how I can have these two classes to interact with each other. Like if John Smith's PIN is 1234, and he enters his PIN into a textbox, how do I successfully add his clock in time and date to the employee hours class?
and if I have a view that looks like this for the employee to enter their PIN
#using (Html.BeginForm("ClockIn", "Login"))
{
#Html.LabelFor(c => c.Employee.PIN)
#Html.TextBoxFor(c => c.Employee.PIN)<br />
<button type="submit">Save</button>
}
and the clockIn controller looks like this
[HttpPost]
public ActionResult ClockIn(string Pin)//employee clocking in
{
_context.EmployeeHours.Add();
_context.SaveChanges();
return View();
}
I'm trying to figure out how to store the time and date associated with this employee in the class, so I can go back and see when this employee clocked in. Thanks!
Add a Navigation Property to Employee, EG:
public class Employee
{
public int Id { get; set; }
public string PIN { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<EmployeeHours> Hours { get; } = new HashSet<EmployeeHours>();
}
Then use it something like this:
[HttpPost]
public ActionResult ClockIn(string pin)//employee clocking in
{
var emp = _context.Employees.Where(e => e.Pin == pin).First();
var hours = new EmployeeHours();
hours.StartTime = DateTime.Now;
//...
emp.Hours.Add(hours);
_context.SaveChanges();
return View();
}
I have a question. I am new on ASP.NET MVC Code First.
I am creating a simple CRUD using Scaffold MVC.
My Model:
public class Manufacture
{
[Key]
public int ManufactureID { get; set; }
[Required]
[Column("Manufacture", TypeName = "varchar")]
[MaxLength(25)]
[Display(Name = "Manufacture")]
public string ManufactureCode { get; set; }
[Column("ManufactureDescription", TypeName = "varchar")]
[MaxLength(50)]
[Display(Name = "Description")]
public string ManufactureDescription { get; set; }
[Column("IsActive", TypeName = "bit")]
[Display(Name = "Active?")]
public bool IsActive { get; set; }
public DateTime CreatedDateTime { get; set; }
}
My Controller:
public ActionResult ManufactureCreate()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ManufactureCreate([Bind(Include = "ManufactureID,ManufactureCode,ManufactureDescription,IsActive,CreatedDateTime")] Manufacture manufacture)
{
if (ModelState.IsValid)
{
if (db.Manufactures.Any(ac => ac.ManufactureCode.Equals(manufacture.ManufactureCode)))
{
return View();
}
else
{
db.Manufactures.Add(manufacture);
db.SaveChanges();
return RedirectToAction("ManufactureCreate");
}
}
return View(manufacture);
}
I want to add a value on "CreatedDateTime" field using current DateTime. When User click the "Save" button on the View. "CreatedDateTime" field will be filled by current DateTime.
How can I do that?
Please advise.
Thank you.
manufacture.CreatedDateTime=DateTime.Now;
add above the line
db.Manufactures.Add(manufacture);
Modify your else block as below:
else
{
manufacture.CreatedDateTime=DateTime.Now;
db.Manufactures.Add(manufacture);
db.SaveChanges();
return RedirectToAction("ManufactureCreate");
}
and yes, you should use ViewModel here as Stephen suggested in the comment.
I am using autocomplete from JQueryUIHelpers in my Asp.Net MVC project with EF6.
Model structure:
public class Employee
{
[Key]
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string SecondName { get; set; }
[NotMapped]
public string FullName => FirstName + " " + SecondName;
public bool IsDriver { get; set; } = false;
public virtual ICollection<Delivery> Deliveries { get; set; }
}
public class Delivery
{
[Key]
public int Id { get; set; }
[Required]
public Employee Driver { get; set; }
public virtual ICollection<EggsMag> Eggs { get; set; }
}
EmployeeController:
public ActionResult Drivers(string term)
{
var drivers = _rep.GetAll(e => e.IsDriver && (e.FirstName.StartsWith(term) || e.SecondName.StartsWith(term)));
return Json((from d in drivers select new { label = d.FullName, value = d.Id }).ToList(), JsonRequestBehavior.AllowGet);
}
DeliveriesController:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ID,DateOfDelivery,Driver")] Delivery delivery)
{
if (ModelState.IsValid)
{
_rep.Save(delivery);
return RedirectToAction("Index");
}
return View(delivery);
}
View:
#Html.JQueryUI().AutocompleteFor(m => m.Driver.Id, Url.Action("Drivers", "Employees"), "DriverId", null)
Problem Description:
Autocomplete is working correctly but when in Edit view I send POST request I receive all the data, but ModelState.IsValid is false.
The error shows that fields of FirstName and SecondName are empty which is true because I sent just Id of existing Driver, not whole object.
Is there a way to fix it?
Maybe some way to change validation to not check inner model(Driver) fields except Driver.Id existence.
I have a single page on my MVC application. On this page, a user can enter their user name and password and click a login button, or the user can enter their first name, email address and click a signup button.
My initial thought was to create a ViewModel with UserName, Password, FirstName and Email properties, all with a [Required] attribute. Then having a view with two Html.BeginForm()'s. Although this would probably work, I have the feeling that when I post my data back to my controller, ModelState.IsValid will always return false since the ViewModel, is indeed, invalid.
So can someone tell me the proper way to handle a situation like this?
I have implemented very similiar scenario in my project i think the best way of achievie this is to create a viewmodel that will have 2 child viewmodel's inside something like this:
public class AuthModelView
{
public MemberLoginViewModel LoginModel { get; set; }
public MemberRegisterViewModel RegisterModel { get; set; }
[HiddenInput]
public string ReturnUrl { get; set; }
}
MemberLoginViewModel:
public class MemberLoginViewModel
{
[Required(ErrorMessage = "")]
[Display(Name = "")]
[EmailAddress]
public string Email { get; set; }
[DataType(DataType.Password)]
[Display(Name = "")]
[Required(ErrorMessage = "")]
public string Password { get; set; }
[Display(Name = "")]
public bool RememberMe { get; set; }
}
MemberRegisterViewModel:
public class MemberRegisterViewModel
{
[Required(ErrorMessage = "")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required(ErrorMessage = "")]
[DataType(DataType.Password)]
[Display(Name = "")]
public string Password { get; set; }
[Required]
public string RepeatPassword { get; set; }
[HiddenInput(DisplayValue = false)]
public string ReturnUrl { get; set; }
}
Then you create the View that will render 2 partial views
#Html.Partial("MemberLoginSummary", Model)
#Html.Partial("MemberRegisterSummary", Model)
Where the "Model" is your parent Viewmodel, then you will have 2 separated forms
in one view. In your partial view you simply do something like :
#Html.TextBoxFor(m => m.LoginModel.Email, null, new { #class = "form-control", placeholder = "email", id="Email" })
You can define 2 view models.
Login view model:
public class LoginViewModel
{
[Requried]
public string UserName { get; set; }
[Requried]
public string Password { get; set; }
}
Register view model:
public class RegisterViewModel
{
[Requried]
public string UserName { get; set; }
[Requried]
public string Password { get; set; }
[Requried]
public string FirstName{ get; set; }
[Requried]
public string Email { get; set; }
}
Login view:
#Html.BeginForm("Login", "Account", FormMethod.Post)
{
<!-- login form implements... -->
}
Register view:
#Html.BeginForm("Register", "Account", FormMethod.Post)
{
<!-- register form implements... -->
}
Controller:
public IActionResult Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
}
}
public IActionResult Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
}
}
Or, if you want only 1 model. Try to remove [Required] attribute. You can check it inside the action. Like this:
public IActionResult Login(YourViewModel model)
{
if (!string.IsNullOrEmpty(model.UserName) && !string.IsNullOrEmpty(model.Password))
{
}
}
public IActionResult Register(YourViewModel model)
{
if (!string.IsNullOrEmpty(model.UserName) &&
!string.IsNullOrEmpty(model.Password) &&
!string.IsNullOrEmpty(model.FirstName) &&
!string.IsNullOrEmpty(model.Email))
{
}
}
Hope this help!
If you want to try different library then you can go with FluentValidation
FluentValidation provides elegant way to customize validation for single class for different methods.
For e.g
[Validator(typeof(LoginRegisterModelValidator))]
public class LoginRegisterViewModel
{
public string UserName { get; set; }
public string Password { get; set; }
public string FirstName{ get; set; }
public string Email { get; set; }
}
You can have multiple rules defined for different actions
The validator class would look like this
public class LoginRegisterModelValidator : AbstractValidator<LoginRegisterViewModel>
{
public RegistryAddEditModelValidator()
{
/* Define the rule set to call them specifically inside contrller action parameter with CustomizeValidator Attribute */
RuleSet("LoginRuleSet", LoginRuleSet);
RuleSet("RegisterRuleSet", RegisterRuleSet);
}
protected void LoginRuleSet()
{
RuleFor(x => x.UserName).NotEmpty();
RuleFor(x => x.Password).NotEmpty();
}
protected void RegisterRuleSet()
{
RuleFor(x => x.Email).NotEmpty();
RuleFor(x => x.FirstName).NotEmpty();
}
}
The Controller Action would look like this
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login([CustomizeValidator(RuleSet = "LoginRuleSet")] LoginRegisterViewModel model)
{ ...
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Register([CustomizeValidator(RuleSet = "RegisterRuleSet")] LoginRegisterViewModel model)
{ ...
}
}
Hope this helps you to validate different rule with same class.
I believe the proper way is to have a ViewModel for each. Your underlying business logic and/or code talking to the database can still work with a single model if you want.
Your options are discussed well here.
Although I would add that having more than one ViewModel on an MVC page is a pain. Some guidance here on how.
So i'm working with MVC 4 and i have a question. I have two classes, Schedule and Simulation(its to simulate a student to enter the classroom). I have a view that gives all the schedules that exists next to a link to simulate entrance (it's another view, another class). I would like to pass the id from the schedule that the person chooses to a attribute in the Simulation class.
Class simulation:
namespace GestorSalas.Models
{
public class Simulation
{
public virtual int SimulationId { get; set; }
public virtual int ScheduleId { get; set; }
public virtual string Utilizador { get; set; }
[DisplayName("Tipo de Utilizador")]
public virtual string TipoUtilizador { get; set; }
public virtual int Codigo { get; set; }
public virtual string Hora { get; set; }
public virtual bool Entrar { get; set; }
}
Class Schedule:
namespace GestorSalas.Models
{
public class Schedule
{
public virtual int ScheduleId { get; set; }
public virtual int DisciplinaId { get; set; }
public virtual int SalaId { get; set; }
[Required]
public virtual int Dia { get; set; }
[Required]
[DisplayName("Hora de Entrada")]
public virtual string HoraEntrada { get; set; }
[Required]
[DisplayName("Hora de Saida")]
public virtual string HoraSaida { get; set; }
public virtual Disciplina Disciplina { get; set; }
public virtual Sala Sala { get; set; }
}
This is the view: (what the user sees)
(Entrar na sala=link to the simulation create view, in this image is the schedule index view).
I would like to pass the id from the schedule in order to appear in the simulation form (or in the table after the creation, much like when we click details or edit and it takes the user id but i want the schedule id).
This is the code in the "Entrar em sala" link:
#Html.ActionLink("Entrar em sala", "Create", "Simulation", new {id = item.HorarioId }, null)
But it doesn't work. Any ideas on how can i do this?
EDIT: The controllers:
Simulation:
To create
//
// GET: /Simulacao/Create
public ActionResult Create()
{
return View();
}
//
// POST: /Simulacao/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Simulation simulation)
{
if (ModelState.IsValid)
{
db.Simulacaos.Add(simulation);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(simulation);
}
And the schedule is just the index that is being used here:
public ActionResult Index()
{
var schedules= db.Schedules.Include(h => h.Disciplina).Include(h => h.Sala);
return View(schedules.ToList());
}
Your create method should accept the id as a parameter and use it as needed.
public ActionResult Create(int id)
{
// Id has the scheduleId
// to do : Do something with the Id passed in and return something
}
and in your index view, you need to pass the scheduleId as value of route param Id
#model List<Schedule>
<h2>Simulator</h2>
<table>
<tr><th>HoraEntrada </th><th>Dia </th><th></th></tr>
#foreach(var item in Model)
{
<tr>
<td>#item.HoraSaida</td>
<td>#item.Dia</td>
<td>#Html.ActionLink("Entrar em sala", "Create", "Simulation",
new {#id = item.ScheduleId}, null)
</td>
</tr>
}
</table>