I am following Spring in Action, 5th edition, and in chapter 3 I am facing this issue:
#PostMapping
public String processDesign(#Valid Taco design, Errors errors, #ModelAttribute Order order) {
if (errors.hasErrors()) {
return "design";
}
Taco saved = designRepo.save(design);
order.addDesign(saved);
return "redirect:/orders/current";
}
On Submit your Taco action, a request goes to the above method, but the ingredients field in Taco object is null.
My Taco class is the same as provided here.
So, what am I missing?
You need to generate the list of ingredients in case of error (again).
#PostMapping
public String designForm(#ModelAttribute("design") #Valid Taco taco, Errors errors, Model model) {
if (errors.hasErrors()) {
List<Ingredient> ingredients = ingredientRepository.findAll();
Type[] types = Ingredient.Type.values();
for (Type x : types) {
model.addAttribute(x.toString().toLowerCase(),
ingredients.stream().filter(p -> p.getType().equals(x)).collect(Collectors.toList()));
}
return "design";
}
log.info("Processing desing: " + taco);
return "redirect:/order/current";
}
Removing
model.addAttribute("order",new Order());
in OrderController would fix the issue.
TacoController is passing the model with order instance populated, however the get method in order controller, specifically the above line add a new order to model, overwriting the old order details.
I too am going over Spring in Action 5, chapter 3. I had an issues with my "ingredients" field being null as well. I have managed to fix this.
Somewhere in Ch3, the code for "ingredients" changed types. In your Taco class, "ingredients" should be List (as seen from ch2); however this changed to List in Ch3 without any mention. I found out about that change looking at the chapter's source code. Anyhow, because the list's type switched from String to Ingredient, Spring will no longer bind the list of ingredients from the form.
My fix is this:
1 - revert the Taco class' "ingredient" field to be a List data type.
2 - in the JdbcTacoRepository class, make the following changes:
2a - add a new private final field of type IngredientRepository
2b - update the constructor to autowire the above field
2c - within the save(Taco taco) method, use the following changes:
for(String ingredientId : taco.getIngredients()){ Ingredient ingredient = ingredientRepo.findOne(ingredientId); saveIngredientToBurger(ingredient, burgerId); }
Overall the ingredients field was null b/c Spring couldn't bind a String object to an Ingredient object. I'm still trying to understand how Spring recognizes the ingredient in the first place, but hopefully this works for you.
Related
Good afternoon.
I am trying to generate Swagger documentation but fails to generate clean result if the response is not a simple class.
if we consider a user with family members, the following REST function as a partial documentation
public ResponseEntity<FamilyMembersResponse> getUserFamily(#PathVariable("user_uuid") String UUID) {
...
FamilyMembersResponse response= new FamilyMembersResponse();
...
return new ResponseEntity<>(response, HttpStatus.OK);
}
By partial, I mean that swagger will say that result is of type FamilyMembersResponse (that is correct) but the FamilyMembersResponse class itself is not documented (return without any attributes).
The issue may be that the class FamilyMembersResponse is created within the controler but even with such definitions the class description is always empty:
class FamilyMembersResponse {
#Schema(name = "user" )
User user;
#Schema(name = "family_members" )
List<FamilyMember> family_members;
}
Any idea why? Issue seems "only" on the generation of FamilyMembersResponse "class", not the route
Just found the answer... as in simply posting in SO after looking for hours helped solving the issue (why, that is the question :D).
JsonProperty was expected, .. which kind of makes sense.
#JsonProperty("user")
I have the following entities:
Area
Listing
They are both many-to-many:
An area can have many listings
A listing can have many areas
Both Area and Listing have other fields like name, domain, etc.
I'm using Spring Web RestController as a way to update the entities.
For example:
#PutMapping("/{id}")
public Area update(#PathVariable Long id, #RequestBody Area update) {
return areaRepository.save(update);
}
However, as an Area can have many thousands of Listings, it's not practical to pass them all in the update request when I just want to update the Area name and other basic fields in my web application.
For example, the update json in the http request would be:
{
"id" : 69,
"name" : "San Francisco",
"domain" : "foo",
...
}
When serialised, the area instance above will have a listings field equal to null (obviously) and then when saved, all association are remove from this Area.
I'm thinking that I could do a select-then-update set of operations and only update the values necessary but that is cumbersome - especially when there are many dozens of non-association fields.
The question would be: how can I try to keep to the above code and http request and not remove all of the existing Listing associations when saving the input area? Is this possible? I want to update the name and other basic fields but not the association fields.
You can use the BeanUtilBean(org.apache.commons.beanutils.BeanUtilsBean).
Step 1: Create custom beanutilbean class.
#Component
public class CustomBeanUtilsBean extends BeanUtilsBean {
#Override
public void copyProperty(Object dest, String name, Object value)
throws IllegalAccessException, InvocationTargetException {
if(value==null)return;
super.copyProperty(dest, name, value);
}
}
Step 2: In your controller while updating. first get the Area from database & use copyProperties method as shown in below.
#PutMapping("/{id}")
public Area update(#PathVariable Long id, #RequestBody Area update) {
Area areaDB = areaRepository.findOne(update.getId());
customBeanUtilsBean.copyProperties(areaDB,update);//it will set not null field values of update to areaDB.
return areaRepository.save(areaDB);
}
Hope this will helps you..:)
Since you are using spring-data-jpa repository you can write a new method that takes the changed values of Area object with #Modifying and #Query annotations on it.
In the #Query you specify the HQL query with update statement as in here
It seems like the preferred way to validate a spring annotated bean is by using #valid, like in the block below, however I want to display error messages one field at a time still using Spring annotations. I know I can validate the whole form after each field and show only messages for a single field, but that is inefficient, anyone know of a better way?
#RequestMapping(value="/register",method=RequestMethod.POST)
public #ResponseBody RegisterResponse registerSubmit(#Valid #ModelAttribute("registerData") Register registerData, BindingResult result ){
RegisterResponse rr= new RegisterResponse();
if(!result.hasErrors()) {
rr.setStatus("SUCCESS");
rr.setResult(result);
}
else {
rr.setStatus("FAIL");
rr.setResult(result.getAllErrors());
}
return rr;
}
Return from the Validator#validate method upon hitting the first Error.
For this
You need to have a Custom Validator implementing org.springframework.validation.Validator where you have control of validating the fields in the order you need, whereas, using #Valid annotation will validate all the fields in your Form Bean and return the BindingResult for all the fields.
I was able to make things work by filtering out the errors that had an empty/null fields, this does work but is a bit inefficient since we validate entire bean even though we care for the first field. I'd love to hear of a better way to accomplish this kind of step by step form field validation.
I used something like the following:
private List<ObjectError> removeEmpties(BindingResult result) {
List<ObjectError> errors = result.getAllErrors();
ArrayList<ObjectError> myErrors = new ArrayList<ObjectError>();
for(ObjectError error: errors){
FieldError realThing = (FieldError) error;
if (realThing.getRejectedValue()!=null && !realThing.getRejectedValue().toString().isEmpty()){
myErrors.add(error);
}
}
return myErrors;
}
I am working with a Spring MVC project and I can't figure out how to change the String representation of a Model in the Views.
I have a Customer model that has a ONE_TO_MANY relationship with a WorkOrder model. On the workorders/show.jspx the Customer is displayed as a String that is the first and last name, email address, and phone number concatenated.
How do I change this? I thought I could just change the toString method on the Customer, but that didn't work.
One solution would be to change/push-in show() to WorkOrderController to map a rendered-view-tag to what you would like to see.
#RequestMapping(value = "/{id}", produces = "text/html")
public String show(
#PathVariable("id") Long id,
Model uiModel)
{
final WorkOrder workOrder = WorkOrder.findWorkOrder(id);
uiModel.addAttribute("workOrder", workOrder);
uiModel.addAttribute("itemId", id);
// Everything but this next line is just ripped out from the aspectJ/roo stuff.
// Write a method that returns a formatted string for the customer name,
// and a customer accessor for WorkOrder.
uiModel.addAttribute("customerDisplay", workOrder.getCustomer().getDisplayName());
return "workorders/show";
}
Put/define a label in your i18n/application.properties file for customerDisplay.
Then in your show.jspx, you can access the mapping with something like... (The trick is similar for other views.)
<field:display field="customerDisplay" id="s_your_package_path_model_WorkOrder_customerDisplay" object="${workorder}" z="user-managed" />
I'm new to Roo, so I'd love to see a better answer.
We found a good solution. There are toString() methods for all the models in ApplicationConversionServiceFactoryBean_Roo_ConversionService.aj
You can just push the method for the Model you want into ApplicationConversionServiceFactoryBean.java and modify it. In my case I added this:
public Converter<Customer, String> getCustomerToStringConverter() {
return new org.springframework.core.convert.converter.Converter<com.eg.egmedia.bizapp.model.Customer, java.lang.String>() {
public String convert(Customer customer) {
return new StringBuilder().append(customer.getId()).append(' ').append(customer.getFirstName()).append(' ').append(customer.getLastName()).toString();
}
};
}
Spring uses this for all the view pages so this will change the String representation of your model across your whole app!
I'm developing an MVC3 application and I have a page (well, a view) that let the users edit document's metainfo (a classic #Html.BeginForm usage). For general documents users will see standard fields to fill up, but through a dropdownlist they will be able to specify the type of the document: this, through an ajax call, will load new fields on the edit-document-form.
Whem the user submit the completed form, at last, the controller should read all the standard fields, plus all the fields loaded as being specific to the type of document selected.
Question is, how can I handle all this extra fields in a controller?
Say that I have Document class and a bunch of other classes extendinf Document, like Contract : Document, Invoice : Document, Complaint : Document and so forth, each having specific property (and this fields loaded on the form), how do I write the action in the controller?
I thought to use something like (I'll omitt all the conversions, validations, etc, for brevity)
[HttpPost]
public ActionResult Save(dynamic doc)
{
int docType = doc.type;
switch (docType)
{
case 1:
var invoice = new Invoice(doc);
invoice.amount = Request.Form["amount_field"];
invoice.code = Request.Form["code_field"];
//and so forth for every specific property of Invoice
Repository.Save(invoice);
break;
case 2:
var contract = new Contract(doc);
contract.fromDate = Request.Form["fromDate_field"];
contract.toDate = Request.Form["toDate_field"];
//and so forth for every specific property of Contract
Repository.Save(contract);
break;
..... // and so forth for any document types
default:
break;
}
}
But it seems a very dirty approach to me. Do you have a better idea on how to achive this? Maybe there's a pattern that I don't know nothing about to approach this kind of scenario.
Update
A second idea comes to my mind. After commenting Rob Kent's answer, I thought I could take a different approach, having just one class Document with a property like
public IEnumerable<Field> Tipologie { get; set; }
where
public class Field
{
public int IdField { get; set; }
public String Label { get; set; }
public String Value { get; set; }
public FieldType ValueType { get; set; }
public List<String> PossibleValues { get; set; } // needed for ENUMERATION type
}
public enum FieldType
{
STRING, INT, DECIMAL, DATE, ENUMERATION
}
Is this a better approach? In this case I can have just an action method like
[HttpPost]
public ActionResult Save(Document doc)
But shoud I create the fields in the view in order to make the MVC engine do the binding back to the model?
Given that the class inheriting from Document in the first approach will probably be generated at run-time, would you prefer this second approach?
To keep it all hard-typed on the server, you could use an abstract base type with a custom binder. See my answer here to see how this works: MVC generic ViewModel
The idea is that every time they load a new set of fields, you change the BindingType form variable to the instantiated type of the handler. The custom binder is responsible for creating the correct type on submission and you can then evaluate that in your action, eg:
if (model is Contract) ...
I'm not sure if you will be able to set up different actions each with a different signature, eg,:
public ActionResult Save(Contract contract) ...
public ActionResult Save(Invoice invoice) ...
Pretty sure that won't work because Mvc will have already decided which method to call, or maybe it will firstly see what type it gets back and then decides.
In my linked example, I am checking for overridden base members but if that is not an issue for you, you just need to create the correct type.