Vaadin REST validation response - vaadin

When a REST endpoint returns an validation error I want to reflect the validation message in the corresponding vaadin input field. In this example I would like to have an error message near to the password input field. So the user is able to fix the error easily.
#Route("create-customer")
public class CreateCustomerView extends Div {
public CreateCustomerView(RestClientService restClientService) {
TextField firstName = new TextField("First name");
DatePicker birthDay = new DatePicker("Birthday");
PasswordField password = new PasswordField("Password");
Button submit = new Button("Submit", (ComponentEventListener<ClickEvent<Button>>) event -> restClientService.createCustomer(
new RegisterCustomerRequest(firstName.getValue(), birthDay.getValue(), password.getValue())));
FormLayout formLayout = new FormLayout();
formLayout.add(firstName, password, submit);
add(formLayout);
}
}
My repsonse is very standard and verbose.
{
"timestamp": "2022-09-17T12:39:55.642883700",
"status": 400,
"error": "ConstraintViolationException",
"message": "Some arguments are not valid.",
"fieldErrors": [
{
"field": "register.registerCustomerCommand.password",
"error": "com.example.application.service.RegisterCustomerService register.registerCustomerCommand.password: # a digit must occur at least once\n# a lower case letter must etc. etc."
}
],
"path": "/v1/customers/"
}
Is it possible with vaadin out of the box? Binder seems to be good for client side validation errors but my errors depend on the server side validation on the request.

Solved it like this:
introduced ServersideValidationException that is thrown if response from server contains the error "ConstraintViolationException". In this case I deserialize the content to an Error that contains List of fieldErrors with path and errorMessage and pass it to the Exception.
map these fieldErrors trough the binder to the errorMessage of a Component.
// button action
private void validateAndSave() {
if (binder.isValid()) {
try {
binder.writeBean(registerCustomerRequest);
final CustomerResponse customer = restClientService.createCustomer(registerCustomerRequest);
} catch (ValidationException e) {
throw new ApplicationException(e);
} catch (ServersideValidationException e) {
populateErrorsToForm(e);
}
}
}
private void populateErrorsToForm(ServersideValidationException e) {
e.getFieldErrors().forEach(fieldError -> {
final String fieldName = StringUtils.substringAfterLast(fieldError.getPath(), ".");
final var binding = binder.getBinding(fieldName).orElseThrow(fieldNotFoundInForm(fieldName));
BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(binding.getField());
wrapper.setAutoGrowNestedPaths(true);
wrapper.setPropertyValue("invalid", true);
wrapper.setPropertyValue("errorMessage", fieldError.getErrorMessage());
});
}
Sidenote: Binder is not required for setting the error but it provides handy method binder.getBinding(fieldName). Does not support nested errors yet it is feasible. It is ugly.

As long as you can get the validation answer quickly enough, you can just use Binder since it doesn't care about the origin of the validation result. If the response is slow, then you might have to validate asynchronously, but then you're on your own without direct help from Binder.

Related

What would be the best way to check whether all fields are valid?

I have fields in a Window, some with validators and all bound to properties.
The validation works as expected.
But -
I do not want to proceed when any field is invalid. What would be the best way to determine if any validation went wrong?
There are several ways of dealing with validation in Vaadin, all supported by Vaadin (no need for custom boolean afterValidationFlag).
One possible way (preffered by me) shown below:
public class CustomWindow extends Window {
DateField customBeanFirstPropertyName = new DateField("Caption1");
ComboBox customBeanSecondPropertyName = new ComboBox("Caption2");
TextArea customBeanThirdPropertyName = new TextArea("Caption3");
BeanFieldGroup<CustomBean> binder = new BeanFieldGroup<>(CustomBean.class);
public CustomWindow(CustomBean customBean) {
buildLayout();
binder.buildAndBindMemberFields(this);
binder.setItemDataSource(new BeanItem<>(customBean));
//add validators
customBeanFirstPropertyName.addValidator((Validator) value -> {
if (value == null) throw new Validator.InvalidValueException("nonnull required");
});
customBeanThirdPropertyName.addValidator(
new RegexpValidator(".{3,20}", "length between 3-20 required")
);
/*
or have basic validators on #Entity level with e.g. javax.validation.constraints.Size
example:
#Size(min = 3, max = 20)
#Column(name = "customBeanThirdPropertyName", unique = true)
private String customBeanThirdPropertyName;
*/
}
void commit(Button.ClickEvent event) { //method called by "save" button
try {
binder.commit(); //internally calls valid() method on each field, which could throw exception
CustomBean customBeanAfterValidation = binder.getItemDataSource().getBean(); //custom actions with validated bean from binder
this.close();
} catch (FieldGroup.CommitException e) {
Map<Field<?>, Validator.InvalidValueException> invalidFields = e.getInvalidFields(); //do sth with invalid fields
}
}
}
If you use a FieldGroup instance to bind your fields with the properties, which is the recommended way, you can write:
fieldGroup.isValid();
That checks on all field validations of the fields managed by the field group.
Maintain a flag. Before proceeding, check if the flag is set. In the validation code, set the flag if the validation fails.

MVC Post Request how to get Request Content if it's different to that expected by the Default ModelBinder

I have this MVC WebApi action:
PostTrips(List<Trip> trips)
When a list of trips is sent through everything works fine. If, however, someone is trying to post incorrect data, e.g just an object {} then trips is null - this is fine, but I would like to log the data that the user tried to push.
I tried to get it using string requestData = Request.Content.ReadAsStringAsync().Result; but it can only be called once, and I guess the default model binder is calling it to try an map it to my List<Trip>, as when I call it, the result is always null, even though I know I'm passing something in.
Does anyone know of another way to get the posted data again?
I got around this my removing the parameter List<Trip> trips from the action so I had:
public async Task<HttpResponseMessage> PostTrips()
{
}
This bypasses the default model binder and allows you to get the unmodified request content using:
string requestContent = await Request.Content.ReadAsStringAsync();
You can then do what ever you need with this - I wanted to log the data for error tracking.
To create the actual List<Trip> trips I then used Newtonsoft.Json to deserialise the string into a list:
List<TravelTrackerTrip> appTrips = JsonConvert.DeserializeObject<List<TravelTrackerTrip>>(requestContent);
Full example:
public async Task<HttpResponseMessage> PostTrips()
{
HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
List<Trip> appTrips = null;
string requestContent = await Request.Content.ReadAsStringAsync();
try
{
appTrips = JsonConvert.DeserializeObject<List<Trip>>(requestContent);
}
catch(Exception ex)
{
//ERROR LOGGING HERE...
//QUIT - Return failure response
}
try
{
//Success - do whatever we need
}
catch(Exception ex)
{
//ERROR LOGGING HERE...
//QUIT - Return failure response
}
//Return success response
}

XML Schema validation for POST requests with ASP.NET WebAPI

I am trying to find a solution to validate if XML data sent in a POST request are fulfilling a given custom XML schema.
If I use the XmlMediaTypeFormatter delivered with ASP.NET Web API I don't have a schema validation available, as far as I can see. For example: If I have a model type...
public class Order
{
public string Code { get; set; }
public int Quantity { get; set; }
}
...and a POST action in an ApiController...
public HttpResponseMessage Post(Order order)
{
if (ModelState.IsValid)
{
// process order...
// send 200 OK response for example
}
else
// send 400 BadRequest response with ModelState errors in response body
}
...I can post the following "wrong" XML data and will get a 200 OK response nevertheless:
User-Agent: Fiddler
Host: localhost:45678
Content-Type: application/xml; charset=utf-8
<Order> <Code>12345</Nonsense> </Order> // malformed XML
Or:
<Order> <CustomerName>12345</CustomerName> </Order> // invalid property
Or:
<Customer> <Code>12345</Code> </Customer> // invalid root
Or:
"Hello World" // no XML at all
etc., etc.
The only point where I have a validation of the request is model binding: In request example 1, 3 and 4 the order passed into the Post method is null, in example 2 the order.Code property is null which I could invalidate by testing for order == null or by marking the Code property with a [Required] attribute. I could send this validation result back in the response with a 400 "BadRequest" Http status code and validation messages in the response body. But I cannot tell exactly what was wrong and can't distinguish between the wrong XML in example 1, 3 and 4 (no order has been posted, that's the only thing I can see) - for instance.
Requiring that an Order has to be posted with a specific custom XML schema, for example xmlns="http://test.org/OrderSchema.xsd", I would like to validate if the posted XML is valid with respect to this schema and, if not, send schema validation errors back in the response. To achieve this I have started with a custom MediaTypeFormatter:
public class MyXmlMediaTypeFormatter : MediaTypeFormatter
{
// constructor, CanReadType, CanWriteType, ...
public override Task<object> ReadFromStreamAsync(Type type, Stream stream,
HttpContentHeaders contentHeaders, IFormatterLogger formatterLogger)
{
var task = Task.Factory.StartNew(() =>
{
using (var streamReader = new StreamReader(stream))
{
XDocument document = XDocument.Load(streamReader);
// TODO: exceptions must the catched here,
// for example due to malformed XML
XmlSchemaSet schemaSet = new XmlSchemaSet();
schemaSet.Add(null, "OrderSchema.xsd");
var msgs = new List<string>();
document.Validate(schemaSet, (s, e) => msgs.Add(e.Message));
// msgs contains now the list of XML schema validation errors
// I want to send back in the response
if (msgs.Count == 0)
{
var order = ... // deserialize XML to order
return (object)order;
}
else
// WHAT NOW ?
}
});
return task;
}
}
This works so far as long as everything is correct.
But I don't know what to do if msgs.Count > 0. How can I "transfer" this validation result list to the Post action or how can I create a Http response that contains those XML schema validation messages?
Also I am unsure if a custom MediaTypeFormatter is the best extensibility point for such a XML schema validation and if my approach isn't the wrong way. Would possibly a custom HttpMessageHandler/DelegatingHandler be a better place for this? Or is there perhaps something much simpler out of the box?
If I were doing this I wouldn't use the Formatter. The primary goal of a formatter is to convert a wire representation to a CLR type. Here you have an XML document that you want to validate against a schema which is a different task altogether.
I would suggest creating a new MessageHandler to do the validation. Derive from DelegatingHandler and if the content type is application/xml load the content into XDocument and validate. If it fails, then throw a HttpResponseException.
Just add your MessageHandler to the Configuration.MessageHandlers collection and you are set.
The problem with using a derived XmlMediaTypeFormatter is that you are now executing at some point embedded inside the ObjectContent code and it is likely to be tricky to cleanly exit out. Also, making XmlMediaTypeFormatter any more complex is probably not a great idea.
I had a stab at creating the MessageHandler. I did not actually try running this code, so buyer beware. Also, the task stuff gets pretty hairy if you avoid blocking the caller. Maybe someone will clean that code up for me, anyway here it is.
public class SchemaValidationMessageHandler : DelegatingHandler {
private XmlSchemaSet _schemaSet;
public SchemaValidationMessageHandler() {
_schemaSet = new XmlSchemaSet();
_schemaSet.Add(null, "OrderSchema.xsd");
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
if (request.Content != null && request.Content.Headers.ContentType.MediaType == "application/xml")
{
var tcs = new TaskCompletionSource<HttpResponseMessage>();
var task = request.Content.LoadIntoBufferAsync() // I think this is needed so XmlMediaTypeFormatter will still have access to the content
.ContinueWith(t => {
request.Content.ReadAsStreamAsync()
.ContinueWith(t2 => {
var doc = XDocument.Load(t2.Result);
var msgs = new List<string>();
doc.Validate(_schemaSet, (s, e) => msgs.Add(e.Message));
if (msgs.Count > 0) {
var responseContent = new StringContent(String.Join(Environment.NewLine, msgs.ToArray()));
tcs.TrySetException(new HttpResponseException(
new HttpResponseMessage(HttpStatusCode.BadRequest) {
Content = responseContent
}));
} else {
tcs.TrySetResult(base.SendAsync(request, cancellationToken).Result);
}
});
});
return tcs.Task;
} else {
return base.SendAsync(request, cancellationToken);
}
}
By trial and error I found a solution (for the WHAT NOW ? placeholder in the question's code):
//...
else
{
PostOrderErrors errors = new PostOrderErrors
{
XmlValidationErrors = msgs
};
HttpResponseMessage response = new HttpResponseMessage(
HttpStatusCode.BadRequest);
response.Content = new ObjectContent(typeof(PostOrderErrors), errors,
GlobalConfiguration.Configuration.Formatters.XmlFormatter);
throw new HttpResponseException(response);
}
...with the response class like this:
public class PostOrderErrors
{
public List<string> XmlValidationErrors { get; set; }
//...
}
That seems to work and the response looks like this then:
HTTP/1.1 400 Bad Request
Content-Type: application/xml; charset=utf-8
<PostOrderErrors xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<XmlValidationErrors>
<string>Some error text...</string>
<string>Another error text...</string>
</XmlValidationErrors>
</PostOrderErrors>

ASP.NET MVC - Proper way to handle ajax actions with no return object

I have a controller action that does some work in the database and then exits when it's finished. This action is being called via jQuery's ajax function with the dataType set to 'json'.
If I set the return type of the action to void, everything will function just fine except Firefox will show an error in the console that says: "no element found".
It makes sense that Firefox would throw this error if it was expecting XML to come back. However, even when I change the dataType property of the ajax call to "text", I still receive the error. In order to get rid of the error with the return type void, I would have to set the Response's ContentType to "text/html". Or I could set the return type to JsonResult and return a new [empty] JsonResult object.
I'm sure there are several ways I can make this error go away, but I wanted to know the proper way to handle actions with no return values being called via ajax.
If it matters, I'm also using the async controller action pattern.
public void DoSomethingAsync(SomeJsonObjectForModelBinding model)
{
// do some database things
}
public void DoSomethingCompleted()
{
// nothing to do...
// what should my return type be?
// do I need to set the content type here?
}
I know this doesn't exactly answer your question, but I would argue that you should always have a return value coming back from an AJAX or web service call. Even if only to tell you that the operation was successful, or otherwise return the error (message) back to you.
I often define a class like this:
public class JsonResultData
{
private bool _success = true;
public bool Success
{
get { return _success; }
set { _success = value; }
}
public object Value { get; set; }
public List<string> Errors { get; set; }
public JsonResultData()
{
this.Errors = new List<string>();
}
}
And then use it to return data or any other call meta data in the JsonResultData wrapper like so:
return new JsonResult {
Data = new JsonResultData { Value = returnValue, Success = true }
};
I can't comment because of my reputation but I still wanted to contribute to clear the confusion in Kon's answer.
In an application I caught all exceptions within an ActionMethod, set an HttpStatusCode and added an error message to the response. I extracted the message in the Ajax error function and showed it to the user.
Everything worked out fine until the application got put on the staging server, who had some kind of settings that did not allow a return message within an erroneous response. Instead some standard Html was transmitted resulting in a JS error processing the response.
In the end I had to rewrite all my exception handling returning my application errors as successful Ajax call (which it actually is) and then differ within the Ajax success function, just the way it should be.
You should not mix system-level and application-level feedback. You may not be able to control the system-level feedback the way your application needs.

An ASP.NET MVC validator to make sure at least one checkbox is checked

I have an ASP.NET MVC 2 project in which I've created a data transfer object to receive data from a web page form. The form has two groups of checkboxes on it. I want to validate the object to make sure that at least one of the checkboxes in each group is checked.
I'm doing the validation on the server side so that a user won't be able to hack around any client-side validation. (I will add client-side validation with jQuery later; that's easy.)
My understanding is that I have to create my own custom ValidationAttribute for my data transfer object class, but I don't understand how to create and use one that can accept an arbitrary list of checkbox properties to make sure that at least one of them is true. I am guessing I will have to call the attributes like this:
[AtLeastOneCheckbox("set1check1", "set1check2", "set1check3",
ErrorMessage = "You must check at least one checkbox in set 1.")]
[AtLeastOneCheckbox("set2check1", "set2check2", "set2check3", "set2check4", "set2check5",
ErrorMessage = "You must check at least one checkbox in set 2.")]
public class MyFormDTO
{
...
}
What would the implementation of AtLeastOneCheckboxAttribute look like?
Or is there a different way that I should do this kind of validation?
if you have several checkbox groups, you just need to deine the attribute several times.
[AttributeUsage( AttributeTargets.Class)]
public class AtLeastOneCheckboxAttribute : ValidationAttribute
{
private string[] _checkboxNames;
public AtLeastOneCheckboxAttribute(params string[] checkboxNames)
{
_checkboxNames = checkboxNames;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var propertyInfos = value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x=>_checkboxNames.Contains(x.Name));
var values = propertyInfos.Select(x => x.GetGetMethod().Invoke(value, null));
if (values.Any(x => Convert.ToBoolean(x)))
return ValidationResult.Success;
else
{
ErrorMessage = "At least one checkbox must be selected";
return new ValidationResult(ErrorMessage);
}
}
}
UPDATE
as you have found out, class-level validation is called only after all properties pass. In order to get the error, just use empty string as the key.
Your DTO, which is I'm guessing your ViewModel can ihert IDataErrorInfo.
Then you can do your validation like this (note I didn't compile this)
//I'm guessing you have a list of checkboxes
IEnumerable<bool> checkBoxes1;
IEnumerable<bool> checkBoxes2;
public class MyFormDTO : IDataErrorInfo
{
public string this[string prop]
{
get
{
if(prop == "checkBoxes1")
{
if(checkBoxes1.Any(x => x == true))
{
return "Error: You need to select atleast one checkbox from set1";
}
}
else if(prop == "checkBoxes2")
{
if(checkBoxes2.Any(x => x == true))
{
return "Error: You need to select atleast one checkbox from set2";
}
}
return null;
}
}
public string Error { get { return null; } }
}

Resources