I have an project that I have to do in mvc pattern. But I'm confused. If my model only contains method for manipulating the database and do not have any attribute, will that be the right way ?
For example, for login :
The model will have a method
static insertAccount(String username, String password) {
// the code to insert username and password to the database
}
And when the user push "submit", the form will pass the username and password to the controller, which has this method
createNewAccount(String username, String password) {
Model.insertAccount(username,password);
}
Your model should not contain methods to insert data. That should be abstracted out to the Data Access Layer of your application. The MVC pattern is for interacting with the UI.
The controller will pass data (a model) to be rendered. After some type of action, the view will pass data (a model) back to the controller.
From the controller, you can run some business logic, make calls to the data access layer, call another API. And, if nessecary, pass that model to the other layer.
Related
I am pretty new in Spring MVC and I have the following doubt about how correctly achieve the following task.
I am working on a web application that implement a user registration process. This registration process is divided into some consecutive steps.
For example in the first step the user have to insert a identification code (it is a code that identify uniquely a user on some statal administration systems) and in the second step it have to compile a form for his personal data (name, surname, birth date, and so on).
So, actually I have the following controller class that handle these steps:
#Controller
public class RegistrazioneController {
#Autowired
private LoadPlacesService loadPlacesService;
#RequestMapping(value = "/iscrizioneStep1")
public String iscrizioneStep1(Model model) {
return "iscrizioneStep1";
}
#RequestMapping(value = "/iscrizioneStep2", method=RequestMethod.POST)
public String iscrizioneStep2(Model model, HttpServletRequest request, #RequestParam("cf") String codicFiscale) {
System.out.println("INTO iscrizioneStep2()");
//String codicFiscale = request.getParameter("cf");
System.out.println("CODICE FISCALE: " + codicFiscale);
model.addAttribute("codicFiscale", codicFiscale);
return "iscrizioneStep2";
}
#RequestMapping(value = "/iscrizioneStep3", method=RequestMethod.POST)
public String iscrizioneStep3(#ModelAttribute("SpringWeb")Step2FormCommand step2Form, ModelMap model, HttpServletRequest request) {
System.out.println("INTO iscrizioneStep3()");
System.out.println("NOME: " + step2FormCommand.getName());
return "iscrizioneStep3";
}
Into the iscrizioneStep2() it is retrieved the first code (#RequestParam("cf") String codicFiscale).
Into the iscrizioneStep3() it is retrieved a command object containing the data inserted into the form of the view in which this form was submitted, this one:
#ModelAttribute("SpringWeb")Step2FormCommand step2FormCommand
It works fine.
Now my problem is that I have another object named Step3View that have to be initialized with the aggregation of the #RequestParam("cf") String codicFiscale object retrieved into the iscrizioneStep2() method and the #ModelAttribute("SpringWeb")Step2FormCommand step2FormCommand retrieved into the iscrizioneStep3() method.
This Step3View class simply contain the String codicFiscale and all the fields of the Step2FormCommand class.
Now my doubts are: what is the best way to handle this situation? Where have I to declare this Step3View object? at controller level? (so I can use it in all my controller methods?). Have I to annotate this class with #Component (or something like this) to inject it in my controller?
What is the best solution for this situation?
I think in order to get an answer you need to understand the question and ask the right question. I think your question is "how do I pass a parameter from one page to another page in SpringMVC?". You specifically want to know how to pass the "cf" param, but readers here will tend to pass over questions that are too specific because it takes too much time to figure out what you want.
In answer to that, see Spring MVC - passing variables from one page to anther as a possible help.
Also, there are many good answers about this question for JSP in general, which can be worked into the SpringMVC architecture. See How to pass value from one jsp to another jsp page? as a possible help.
In my Spring MVC Project I created a registration page.In this page there is a form in which the user insert his information(name,surname and so on).I have used the Spring tag form to bind the object "cliente" to the form. In my controller I have:
#RequestMapping(value="/registration",method=RequestMethod.GET)
public String viewRegistration(ModelMap model){
model.addAttribute("cliente",clienteFactory.createCliente());
return "registrazione";
}//registrazione
In registration.jsp
<form:form method="post" action="add" modelAttribute="cliente">
....
</form:form>
In this project I have not used Spring Security,because I'm a student and I hadn't learned this part yet.
If the user leaves the page without register, I want to delete the object "Cliente" from the model.How can I solve it?Thanks
At first you don't need to delete object from model as if client leaves page with mapping /registration model ref will be overriden with model of another mapping method.
Second in more cases it's no good idea to call your method clienteFactory.createCliente()) in GET request method. Better to call it in POST after user fill all form fields and post his request than you know that you need to call clientFactory.Also use #ModelAttribute annotation as method argument.
As you have in your form form:form method="post" it will not working without such method
#RequestMapping(value="/registration",method=RequestMethod.POST)
public String makeRegistration(ModelMap model){
.....
Also see simple Sring tutorial for handling forms.
If I have some code which is will HTTP request a remote website and extract data from it, or login to this website and extract data from it; what would I put this under? The model or the controller? I'm think that it is a model, because the controller should talk to the user and call upon my models to do stuff.
Is this correct?
Model is correct. The data crunching portion should be left to the models. The controllers direct the traffic between the models and views. Finally, views are used to display the data.
You would probably do this a view or controller. If you will need to do any large manipulation with the data, you could create and action that makes a request and then manipulates the data and then pass that data onto a view. If you will need simpler operations, and depending on your framework, you can make the request in the view itself.
You would possibly use all of the View, Model and controller. But if you don't want to display the result, it would likely be just the controller and model.
For example, the controller code would have an ActionResult method which would call your remote website.
e.g. your model
public class RemoteWSData
{
public string Code { get; set; }
public string Name { get; set; }
}
your controller:
public ActionResult GetRemoteWebsiteData()
{
RemoteWSData model = repository.GetRemoteWebsiteData();
string code = model.Code;
string name = model.Name;
}
with the repository above being a data access class. This data access class would contain references to the connections required to access the data or server and get these values from a web.config file.
MVC best practices state that the model should handle input/data validation. Let's say that we have a model that creates new user accounts, with the following fields and constraints:
Username - not null, not already in DB
Password - not null, alphanumeric only
E-mail - not null, not already in DB, valid e-mail format
We have an AccountModel with a CreateNewUser() function:
component
{
public void function CreateNewUser(string username, string password, string email)
{
// create account
}
}
Then we have a controller that processes a form post and tells the model to create the account:
component
{
public void function NewUser()
{
var username = event.getValue("username");
var password = event.getValue("password");
var email = event.getValue("email");
var accountModel = new AccountModel();
accountModel.CreateNewUser(username, password, email);
event.addResult("UserCreated");
}
Now I want to add validation. If the user fails to provide input for all three fields, the application should show the user three validation error messages. This is easy enough to do in the controller:
// assumes that ValidateInput() is a function on the controller that returns an array
var validationErrors = ValidateInput(username, password, email);
// if there were any validation errors, return a ValidationError result
if (len(validationErrors)
{
event.setValue("validationerrors", validationErrors);
event.addResult("ValidationError");
}
else
{
event.addResult("UserCreated");
}
And the view will pull the validationerrors variable and display the error messages.
However, this logic is supposed to reside in the model. How do I do this? I can think of two ways:
Method 1: Move ValidateInput() from the controller to the model. Now the controller has to call ValidateInput() first before CreateNewUser(). If the programmer forgets to do this, CreateNewUser() will throw a validation exception. The downside to this is that now every data operation that requires validation will need an if/else block.
Method 2: Forego having to call ValidateInput() and just call CreateNewUser() directly. If there were any validation errors, an exception will be thrown and it will contain the error message array. This method would work in C#, but it looks like ColdFusion does not support returning of data with the exception, only an exception message and type. Also, every data operation will require a try/catch block.
Method 3: ??
How would you handle validation in this case? Is method 1 what most people do when it comes to validation in the model? Or do people typically use method 2? Or is there a method 3 that I'm not thinking of?
I don't think you should couple the validation of the user's data entry to the creation of the user's account: they are too different things.
If you couple the two together, it means you're basically doing form validation every time you create an account, which doesn't seem right to me.
I see form validation as a UI concern, more than a concern of the objects that might be ultimately created from that data. Obviously your createNewUser() method has its own business rules (which will probably closely mirror that of the validation for a create-new-user form, but they are still separate concerns).
It is possibly a bit unorthodox, but I will put a validateUserData() (or something) method in my User CFC which the form-validation model then calls to help with the validation. This means the business rules for a user are in the same place, and can be called separately. Thereafter, createNewUser() works on a garbage-in/garbage-out principle.
Sidenote: createNewUser() is a bit of a tautological name, innit? What other sort of user would you be creating other than a new one? Also, if it's in your Account.cfc; is it a new user or a new account that's being created? If an account and a user are not synonymous and an account might exist without a user (or vice-versa), perhaps you ought to have a User.cfc too. Then again, this code you've given us could simply be for the purposes of illustration, and you've got all this covered.
I was researching something and came across this blog post at buildstarted.com about model binders. It actually works pretty darn well for my purposes but I am not sure exactly whats going on behind the scenes. What I did was create a custom ModelBinder called USerModelBinder:
public class UserModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ValueProviderResult value = bindingContext.ValueProvider.GetValue("id");
MyEntities db = new MyEntities();
User user = db.Users.SingleOrDefault(u => u.UserName == value.AttemptedValue);
return user;
}
}
Then in my Global.asax.cs I have:
ModelBinders.Binders.Add(typeof(User), new UserModelBinder());
My understanding is that using the model binder allows me to NOT have to use the following lines in every controller action that involves a "User". So instead of passing in an "id" to the action, the modelbinder intercepts the id, fetches the correct "item"(User in my case) and forwards it to the action for processing.
MyEntities db = new MyEntities();
User user = db.Users.SingleOrDefault(u => u.UserName == value.AttemptedValue);
I also tried using an annotation on my User class instead of using the line in Global.asax.cs:
[ModelBinder(typeof(UserModelBinder))]
public partial class User
{
}
I'm not looking for a 30 page white paper but I have no idea how the model binder does what it does. I just want to understand what happens from when a request is made to the time it is served. All this stuff "just working" is not acceptable to me, lol. Also, is there any difference between using the annotation versus adding it in Global.asax.cs? They seem to work the same in my testing but are there any gotchas?
Usually the Model Binder (in MVC) looks at you Action method and sees what it requires (as in, the objects types). It then tries to find the values from the HTTP Request (values in the HTTP Form, QueryString, Json and maybe other places such as cookies etc. using ValueProviders). It then creates a new object with the parameters that it retrieves.
IMO What you've done is not really "model binding". In the sense that you've just read the id and fetched the object from the DB.
example of usual model binding:
// class
public class SomeClass
{
public int PropA {get;set;}
public string PropB {get;set;}
}
// action
public ActionResult AddSomeClass(SomeClass classToBind)
{
// implementation
}
// pseudo html
<form action="">
<input name="PropA" type="text" />
<input name="PropB" type="text" />
</form>
if you post a form that contains the correct values (lets say you post a form with PropA and PropB ) the model binder can identify that you've sent those values in the form and build a SomeClass object.
If you really want to create a real working example you should use a strongly typed View and use HtmlHelper's EditorFor (or EditorForModel) to create all the correct names that MVC needs.
--
for reference MVC's default binder is the DefaultModelBinder, and some (there are more, you can look around in the System.Web.Mvc namespace) ValueProviders that it uses by default are the FormValueProvider and the QueryStringValueProvider
So, as I already said, how this basically works is that the default model binder reads the model that the action is recieving (say SomeClass in the example) reads what are the values that it can read (say PropA and PropB) and asks the ValueProviders for the correct values for the properties.
Also, if I recall correctly, you can also see the value providers in runtime using the ValueProviderFactories static class.
A ModelBinder looks at the arguments of the selected Controller action's method signature, then converts the values from the ValueProviders into those arguments.
This happens when the ControllerActionInvoker invokes the action associated with the ControllerContext, because the Controller's Execute() method told it to.
For more about the ASP.NET MVC execution process, see Understanding the MVC Application Execution Process