How to throw Spring #Valid "exception"? - java

I'm using Spring to build my REST api. One of my methods is decorated with #Valid. This produces a JSON that looks like this:
{
"timestamp": "2019-11-04T19:07:08.387+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"NotNull.customer.firstName",
"NotNull.firstName",
"NotNull.java.lang.String",
"NotNull"
],
"arguments": [
{
"codes": [
"customer.firstName",
"firstName"
],
"arguments": null,
"defaultMessage": "firstName",
"code": "firstName"
}
],
"defaultMessage": "must not be null",
"objectName": "customer",
"field": "firstName",
"rejectedValue": null,
"bindingFailure": false,
"code": "NotNull"
}
],
"message": "Validation failed for object='customer'. Error count: 1",
"path": "/customers"
}
Doing a Google search, to produce a HTTP 400 error, I'm supposed to throw ResponseStatusException(HttpStatus.BAD_REQUEST);
This produces a JSON like this:
{
"timestamp": "2019-11-04T19:08:02.408+0000",
"status": 400,
"error": "Bad Request",
"message": "400 BAD_REQUEST",
"path": "/customers"
}
The message is easy to change obviously, but I'm more asking about how to get the Errors array. That doesn't seem to be in ResponseStatusException?
I can recreate it pretty easily, but I was hoping there was one built already?
EDIT: This isn't input validation per say... this is throwing an error FOR the input, but I need to hit the database first to do the actual validation, so that part is in my service layer and throws an internal exception if it isn't valid.

If you are using the Validator, then you can customize the errors using a configuration file like validator.properties

Related

Unable to launch a Dataflow template using API client library (Java) as I am getting Invalid argument exception

The interesting fact is that I am able to launch the Dataflow template from cloud shell, as well as from Google OAuth2 playground console. When I try to launch it using the API client library with the same set of details, I am getting 400 Bad request error.
URL Used:
GenericUrl URL = new GenericUrl("https://dataflow.googleapis.com/v1b3/projects/servicetesting-g-1575763471750/templates:launch?gcsPath=gs://dataflow-templates/latest/GCS_Text_to_Cloud_PubSub");
POST Body content:
{
"jobName":"st-txt-to-pubsub",
"parameters":
{
"inputFilePattern":"gs://soa_global_storage/input/my_input.txt",
"outputTopic":"projects/servicetesting-g-1575763471750/topics/Input_Topic_PUB"
},
"environment": { "zone": "us-central1" }
}
Exception:
Exception in thread "main" com.google.api.client.http.HttpResponseException: 400 Bad Request
{
"error": {
"code": 400,
"message": "The template parameters are invalid.",
"errors": [
{
"message": "The template parameters are invalid.",
"domain": "global",
"reason": "badRequest"
}
],
"status": "INVALID_ARGUMENT"
}
}
Take a look on your template metadata, be sure the field name is the same you call in the POST body. Check also if the parameters pass on the regex test.
As others have said, you also have to correct your zone to us-central1-a, or you can leave it blank.
Ex. of metadata that would fit your content:
{
"description": "enter your description",
"name": "template_name",
"parameters": [
{
"name": "inputFilePattern",
"helpText": "Bucket URI",
"regexes":["^gs://.*"],
"label": "input file pattern"
},
{
"name": "outputTopic",
"helpText": "lorem ipsum",
"label": "Topic on Pub/sub",
}
]
}
Worth to mention that you should try the template with the parameters in the console. If doesn't work there, it won't work anywhere.

Handle exceptions as spring do [duplicate]

This question already has answers here:
Spring3 #ExceptionHandler for ServletRequestBindingException
(4 answers)
Closed 4 years ago.
I would like to handle exceptions without override default spring handler for validation.
If i do not implement an exception handler by #ControllerAdvice, my validation erros will response like this:
{
"timestamp": "2018-09-25T13:15:30.037+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"Null.notificationEntity.id",
"Null.id",
"Null.java.math.BigInteger",
"Null"
],
"arguments": [
{
"codes": [
"notificationEntity.id",
"id"
],
"arguments": null,
"defaultMessage": "id",
"code": "id"
}
],
"defaultMessage": "must be null",
"objectName": "notificationEntity",
"field": "id",
"rejectedValue": 15,
"bindingFailure": false,
"code": "Null"
}
],
"message": "Validation failed for object='notificationEntity'. Error count: 1",
"path": "/v1/notifications"
}
The repsonse above is nice and clear for me, but if i create an exception handler, to handle my own exceptions, i have to manually handle validation errors throwed by #Valid.
I have found here a sample about it: http://www.springboottutorial.com/spring-boot-validation-for-rest-services
But on this example i still have to implement manually, and my result is:
{
"timestamp": "2018-09-25T13:07:22.779+0000",
"status": 400,
"code": "BAD_REQUEST",
"error": "Bad Request",
"message": "org.springframework.validation.BeanPropertyBindingResult: 1 errors\nField error in object 'notificationEntity' on field 'id': rejected value [15]; codes [Null.notificationEntity.id,Null.id,Null.java.math.BigInteger,Null]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [notificationEntity.id,id]; arguments []; default message [id]]; default message [must be null]",
"path": "/v1/notifications"
}
My question is, how could i implement my own exception handler without override spring validation handler or how could i call spring validation handler to keep the same response for validation exceptions?
If you go with catching the exception in your ControllerAdvice, you will have access to the validation exception (which I assume is the one with the structure you want to replicate).
#ExceptionHandler(ValidationException.class)
#ResponseBody
public ResponseEntity<YourCustomResponse> handleValidationexception(ValidationException e) {
// probably do some logging
YourCustomResponse response = buildCustomResponse(e); // build the response payload however you see fit
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
In this way, you can set the body, the HTTP headers and the HTTP status that is returned to the client in case of a ValidationException.

Proper way to parse the body of an error with Spring's RestTemplate

I'm using the #Valid annotation on the #RequestBody argument which uses Hibernate Validation, to ensure it's validated:
public ResponseEntity<?> register(#RequestBody #Valid RegistrationModel registration) { ...
Following the recommendations of the Spring Rest book, I'm catching that exception and turning it into a nice JSON error message that looks like this:
{
"title": "Validation Failed",
"status": 400,
"detail": "Input validation failed",
"timeStamp": 1505345551757,
"developerMessage": "org.springframework.web.bind.MethodArgumentNotValidException",
"errors": {
"hashedPassword": [
{
"code": "NotNull",
"message": "must not be null"
}
],
"organization": [
{
"code": "NotBlank",
"message": "must not be blank"
}
],
"name": [
{
"code": "NotBlank",
"message": "must not be blank"
}
]
}
}
On the client side, I'm doing this to make the call:
Object result = restTemplate.postForObject("http://localhost:8080/v1/registration", registration, Object.class);
Using Object until I define exactly on the representation of the response. When there's a validation error though, I get a 400 error which throws a HttpClientErrorException.
HttpClientErrorException has a getResponseBodyAsString() that responds with the JSON I pasted above. I could manually parse it, but I was wondering what's the most correct way to parse it. RestTemplate clearly has a mechanism for automatically parsing responses when there's no errors. Does it have something for errors?

Documentation of Spring Validation Errors

Spring, als always, provides very useful defaults to handle Validation errors. But sometimes it looks difficult to customize those. In my case I have a custom validation that uses a javascript function to validate a field in a domain object. The default validation error produces 4 message codes that use the object name, the field name, the field type and the validation type. So far so good. But I would like to add an additional code that contains the name of the js-function as a component. How could I do that?
Or more general my question is: where do I find a documentation of the way Spring builds the default error messages, and how they can be manipulated.
In my case I get an output like:
{
"timestamp": 1457092927829,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.web.bind.MethodArgumentNotValidException",
"errors": [
{
"codes": [
"JSValidated.order.validFrom",
"JSValidated.validFrom",
"JSValidated.java.time.ZonedDateTime",
"JSValidated"
],
"arguments": [
{
"codes": [
"order.validFrom",
"validFrom"
],
"arguments": null,
"defaultMessage": "validFrom",
"code": "validFrom"
},
"checkOrder",
"static/OrderValidator.js"
],
"defaultMessage": "validation checkValidFrom failed",
"objectName": "order",
"field": "validFrom",
"rejectedValue": 1196586930,
"bindingFailure": false,
"code": "JSValidated"
},
{
"codes": [
"NotEmpty.order.id",
"NotEmpty.id",
"NotEmpty.java.lang.String",
"NotEmpty"
],
"arguments": [
{
"codes": [
"order.id",
"id"
],
"arguments": null,
"defaultMessage": "id",
"code": "id"
}
],
"defaultMessage": "may not be empty",
"objectName": "order",
"field": "id",
"rejectedValue": null,
"bindingFailure": false,
"code": "NotEmpty"
}
],
"message": "Validation failed for object='order'. Error count: 2",
"path": "/order"
}
How can I add or change the codes? How can I add or change the list of arguments? Where is all the stuff documented?
You can use a global exception handler using #ExceptionHandler
You can define which exceptions should be handled. You have access to the thrown exception, which contains also the validation errors.
Create your own error class, that contains the properties you want to return.
Map the validation error into your error object and return it along with the HTTP status of your choice.
BindingException is one Exception I got from validation and the handler looks like this :
#ExceptionHandler(BindException.class)
#ResponseBody
public ResponseEntity<Object> handle(HttpServletRequest req, BindException ex) {
ExceptionResponse response = new ExceptionResponse(ex);
return new ResponseEntity<>(response, HttpStatus.EXPECTATION_FAILED);
}
And the error class ExceptionResponse :
#JsonInclude(Include.NON_NULL)
#JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
public static class ExceptionResponse{
String exception;
String message;
String trace;
public ExceptionResponse(Exception exception) {
super();
this.exception = exception.getClass().getName();
this.message = exception.getMessage();
this.trace = Arrays.toString(exception.getStackTrace());
}
This is a json serialization of the result of method getErrorAttributes(RequestAttributes requestAttributes,
boolean includeStackTrace) of class
org.springframework.boot.autoconfigure.web.DefaultErrorAttributes.
This class can be extended to add additional properties.
Codes and messages are added by validators, do if you'd like to change them you need to customize validators used.

how to send HTTP Post request using jersey with a complex parameter

I need to send a HTTP Post to a REST API with the following complex type as parameters. I looked at the documentation of jersey and it helps only to send a key value pair. How can i send a HTTP Post request with the below parameters using jersey.
{
"key": "example key",
"message": {
"html": "<p>Example HTML content</p>",
"text": "Example text content",
"subject": "example subject",
"from_email": "message.from_email#example.com",
"from_name": "Example Name",
"to": [
{
"email": "recipient.email#example.com",
"name": "Recipient Name"
}
],
"headers": {
"Reply-To": "message.reply#example.com"
},
"important": false,
"track_opens": null,
"track_clicks": null,
"auto_text": null,
"auto_html": null,
"inline_css": null,
"url_strip_qs": null,
"preserve_recipients": null,
"view_content_link": null,
"bcc_address": "message.bcc_address#example.com",
"tracking_domain": null,
"signing_domain": null,
"return_path_domain": null,
"merge": true,
"global_merge_vars": [
{
"name": "merge1",
"content": "merge1 content"
}
],
"merge_vars": [
{
"rcpt": "recipient.email#example.com",
"vars": [
{
"name": "merge2",
"content": "merge2 content"
}
]
}
],
"tags": [
"password-resets"
],
"subaccount": "customer-123",
"google_analytics_domains": [
"example.com"
],
"google_analytics_campaign": "message.from_email#example.com",
"metadata": {
"website": "www.example.com"
},
"recipient_metadata": [
{
"rcpt": "recipient.email#example.com",
"values": {
"user_id": 123456
}
}
],
"attachments": [
{
"type": "text/plain",
"name": "myfile.txt",
"content": "ZXhhbXBsZSBmaWxl"
}
],
"images": [
{
"type": "image/png",
"name": "IMAGECID",
"content": "ZXhhbXBsZSBmaWxl"
}
]
},
"async": false,
"ip_pool": "Main Pool",
"send_at": "example send_at"
}
I looked at the other questions of sending HTTP Post using Jersey and all I could find was a way to only send a key\value pairs as parameters and not complex string types like above.
You should look at JAXB, it allows you to "automatically" build "complex parameters" out of "objects". Basically the procedure would be to define a class that represents the data structure you present as request message the REST API resource accepts, then populate it with the data your want to POST and send it. In this question you can find more details on how to exactly do that: Can jersey clients POST a JAXB object to the server using JSON?

Categories

Resources