why setDisallowedFields for id? -- Spring petclinic example - spring

From the Spring API, i understood that #InitBinder is used to initialize some binding rules..
In the petclinic example why we have setdisallowed("id")? when the id is not displayed on the form?
#InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id");
}
The id field is not displayed on the web page then why we are using the above code?
can we have some thing like this:
#InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("FirstName");
}
as per the above code the first name field of the owner object will not be set though user enters on the form? Is that correct?
link for the source

Because it can still be submitted if the end-user modifies the page or the request (for example using FireBug). Thus he can inject values into your bound object even if you don't want this.

Related

#InitBinder in spring boot not working with #RequestBody

If I use #InitBinder without limiting it,it is working fine with #RequestBody to validate my objects.
#InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
#RequestMapping(method=RequestMethod.POST)
public CustomerQuickRegisterEntity saveNewCustomer(#Valid #RequestBody CustomerQuickRegisterEntity customerEntity,BindingResult result)
{
if(result.hasErrors())
{
return new CustomerQuickRegisterEntity();
}
return customerQuickRegisterRepository.save(customerEntity);
}
But problem is that when i limit it to just one object by doing it as #InitBinder("customerEntity") it is not validating the object. So I have searched through stackoverflow and found that #InitBinding only works with objects annotated with #ModelAttribute. Then my question is that it is working fine with #RequestBody when I use it as #InitBinder but does not work well when I use it as #InitBinder("customerEntity") ...why is it so?
Is there any other way to validate Objects(Not properties of object Individually) associated with #RequestBody
This is an old question, but I've managed to get the #InitBinder annotation to bind my custom Validator to a #Valid #RequestBody parameter like this:
#InitBinder
private void bindMyCustomValidator(WebDataBinder binder) {
if ("entityList".equals(binder.getObjectName())) {
binder.addValidators(new MyCustomValidator());
}
}
If you try to filter the bound argument by setting the value of the annotation, then it won't work for a #RequestBody argument. So here I check the object name instead. My method parameter is actually called entities, but Spring had decided to call it entityList. I had to debug it to discover this.
From the docs,
Default is to apply to all command/form attributes and all request
parameters processed by the annotated handler class. Specifying model
attribute names or request parameter names here restricts the
init-binder method to those specific attributes/parameters, with
different init-binder methods typically applying to different groups
of attributes or parameters.
Please have a look here
You can try my solution:
#InitBinder
private void initBinder(WebDataBinder binder) {
if (CustomerQuickRegisterEntity.class.equals(binder.getTarget().getClass())) {
binder.addValidators(new YourValidator());
}
}

ObjectId Model Binding with Web Api 2

I'm using MongoDB with an ASP.NET Web Api (2) application, and want to accept ObjectId arguments in the Web Api methods.
I've written a custom model binder for the ObjectId type, and it when adding it to the Get method of a controller, everything works.
[Route("{id}")]
public HttpResponseMessage Get(String type, [ModelBinder(typeof(ObjectIdModelBinder))]ObjectId id) {
But I need to do this in several methods and controllers, so I would rather put it somewhere central. I've read that I can register the binder centrally like this:
public static void Register(HttpConfiguration config) {
var provider = new SimpleModelBinderProvider(typeof(ObjectId), new ObjectIdModelBinder());
config.Services.Insert(typeof(ModelBinderProvider), 0, provider);
}
But that's not working!
Any ideas? Not really sure what the config.Services collection should contain, but I'm having a hard time locating the binder I insert.
With WebAPI even if you register a model binder, you still need to attach the [ModelBinder] to the input parameter, you just don't have to specify the type anymore so your method now looks like this:
[Route("{id}")]
public HttpResponseMessage Get(String type, [ModelBinder]ObjectId id) {
If you want to skip adding the attribute every time you declare a parameter of this type, then you have to look at writing a custom IActionValueBinder (which can be made very simple if you just extend the DefaultActionValueBinder) which is the default implementation. You might want to look at this post for pointers:
http://www.strathweb.com/2013/04/asp-net-web-api-parameter-binding-part-1-understanding-binding-from-uri/

WebApi action parameters validation by ValidationAttribute

Does WebAPI can handle ValidationAttribute on action parameter?
For instance:
public class SampleController : ApiController
{
public string Get([RegularExpression("sampleExpression")]string id)
{
return "result";
}
}
In this sample WebAPI doesn't invoke any methods of RegularExpressionAttribute (or any other data annotation attribute) to validate input parameter. But in case if we passing an object as parameter, for instance a class then WebAPI can validate properties.
Is it a bug? Or I'm doing something wrong?
UPD: It's an open issue:
http://aspnetwebstack.codeplex.com/workitem/24?PendingVoteId=24
Does anyone can suggest a workaround?
This is a genuine question, I'm curious why not just do something like :
public class SampleController : ApiController
{
public string Get(string id)
{
RegularExpressionHelper.ValidateWith("sampleExpression",id);
return "result";
}
}
To me this seems to be equivalently concise. It is obvious when the validation occurs. I can safely assume that if the validation fails then an exception will be thrown. I can easily add additional validation and be confident of the order in which they occur.
Regarding the attribute method, I don't know if the validation is used as part of the route matching, I have no idea what happens if it fails. I don't know what filters run before or after the validation. I'm sure if I knew MVC better I would know the answer to these questions, but I don't see the advantage of using an attribute that makes my code's behaviour dependent on some framework controlled infrastructure.
Am I missing some significant benefit?
I had the same doubt. My workaround consists in creating a class just for encapsulating the parameter, so I can decorate it with the validation attribute I want. I could use the workaround proposed by Darrel in his answer, but I have a filter that checks if ModelState.IsValid before entering the action, so I need to validate before the action gets executed.
[ModelBinder]
public class Item
{
[RegularExpression("sampleExpression")]
public string Id { get; set; }
}
The class must be annotated with [ModelBinder], otherwise the parameter binding mechanism will try to extract the id field from the body of the request. Read this article for more info.
Also, note that Id is now in PascalCase instead of camelCase. Read this article to understand how the conversion is made.
The action signature is:
public string Get(Item item)

Why is my Spring 3 Validator Validating Everything on the Model?

I have a spring 3 controller with a validator for one of the methods. It insists on validating every object on the model. Would anyone be able to explain to me why it does this or if I'm doing something wrong?
According to the docs, 5.7.4.3 Configuring a JSR-303 Validator for use by Spring MVC (http://static.springsource.org/spring/docs/3.0.0.RC3/spring-framework-reference/html/ch05s07.html)
With JSR-303, a single javax.validation.Validator instance typically validates all model objects that declare validation constraints. To configure a JSR-303-backed Validator with Spring MVC, simply add a JSR-303 Provider, such as Hibernate Validator, to your classpath. Spring MVC will detect it and automatically enable JSR-303 support across all Controllers.
Example:
#Controller
public class WhaleController {
#Autowired
private Validator myValidator;
#Autowired
private WhaleService whaleService;
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(this.myValidator);
}
#RequestMapping(value="/save-the-whales")
#Transactional
public void saveTheWhales(#Valid WhaleFormData formData, BindingResult errors, Model model) {
if (!errors.hasFieldErrors()) {
Whale whale = new Whale();
whale.setBreed( formData.getBreed() );
this.whaleService.saveWhale( whale );
model.addAttribute("whale", whale);
}
model.addAttribute("errors", errors.getFieldErrors());
}
}
When run it will complain that Whale is an invalid target for myValidator (which is set to validate WhaleFormData, and does so fine). Whale is a POJO with no validation constraints, annotation and no config anywhere. Through trial and error I've found that ANY object placed on the model will attempt to be validated and fail if the validator is not setup to handle it. Primitives are just fine.
Can anyone tell me why this is, point me to the appropriate documentation and/or tell me the best way to put something on the model without having it validated?
In the case above I would like to place "whale" on the model as it will now have a unique whaleId() that it received from my persistence layer.
Thanks!
I guess this behaviour is not covered in the documentation well.
The problem is caused by the following:
By default, #InitBinder-annotated method is called for each non-primitive model attribute, both incoming and outcoming (the purpose of calling it for outcoming attibutes is to allow you to register custom PropertyEditors, which are used by form tags when rendering a form).
DataBinder.setValidator() contains a defensive check that call Validator.supports() and throws an exception if false is returned. So, there is no attempt to perform a validation, just an early check.
The solution is to restrict the scope of #InitBinder to particular attribute:
#InitBinder("whaleFormData")
protected void initBinder(WebDataBinder binder) { ... }

Spring - binding to an object rather than a String or primitive

Let's say I have the following command object:
class BreakfastSelectCommand{
List<Breakfast> possibleBreakfasts;
Breakfast selectedBreakfast;
}
How can I have spring populate "selectedBreakfast" with a breakfast from the list?
I was figuring I'd do something like this in my jsp:
<form:radiobuttons items="${possibleBreakfasts}" path="selectedBreakfast" />
But this doesn't seem to work. Any ideas?
thanks,
-Morgan
The key to it all of this is the PropertyEditor.
You need to define a PropertyEditor for your Breakfast class and then configure the ServletDataBinder using registerCustomEditor in the initBinder method of your controller.
example:
public class BreakfastPropertyEditor extends PropertyEditorSupport{
public void setAsText(String incomming){
Breakfast b = yourDao.findById( Integer.parseInt(incomming));
setValue(b);
}
public String getAsText(){
return ((Breakfast)getValue()).getId();
}
}
note you'll be needing some null checking etc, but you get the idea. In your controller:
public BreakfastFooBarController extends SimpleFormController {
#Override
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) {
binder.registerCustomEditor(Breakfast.class, new BreakfastPropertyEditor(yourDao));
}
}
things to watch out for:
PropertyEditor's are not thread safe
if you need spring beans, either manually inject them or define them in spring as prototype scope and use method injection into your controller
throw IllegalArgumentException if the inbound parameter is not valid/not found, spring will convert this into a binding error correctly
hope this helps.
Edit (in response to the comment):
It looks a little strange in the given example because BreakfastSelectCommand doesn't look like an entity, I'm not sure what the actual scenario you have is. Say it is an entity, for example like Person with a breakfast property then the formBackingObject() method would load the Person object from the the PersonDao and return it as the command. The binding phase would then change the breakfast property depending on the selected value, such that the command that arrives in onSubmit has the breakfast property all set up.
Depending on the implementation of your DAO objects calling them twice or attempting to load the same entity twice doesn't actually mean that you will get two SQL statements being run. This applies particularly to Hibernate, where it guarantees that it will return the same object that is in it's session for a given identifier, thus running letting the binding attempt to load the Breakfast selection even through it hasn't changed shouldn't result in any undue overhead.

Resources