javax.validation.constraints with custom Code and Message - java

I am using the javax.validation.constraints.NotEmpty library to validate POJO class for rest request. Within class i have used below annotaion.
#ApiModelProperty(value = "Name", required = true)
#NotEmpty(message = "Name is required")
private String name;
Above code is working fine and returning the response as below when name is empty
"code": "NotEmpty",
"message": "Name is required"
Is there any way through which i can display/provide the my own Custom code value instead of "code": "NotEmpty". Something like "code": "INVALID NAME".
Any approach suggestion on this must be appreciated.

The best way is to implement your own exception handler and build the entire response as you need to it, as simple text, JSON, XML, etc..
#ControllerAdvice
class ExceptionAdvisor {
#ExceptionHandler(ValidationException.class)
ResponseEntity<MyErrorType> handleTypeMismatchException(ValidationException ex) {
/*process all the constraint violations*/
MyErrorType result = . . .;
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(result);
}
}
There is also the Payload option:
public static class NameInvalid implements Payload {}
.
.
.
#NotEmpty(message = "Name is required", payload = NameInvalid.class)
private String name;
Which should get you this:
"code": "NameInvalid",
"message": "Name is required"

Related

How to get a attribute variable with Json response using Wiremock

I need a dynamic response with different values using traffic parrot and wiremock.
I have an integration test with patterns in json files to get a response when I call the API's.
I need to call a product service to retrieve a specific product, my request json:
request.json
{
"request" {
"urlPathPattern": "/urlResponse/product/1000",
"method": "GET"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"bodyFileName": "response.json",
"transformers": [
"response-template"
]
}
}
And I need something like this:
response.json
{
"id": {{request.path.[2]}},
"type": VARIABLE or DYNAMIC CONTENT,
"other attr"....
}
I Want to pass a variable content depending of the request, for example, I have an Object
public CustomObject {
private int id = 1000;
private String type = "product";
}
When I call the API of product with ID 1000, I want a response.json with type set to "product". I see documentation of Traffic parrot example, but I do not know how to apply.
Edit 1: It is possible to define in request.json a map to define the variable type for the response? Something like this:
If I have an ID with 1000
objMap = 1000; type = "product",
objMap = 2000; type = "Balloon",
etc...

Ninja framework endpoint throws 500 error when trying to map JSON to custom object

So I've got a Ninja endpoint here:
public Result processRecurring(Context context, RecurOrderJSON recurOrderJSON) {
String id = recurOrderJSON.id;
String event_type = recurOrderJSON.event_type;
String request_id = recurOrderJSON.request_id;
//Map data = recurOrderJSON.data;
//recurringRouter(event_type, data);
log.info("ID value");
log.info(id);
return JsonResponse.build()
.message("OK")
.toResult();
}
The class I am trying to map to:
public class RecurOrderJSON {
public String id;
public String event_type;
public String request_id;
// Maybe switch data type?
//public Map data;
}
And the route:
router.POST().route("/recurring").with(RecurringController::processRecurring);
I am just trying to send some simple JSON to a webhook and for some reason the object mapping doesn't seem to be working. I think maybe I am misunderstanding the documentation?
http://www.ninjaframework.org/documentation/working_with_json_jsonp.html
The example they give you is this:
If you send that JSON to your application via the HTTP body you only need to add the POJO class to the controller method and Ninja will parse the incoming JSON for you:
package controllers;
public class ApplicationController {
public Result parsePerson(Person person) {
String nameOfPerson = person.name; // will be John Johnson
...
}
}
As far as I can tell, I am doing this correctly? Am I understanding the documentation wrong? Here's an example JSON object - currently I am only trying to grab the top level strings, but I'll eventually want to grab data as well:
{
"id": "hook-XXXXX",
"event_type": "tx-pending",
"data": {
"button_id": "static",
"publisher_organization": "org-XXXXXXX",
"campaign_id": "camp-097714a40aaf8965",
"currency": "USD",
"order_currency": "USD",
"id": "tx-XXXXXXX",
"category": "new-user-order",
"modified_date": "2018-10-15T05:41:12.577Z",
"order_total": 9680,
"button_order_id": "btnorder-77c9e56fd990f127",
"publisher_customer_id": "XymEz8GO2M",
"rate_card_id": "ratecard-41480b2a6b1196a7",
"advertising_id": null,
"event_date": "2018-10-15T05:41:06Z",
"status": "pending",
"pub_ref": null,
"account_id": "acc-4b17f5a014d0de1a",
"btn_ref": "srctok-0adf9e958510b3f1",
"order_id": null,
"posting_rule_id": null,
"order_line_items": [
{
"identifier": "Antique Trading Card",
"description": "Includes Lifetime Warranty",
"amount": 9680,
"publisher_commission": 968,
"attributes": {},
"total": 9680,
"quantity": 1
}
],
"order_click_channel": "webview",
"order_purchase_date": null,
"validated_date": null,
"amount": 968,
"customer_order_id": null,
"created_date": "2018-10-15T05:41:12.577Z",
"commerce_organization": "org-XXXXXX"
},
"request_id": "attempt-XXXXXXX"
}
Currently I am just trying to get the string values, yet I am constantly getting a 500 error and no other indication in my logs of any error.
As far as I can tell, Ninja should just automatically map the JSON to my object, correct?
I successfully reproduced your issue, and then fixed it.
First, for easy way to try/test, I recommend (temporary) modifications:
package controllers;
import models.RecurOrderJSON;
import ninja.Context;
import ninja.Result;
public class RecurringController {
public Result processRecurring(Context context, RecurOrderJSON recurOrderJSON) {
log.info("recurOrderJSON => " + recurOrderJSON);
return ninja.Results.ok();
}
}
And then, update your model this way:
package models;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties(ignoreUnknown = true)
public class RecurOrderJSON {
public String id;
public String event_type;
public String request_id;
public Map data;
#Override
public String toString() {
return "RecurOrderJSON [id=" + id + ", event_type=" + event_type + ", request_id=" + request_id + ", data="
+ data.toString() + "]";
}
}
You can notice:
The data type must stay raw (generic can't be used here)
the important #JsonIgnoreProperties(ignoreUnknown = true) annotation to avoid deserialize issue, if ever your source data does not perfectly match your model (be sure to use the recent version of annotation, in fasterxml sub-package, instead of the old one, in codehaus sub-package)
the toString() implementation only allowing quick check of OK/KO deserialization
Then you can easily test the system with wget, or curl:
curl -H 'Content-Type: application/json' -d "#/tmp/jsonINput.json" -X POST http://localhost:8080/recurring
Notice it is very important to specify the Content-type for good interpretation.
With the /tmp/jsonINput.json file containing exactly the json contents you specified in your question.
This way, everything is working like a charm, obtaining this output:
recurOrderJSON => RecurOrderJSON [id=hook-XXXXX, event_type=tx-pending, request_id=attempt-XXXXXXX, data={button_id=static, publisher_organization=org-XXXXXXX, campaign_id=camp-097714a40aaf8965, currency=USD, order_currency=USD, id=tx-XXXXXXX, category=new-user-order, modified_date=2018-10-15T05:41:12.577Z, order_total=9680, button_order_id=btnorder-77c9e56fd990f127, publisher_customer_id=XymEz8GO2M, rate_card_id=ratecard-41480b2a6b1196a7, advertising_id=null, event_date=2018-10-15T05:41:06Z, status=pending, pub_ref=null, account_id=acc-4b17f5a014d0de1a, btn_ref=srctok-0adf9e958510b3f1, order_id=null, posting_rule_id=null, order_line_items=[{identifier=Antique Trading Card, description=Includes Lifetime Warranty, amount=9680, publisher_commission=968, attributes={}, total=9680, quantity=1}], order_click_channel=webview, order_purchase_date=null, validated_date=null, amount=968, customer_order_id=null, created_date=2018-10-15T05:41:12.577Z, commerce_organization=org-XXXXXX}]
Given the specific input code with data field commented out
//public Map data;
and the posted input JSON that includes this field, the request should fail with 400 Bad Request.
The reason being that Ninja uses Jackson for JSON parsing and it will throw on unknown fields by default.
The quick workaround is to add #JsonIgnoreProperties annotation to RecurOrderJSON class.
e.g.
#JsonIgnoreProperties(ignoreUnknown = true)
public class RecurOrderJSON {
...
}
See: Ignoring new fields on JSON objects using Jackson
Now if the error was not 400 there isn't much information to go by as there doesn't seem to be anything else obviously wrong with the code.
Either post an SSCCE demonstrating the problem or attempt to debug by surfacing the error page with the following method:
Launch the application in debug mode with mvn package ninja:run
Access the end-point with a tool that allows to inspect the response in detail such as curl e.g.
Store request JSON in input.json
Run curl -v -o result.html -H 'Content-Type: application/json' --data '#input.json' http://localhost:8080/recurring
Open result.html to examine the response
Might it be that you are performing a bad request (hence the JSON is not found) but for some Ninja bug it returns error 500?
For example you can take a look here where is stated that parsing an empty JSON in a JSON request does leads to a misguiding error (500) while it is supposed to return 400 "Bad Request"
Context not needed in processRecurring and use Results.json() and return original
public Result processRecurring(RecurOrderJSON recurOrderJSON) {
String id = recurOrderJSON.id;
String event_type = recurOrderJSON.event_type;
String request_id = recurOrderJSON.request_id;
//Map data = recurOrderJSON.data;
//recurringRouter(event_type, data);
log.info("ID value");
log.info(id);
return Results.json().render(recurOrderJSON);
}
Make sure you get the namespace in your RecurOrderJSON
package models;
public class RecurOrderJSON {
public String id;
public String event_type;
public String request_id;
// Maybe switch data type?
//public Map data;
}
Good luck!

Deserialization exception while POSTing nested json to Spring controller

I'm using Spring, following is my controller code:
#RequestMapping(value = "/campaigns/addTask", method = RequestMethod.POST)
public Campaign addTaskToCampaign(#RequestParam(value = "campaignName")String campaignName,#Valid #RequestBody Task task) {
Campaign campaign = campaignInterface.findByName(campaignName);
if (campaign!=null){
List<String> task_ids;
if (campaign.getTask_ids()==null){
task_ids = new ArrayList<>();
}else{
task_ids= campaign.getTask_ids();
}
Task newTask = taskInterface.save(task);
task_ids.add(newTask.getId());
campaign.setTask_ids(task_ids);
return campaignInterface.save(campaign);
}
return null;
}
Where my task model is:
#Document(collection = "tasks")
public class Task {
#Id
private String id;
private String name;
private int points;
private List<Question>questions;
private List<String>answers;
...
}
And the nested question model is:
public class Question {
private Boolean isText = false;
private String questionText;
}
But, the same model when POSTing as nested json throws an HTTP 400 exception saying json unreadable exception, and it tried to parse the String questionText field as a boolean value.
Here is what im POSTing:
{
"name" : "Test Task 3",
"questions": [{ "questionText":"What is the name you college festival?","isText":true}]
}
And the exception that comes is this:
{
"timestamp": 1497508476467,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.http.converter.HttpMessageNotReadableException",
"message": "JSON parse error: Can not deserialize value of type boolean from String \"What is the name you college festival?\": only \"true\" or \"false\" recognized; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type boolean from String \"What is the name you college festival?\": only \"true\" or \"false\" recognized\n at [Source: java.io.PushbackInputStream#6a371c03; line: 3, column: 32] (through reference chain: com.frapp.CBM.prod.model.Task[\"questions\"]->java.util.ArrayList[0]->com.frapp.CBM.prod.model.Question[\"questionText\"])",
"path": "/campaigns/addTask"
}
Any help is appreciated. ive been trying for hours. Thank you, in advance.
The first comment I would make to any of my developers is: please can you rename your boolean. This is because the getter will look like: isIsText().
As a general rule it is good practice to avoid starting a field name with "get", "set", or "is".
This is because these are the prefixes of java-beans properties.
#RequestMapping(value = "/campaigns/addTask", method = RequestMethod.POST)
public Campaign addTaskToCampaign(#RequestParam Map<String,Object> campaignName,#Valid #RequestBody Task task){
/*
if there is exception then just remove Task put it into json to catch into map.
access just as map by campaignName.get("key_name");
*/
Campaign campaign = campaignInterface.findByName(campaignName);
if (campaign!=null){
List<String> task_ids;
if (campaign.getTask_ids()==null){
task_ids = new ArrayList<>();
}else{
task_ids= campaign.getTask_ids();
}
Task newTask = taskInterface.save(task);
task_ids.add(newTask.getId());
campaign.setTask_ids(task_ids);
return campaignInterface.save(campaign);
}
return null;
}

return any exception in json in rest api

Is there any simple methods to return exception in JSON using Rest api?
I've already googled this question, but all solutions i see, was about throwing exceptions during some calculations. But what if income parameters are wrong? I mean what if there is sone string instead of int input parameter?
I created some DTO class for input data:
#XmlRootElement
public class RequestDTO implements Serializable{
private static final long serialVersionUID = 1L;
#XmlElement(name = "request_id")
private String requestId;
#XmlElement(name = "site")
private List<String> sitesIds;
#XmlElement(name = "date_begin")
#JsonSerialize(using = DateSerializer.class)
#JsonDeserialize(using = DateDeserializer.class)
private Date dateBegin;
#XmlElement(name = "date_end")
#JsonSerialize(using = JsonDateSerializer.class)
#JsonDeserialize(using = JsonDateDeserializer.class)
private Date dateEnd;
#XmlElement(name = "volume")
private double volume;
// there is getters and setters
}
If i sent something like 'qwerty' instead of 'volume' field in my json request i'l see error message like Runtime. Is it possible to handle it in someway? I mean to return error in json with such structure?
public class ExceptionDTO {
private String shortExceptionMessage;
private String stackTrace;
public ExceptionDTO(String shotExceptionMessage, String stackTrace){
this.shortExceptionMessage = shotExceptionMessage;
this.stackTrace = stackTrace;
}
public String getShortExceptionMessage() {
return shortExceptionMessage;
}
public String getStackTrace() {
return stackTrace;
}
}
UPD1:
#Provider
#Singleton
public class ExceptionMapperProvider implements ExceptionMapper<Exception>{
#Override
public Response toResponse(final Exception e) {
StringBuilder trace = new StringBuilder();
IntStream.range(0, e.getStackTrace().length)
.forEach(i -> trace.append(e.getStackTrace()[i]).append('\n'));
ExceptionDTO exceptionMessage = new ExceptionDTO(
e.toString(),
trace.toString()
);
return Response.status(500).entity(exceptionMessage).build();
}
}
As it's not really clear if you are interested on checking if field or value of the payload is correct, here are a few ways to work with both.
If you want to check if the value for a field is correct (ie volume field value should be greater than zero etc), check out bean validation. This makes use of annotations on the fields you want to verify.
// for example
#Min(value = 0, message = "invalid message")
private double range;
To use your ExceptionDTO as error response whenever one of those validation fails, you can do so by creating an ExceptionMapper<ConstraintViolationException>. check it here for more details.
If you are checking for the invalid field (ie client sends ragne fields instead of range), have a look at the stack trace on what exception is being thrown. Then register an exception mapper with your ExceptionDTO as body.
For example, if UnrecognizedPropertyException is thrown then you can add:
#Provider
public class UnrecognizedPropertyExceptionMapper implements ExceptionMapper<UnrecognizedPropertyException> {
#Override
public Response toResponse(UnrecognizedPropertyException e) {
ExceptionDTO myDTO = // build response
return Response.status(BAD_REQUEST).entity(myDTO).build();
}
}
If you want to validate input parameters in the request, you should return status code 400 (Bad Request) along with the error details. You can simply send json
{ "error": { "message": "string received for parameter x, where as int expected" } with the response status code 400.
`
I did a bit of research and determined that the best way to encode a Java exception in JSON is to use a convention developed by Oasis that looks like this:
{
"error": {
"code": "400",
"message": "main error message here",
"target": "approx what the error came from",
"details": [
{
"code": "23-098a",
"message": "Disk drive has frozen up again. It needs to be replaced",
"target": "not sure what the target is"
}
],
"innererror": {
"trace": [ ... ],
"context": [ ... ]
}
}
}
details is a list that should have an entry for each nested cause exception in the chain.
innererror.trace should include the stack trace if you wish, as a list of string values.
The response status code should be 400 unless you have a good reason for making it something else, and the code in the structure should match whatever you sent.
Write one method to convert a Java exception to this format, and you are done. Use it consistently and your JS code will be able to handle and display the exception values.
More of the details of the other approaches evaluated and dismissed are covered in this blog post on JSON REST API – Exception Handling
https://agiletribe.purplehillsbooks.com/2015/09/16/json-rest-api-exception-handling/
Here is the java method to convert an exception to this format:
public static JSONObject convertToJSON(Exception e, String context) throws Exception {
JSONObject responseBody = new JSONObject();
JSONObject errorTag = new JSONObject();
responseBody.put("error", errorTag);
errorTag.put("code", 400);
errorTag.put("target", context);
JSONArray detailList = new JSONArray();
errorTag.put("details", detailList);
String lastMessage = "";
Throwable runner = e;
while (runner!=null) {
String className = runner.getClass().getName();
String msg = runner.toString();
runner = runner.getCause();
JSONObject detailObj = new JSONObject();
detailObj.put("message",msg);
int dotPos = className.lastIndexOf(".");
if (dotPos>0) {
className = className.substring(dotPos+1);
}
detailObj.put("code",className);
System.out.println(" ERR: "+msg);
detailList.put(detailObj);
}
JSONObject innerError = new JSONObject();
errorTag.put("innerError", innerError);
JSONArray stackList = new JSONArray();
runner = e;
while (runner != null) {
for (StackTraceElement ste : runner.getStackTrace()) {
String line = ste.getFileName() + ":" + ste.getMethodName() + ":" + ste.getLineNumber();
stackList.put(line);
}
stackList.put("----------------");
runner = runner.getCause();
}
errorTag.put("stack", stackList);
return responseBody;
}

Json Exception wrong type

I use Gson in my project. But it returns me error
String sig = PortalConfig.getSignature(method, callId, params);
String url = PortalConfig.getUrl(method, callId, sig, params);
String plainResponse = BaseClientCommunicator.executeGetMethod(url);
GsonBuilder builder = new GsonBuilder();
Gson gsonObject = builder.create();
response = gsonObject.fromJson(plainResponse, GetMenuResponse.class);
return response;
example I get a Server-response like this
{
"group":
[
{
"id": "206896",
"name": "Ryż",
"info": "xyz"
},
{
"id": "206897",
"name": "Buraki",
"info": {}
}
]
}
and i have error Expected a string but was BEGIN_OBJECT
Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 16151
how should I handle this exception??
public class GetMenuResponse
{
#SerializedName("group")
private group[] group;
//method get and set
//to string method
}
public class group
{
#SerializedName("id")
private String id;
#SerializedName("name")
private String name;
#SerializedName("info")
private String info;
//method get and set
//to string method
}
I do not have access to the database, because I use the API
Problem is at line "info": {} in your json string.
Your class have private String info; String type and in your JSON string it is JSONObject.
It will try to convert JSONObject into String, which give error Expected a string but was BEGIN_OBJECT.GSON API cant able to cast JSONObject into JAVA String.
Value of info in first element of your array group is correct that is "info": "xyz" but same variable value in second element is different.
check value of info if it is String than you need to check your JSON response coming from server, if not than you need to change it's type into class variable.

Categories