I am trying to use custom messages with Bean Validation but my custom messages are not being returned by the JAX-RS resource. What am I doing wrong?
ValidationMessages.properties
invoice.value.notnull=Invoice value must be informed.
The file is located at src/main/resources
InvoiceResource.java
#Path("/invoice")
public class InvoiceResource {
#POST
public void post(#Valid InvoiceRequest request) {
/* stuff */
}
}
InvoiceRequest.java
public class InvoiceRequest {
#NotNull(message = "invoice.value.notnull")
private Double value;
}
Found out that the problem was my declaration of the message in the bean param. The message ID must be forked between braces "{ ... }":
#NotNull(message = "{invoice.value.notnull}")
How we get the proper JSON response with the correct message:
{
"exception": null,
"fieldViolations": [],
"propertyViolations": [],
"classViolations": [],
"parameterViolations": [
{
"constraintType": "PARAMETER",
"path": "post.arg0.value",
"message": "Invoice value must be informed",
"value": ""
}
],
"returnValueViolations": []
}
Related
I'm trying to understand if it's possible to get the index of invalids objects inside a list that is validated with #Valid.
I already have validation in place where I send a response like this
{
"status": "BAD_REQUEST",
"message": "Validation Error",
"detailedMessage": "There are errors in the entered data",
"timestamp": 1657896844868,
"validationErrors": [
{
"field": "items[0].name",
"message": "The name is mandatory"
},
{
"field": "items[1].surname",
"message": "The surname is mandatory"
}
]
}
The problem is that in the frontend I need to know exactly which objects in the array "items" have problems so I can highlight the corret input. What I'm doing right now is getting the index from the string "items[0].name" using a regex but I really dislike this kind of behavior and I would like to exctract the index of the invalid item and put it in the response.
Ideally I would like to not have the array index but a specific field of the invalid object.
What I mean is that every item has an "id" field and I would like to extract that one and send something like this in response
{
"status": "BAD_REQUEST",
"message": "Validation Error",
"detailedMessage": "There are errors in the entered data",
"timestamp": 1657896844868,
"validationErrors": [
{
"field": "items[12345].name",
"message": "The name is mandatory",
"itemId": 12345
},
{
"field": "items[12346].surname",
"message": "The surname is mandatory",
"itemId": 12346
}
]
}
In this way I would be able to know exactly which object is invalid in the frontend without having to rely on array indexes or regex to extract the index from a string. Of course having the array index as "itemIndex" field would also be better than what I have right now.
Following you can find my request class, that is used in the controller as #Valid #RequestBody, and my ControllerAdvice where I build my response when a MethodArgumentNotValidException happens.
Request
public class Request {
#Valid
#NotEmpty(message = "You must insert at least one item")
private List<#Valid #NotNull Item> items;
}
ControllerAdvice
#RestControllerAdvice
public class BaseExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.BAD_REQUEST, "Validation Error", "There are errors in the entered data");
List<ValidationError> errors = new ArrayList<>();
ex.getBindingResult().getAllErrors().forEach(error -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
ValidationError validationError = new ValidationError(fieldName, errorMessage);
errors.add(validationError);
});
errorResponse.setValidationErrors(errors);
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
}
You can access information about objects that violated the constraint by unwrapping your ObjectError or FieldError to ConstraintViolation (https://docs.oracle.com/javaee/7/api/javax/validation/ConstraintViolation.html).
Here is how you can access array index
Path nodes = error.unwrap(ConstraintViolation.class).getPropertyPath();
for (Path.Node node : nodes) {
if (node.isInIterable()) {
node.getIndex() // for items[12346].surname it will return 123456
}
}
Accessing an element that has thrown an error is also possible using unwrap to ConstraintViolation.
Item item = (Item) error.unwrap(ConstraintViolation.class).getLeafBean(); // don't use cast with no checks in your code
This will return an item which triggered constraint violation.
I'm implementing query layer on database by using GraphQl and spring boot project to perform CRUD operation on sql database. In GraphQL schema i mentioned some fields to be mandatory and when those fields are not mentioned in query it is returning ValidationError error message in default format with 200 status code.
Error :
{
"data": null,
"errors": [
{
value=StringValue{value='1235'}}]}}]}' is missing required fields '[book_type]' # 'create_book'",
"locations": [
{
"line": 3,
"column": 23,
"sourceName": null
}
],
"description": "argument 'insert' with value value=StringValue{value='1235'}}]}}]}' is missing required fields '[book_type]'",
"validationErrorType": "WrongType",
"queryPath": [
"create_book"
],
"errorType": "ValidationError",
"path": null,
"extensions": null
}
],
"dataPresent": false,
"extensions": null
}
And here is my code with layer architecture pattern
Controller :
#Autowired
private GraphQLServer graphQlServer;
#PostMapping("test")
public ResponseEntity<Object> graphQl(#RequestBody String body){
ExecutionResult response = graphQlServer.execute(body);
return ResponseEntity.ok(response);
}
Service :
#Service
public class GraphQLServer {
#Autowired
private GraphQL graphQl;
public ExecutionResult execute(String query) {
return graphQl.execute(query);
}
}
Config :
#Bean
public GraphQL loadSchema() throws IOException {
File schemaFile = schemaResource.getFile();
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(schemaFile);
RuntimeWiring wiring = buildRuntimeWiring();
GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(typeRegistry, wiring);
return GraphQL.newGraphQL(schema).build();
}
private RuntimeWiring buildRuntimeWiring() {
return RuntimeWiring.newRuntimeWiring()
.type("Mutation", mutationWiring -> mutationWiring.dataFetcher("create_book", bookDataFetcher))
.build();
}
BookDataFetcher :
#Override
public Map<String, Object> get(DataFetchingEnvironment environment) {
//return data from db by getting Map properties from environment
}
The above code is working as expected but my question here is How to customize the error message? In the error message i would like to mention the status 400 since it is bad request from client
First of all , you should call toSpecification() on ExecutionResult to make sure the response obeys the GraphQL Specification.
By default , there is only one ExecutionResult 's implementation provided by graphql-java which is ExecutionResultImpl , so you can cast ExecutionResult to it in order to use its transform() to update its state.
ExecutionResultImpl internally contains all errors detected by the graphql-java. All of them are in the subclass of GraphQLError which mean you have to cast it to the specific sub-class during customization.
In your case , the subclass is ValidationError and the codes look something like :
#PostMapping("test")
public ResponseEntity<Object> graphQl(#RequestBody String body){
ExecutionResult response = graphQlServer.execute(body);
ExecutionResultImpl responseImpl = (ExecutionResultImpl) response;
List<GraphQLError> customizedErrors = Lists.newArrayList();
for (GraphQLError gqlError : responseImpl.getErrors()) {
//Do your error custmosation here....
GraphQLError customizedError = gqlError;
if (gqlError instanceof ValidationError) {
ValidationError error = (ValidationError) gqlError;
customizedError = new ValidationError(error.getValidationErrorType(), error.getLocations(),
"Customizing some error message blablabla....");
}
customizedErrors.add(customizedError);
}
Map<String, Object> specResponse = responseImpl.transform(b->b.errors(customizedErrors)).toSpecification();
return ResponseEntity.ok(specResponse);
}
I am posting a POJO where I get an error saying the field is not included.
Asset POJO
public class Asset {
private MultipartFile[] files;
private String name;
private String meta;
//Constructor/Getters n Setters
}
Resource Method
#PostMapping("asset")
public ResponseEntity uploadAsset(#RequestParam("asset") Asset asset) {
System.out.println(asset);
return new ResponseEntity(HttpStatus.ACCEPTED);
}
PostMan JSON Body
{
"asset" : {
"files": [
"#/home/Downloads/1.jpeg",
"#/home/Downloads/2.jpeg"
],
"name": "assetName",
"meta": "assetMeta"
}
}
PostMan JSON Response
{
"timestamp": "2019-10-29T20:46:19.536+0000",
"status": 400,
"error": "Bad Request",
"message": "Required Asset parameter 'asset' is not present",
"path": "/asset"
}
I don't understand why I get the Required Asset parameter 'asset' is not present message when I have it in the JSON body. Any ideas on this?
Use #RequestBody rather than #RequestParam
public ResponseEntity uploadAsset(#RequestBody Asset asset) {
Based on your payload, Spring is expecting an object that looks like this:
public class SomeClass {
private Asset asset;
}
Change your payload to look like this:
{
"files": [
"#/home/Downloads/1.jpeg",
"#/home/Downloads/2.jpeg"
],
"name": "assetName",
"meta": "assetMeta"
}
RequestParam
Annotation which indicates that a method parameter should be bound to a web request parameter.
RequestBody
Annotation indicating a method parameter should be bound to the body of the web request. The body of the request is passed through an HttpMessageConverter to resolve the method argument depending on the content type of the request. Optionally, automatic validation can be applied by annotating the argument with #Valid.
HttpMessageConverter
Strategy interface that specifies a converter that can convert from and to HTTP requests and responses.
You need to check converter dependency. because you using application/json.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
Q : Missing request param when it is included in body
A : Use #RequestBody annotation.
I tried #Jordans answer and the endpoint was called with all values set to null :(
Doing more research I came across this statement https://stackoverflow.com/a/51982230/2199102 and tried it out.
Combining #Jordans answer and then the annotation change, I was able to get the answer I wanted
I am trying to create a REST API in the format which will accept request data with both JSON and MultipartFile type.
THis is my request which will be in following format in POSTMAN:
My POJO classes are as follows:
Class:Organisation
public class Organisation
{
priavet int org_id;
private MultipartFile organisationRegistrationDocument;
private Teachers[]
// getters and setters
}
Class: Teachers
class Teachers{
private String teacherId;
private MultipartFile teacherPhoto;
// getters and setters
}
My controller Class is as follows:
#RequestMapping(value="/test",method=RequestMethod.POST,headers = {"content-type=multipart/mixed","content-type=multipart/form-data"})
private ResponseEntity<Object> testUpload(#RequestBody Organisation org) {
return null;
}
Error Thrown from POSTMAN:
{
"timestamp": "2018-10-03T07:38:30.439+0000",
"status": 400,
"error": "Bad Request",
"message": "Required request part 'org' is not present",
"path": "/test"
}
So anyone can kindly guide me what can I am doing wrong due to which I am not able to achieve the desired result to process request of the above form?
For some reason unknown to me, when making a request to my Spring controller it is returning an invalid JSON value. I'm using Jackson to map my JSON object. This is the data being returned when I make the request:
{} &&
{
"registros": [
{
"id": 251,
"matricula": "32849923",
"dadoPessoal": {
"nome": "Testando",
"email": "tiare#terra.com.br",
"telefone": "1235324812",
"celular": "123832911",
"foto": null,
"salario": 3829
},
"status": true,
"nascimento": {
"dataNascimento": 1417485600000,
"nascionalidade": "Brasil",
"localNascimento": "SP"
},
"documentoPessoal": {
"rg": "8329892332",
"orgaoEmissor": "SSP/SP",
"dataEmissao": 1417485600000,
"cpf": "016.015.XXX-XX",
"tituloEleitor": "adw91021",
"zonaDeVoto": "91aa",
"sessaoVoto": "2a",
"enderecoVoto": "adw"
},
"dataAdmissao": 1361674800000,
"dataDesligamento": null,
"version": 0
}
],
"total": 1
}
The problem here is that somehow invalid characters "{} &&" are being added to the beginning of the JSON. What I'm not understanding is how? Its adding these values that are not defined anywhere in my Spring method.
My Request Mapping:
#Override
#RequestMapping(value = { "/", "" }, method = RequestMethod.GET)
public ModelAndView index() {
ModelAndView view = new ModelAndView(VIEW_INDEX);
view.addObject("registros", service.findAll());
view.addObject("total", service.findAll().size());
return view;
}
The Jackson configuration:
/**
* #return MappingJacksonJsonView
*/
#Bean
public MappingJacksonJsonView mappingJacksonJsonView() {
MappingJacksonJsonView mappingJacksonJsonView = new MappingJacksonJsonView();
mappingJacksonJsonView.setContentType("application/json");
mappingJacksonJsonView.setObjectMapper(this.objectMapper());
mappingJacksonJsonView.setEncoding(JsonEncoding.UTF8);
mappingJacksonJsonView.setPrefixJson(true);
return mappingJacksonJsonView;
}
/**
* #return ContentNegotiatingViewResolver
*/
#Bean
public ContentNegotiatingViewResolver contentNegotiatingViewResolver() {
List<ViewResolver> viewResolvers = new ArrayList<ViewResolver>();
// Tiles
viewResolvers.add(this.tileViewResolver());
// Views
List<View> defaultViews = new ArrayList<View>();
defaultViews.add(this.mappingJacksonJsonView());
ContentNegotiatingViewResolver contentNegotiatingViewResolver = new ContentNegotiatingViewResolver();
contentNegotiatingViewResolver.setViewResolvers(viewResolvers);
contentNegotiatingViewResolver.setDefaultViews(defaultViews);
contentNegotiatingViewResolver.setOrder(0);
return contentNegotiatingViewResolver;
}
Thanks you all. I found the error!!
When i started to read the javadoc i found this:
"Indicates whether the JSON output by this view should be prefixed with "{} && ". Default is false."
springsource docs