Spring #RequestBody Override - java

I have a Spring controller that takes posts and it works. The only problem is that our SMS providers will be sending us headers that contain keys with a capitalized first letter, for example:
{
"FromPhoneNumber":"15177754077",
"ToPhoneNumber":"17572046106",
"ResponseReceiveDate":"7/29/2014 5:25:10 AM",
"Message":"PIN 1234"
}
Spring will throw an error like:
Could not read JSON: Unrecognized field "FromPhoneNumber" (class com.talksoft.spring.rest.domain.CDynePost), not marked as ignorable (4 known properties: "responseReceiveDate", "toPhoneNumber", "fromPhoneNumber", "message"])
So, there must be a way for me to override this behavior. Here is the controller method that handles the CDyne posts:
#RequestMapping(method = RequestMethod.POST, value="/celltrust")
public ResponseEntity<String> cellTrustPost(#RequestBody CDynePost cDynePost) {
String message = "FAILED";
UserInteraction userInteraction = getUserInteraction(cDynePost);
boolean success = someSpringService.logMessage(userInteraction);
if (success) {
message = "OK";
return new ResponseEntity<String>(message, HttpStatus.ACCEPTED);
} else {
return new ResponseEntity<String>(message, HttpStatus.FAILED_DEPENDENCY);
}
}
and here is the CDynePost class:
public class CDynePost {
private String FromPhoneNumber;
private String ToPhoneNumber;
private String ResponseReceiveDate;
private String Message;
public String getFromPhoneNumber() {
return FromPhoneNumber;
}
public void setFromPhoneNumber(String FromPhoneNumber) {
this.FromPhoneNumber = FromPhoneNumber;
}
public String getToPhoneNumber() {
return ToPhoneNumber;
}
public void setToPhoneNumber(String ToPhoneNumber) {
this.ToPhoneNumber = ToPhoneNumber;
}
public String getResponseReceiveDate() {
return ResponseReceiveDate;
}
public void setResponseReceiveDate(String ResponseReceiveDate) {
this.ResponseReceiveDate = ResponseReceiveDate;
}
public String getMessage() {
return Message;
}
public void setMessage(String Message) {
this.Message = Message;
}
}
I've looked at ObjectMapper but I am not sure how to work this into my controller, and truth be told I'd prefer not to have to write a bunch of extra classes if Spring will do it for free.

Simply annotate your field, getter, or setter with #JsonProperty, specifying the exact name that will appear in the JSON. For example
#JsonProperty("FromPhoneNumber")
private String FromPhoneNumber;

Related

How to pass API exception output to through own REST service?

Summary :
I want to pass valid exception output given by one REST service end point to the end user by using my own Rest service.
What I did is, I have called that service in service class using RestTemplate class, it's giving valid output on valid post request. But when I am passing invalid input to it I am getting only '400 BAD REQUEST' result in my service class where I have called that API. But when I am calling that API separately using postman, there I'm getting expected output.
Code sample :
class Abc {
ResponseEntity<String> = response;
static final String url = "https://abc-xyz.com/client-rest-end-point-url";
public ResponseEntity getDetails(RequestInput requestInput) {
try{
response=restTemplate.postForObject(url,requestInput,String.class);
} catch(Exception e) {
ResponseEntity response = (ResponseEntity<ErrorModel>)restTemplate.postForEntity(url,requestInput,ErrorModel.class);
}//try-catch
}//getDetails method
}//class
You can create a custom exception class for your entire application and you can send data in JSON by using throw keyword
Suppose you have exception class is:
public class TestException extends Exception {
private static final long serialVersionUID = 1L;
private String code;
private String detailMessage;
public TestException() {
};
public TestException(String message, String code, String detailMessage) {
super(message);
this.code = code;
this.detailMessage = detailMessage;
}
public TestException(String message, String code) {
super(message);
this.code = code;
}
//TestExceptionResponseCode is another class for message data, if required.
public TestException(TestExceptionResponseCode testExceptionResponseCode) {
super(testExceptionResponseCode.getMessage());
this.code = testExceptionResponseCode.getCode();
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDetailMessage() {
return detailMessage;
}
public void setDetailMessage(String detailMessage) {
this.detailMessage = detailMessage;
}
}
Now in your case throwing exception can be like :
class Abc {
ResponseEntity<String> = response;
static final String url = "https://abc-xyz.com/client-rest-end-point-url";
public ResponseEntity getDetails(RequestInput requestInput) {
if(requestInput==null){
throw new TestException("FAILED", "1", "Data can't be null");
}
}
Annotate your method with #ExceptionHandler annotation. You can code in seperate class from controller.
#ControllerAdvice
public class YourExceptionHandler {
#ExceptionHandler(CustomException.class)
public String xException() {
return "error/exception";
}
}

Remove properties from JSON in some cases

I have a class like this:
public class SampleDto {
private String normalProperty1;
private String normalProperty2;
private String normalProperty3;
private String sensitiveProperty1;
private String sensitiveProperty2;
public String getNormalProperty1() {
return normalProperty1;
}
public void setNormalProperty1(String normalProperty1) {
this.normalProperty1 = normalProperty1;
}
public String getNormalProperty2() {
return normalProperty2;
}
public void setNormalProperty2(String normalProperty2) {
this.normalProperty2 = normalProperty2;
}
public String getNormalProperty3() {
return normalProperty3;
}
public void setNormalProperty3(String normalProperty3) {
this.normalProperty3 = normalProperty3;
}
public String getSensitiveProperty1() {
return sensitiveProperty1;
}
public void setSensitiveProperty1(String sensitiveProperty1) {
this.sensitiveProperty1 = sensitiveProperty1;
}
public String getSensitiveProperty2() {
return sensitiveProperty2;
}
public void setSensitiveProperty2(String sensitiveProperty2) {
this.sensitiveProperty2 = sensitiveProperty2;
}
}
There are parts in the application where i need to serialize it as it is because the object is in a secure environment.
But i need to store the json in a db and store it without the sensitiveProperties, I can't just ignore the properties because they are needed in the other processes.
I was thinking to use Jackson views to solve the problem but i don't know if there is something special in Jackson where I can say, every json object that has the property "sensitiveProperty1" set it to null.
I'm using Java and Jackson
I think that this site covers what you're looking for pretty well.
Essentially what you'll want to do is to add #JsonIgnoreProperties(value = { "intValue" }) at the class level or #JsonIgnore at the field level and then Jackson should take care of the rest for you.
In your case that would look something like:
public class SampleDto {
#JsonIgnore
private String normalProperty1;
private String normalProperty2;
...

Spring map Enum in #RequestBody

In my controller I made an endpoint that allows status change:
#RequestMapping(value = "{ids}" + "/" + "status", method = RequestMethod.PUT)
public ResponseEntity<Void> changeStatus(#PathVariable final List<Integer> ids,
#NotNull #RequestBody final String status) {
deviceService.updateStatus(ids, status);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
And enum looks like this:
public enum DeviceStatus {
ACTIVE, INACTIVE, DELETED, ARCHIVED;
#JsonCreator
public static DeviceStatus parseWithValidation(String status) {
final String upperCaseStatus = status.toUpperCase();
if (exists(upperCaseStatus)) {
return DeviceStatus.valueOf(upperCaseStatus);
} else {
throw new UnsupportedStatusException();
}
}
private static boolean exists(final String upperCaseStatus) {
return Arrays.stream(values()).anyMatch(c -> c.name().equals(upperCaseStatus));
}
}
But Device domain object has a field Status of type DeviceStatus, so how should change status:
public void updateStatus(final List<Integer> ids, final String status) {
getByIds(ids).forEach(device -> {
device.setStatus(status);
update(device);
});
}
But there is a problem with device.setStatus(status);. I can use parseWithValidation but it doesn't make sense, because it is already done. Someone gives me {"status":"INACTIVE"} How should I parse this enum ?
EDIT: updated see comments
Your request body is an object with one field named status of type DeviceStatus, so you can probably use your Device class
So:
class Device {
// will be validated in the controller
private String status;
// getter, setter, etc
}
// with:
public enum DeviceStatus {
ACTIVE, INACTIVE, DELETED, ARCHIVED;
}
and #RequestBody Foo foo in the controller method signature:
public ResponseEntity<Void> changeStatus(#PathVariable final List<Integer> ids, #NotNull #RequestBody final Device device) {
try {
deviceService.updateStatus(ids, DeviceStatus.valueOf(device.getStatus()));
} catch(IllegalArgumentException ex) {
// throw some custom exception. device.getStatus() was invalid
} catch(NullPointerException ex) {
// throw some custom exception. device.getStatus() was null
}
// ...

Dropwizard deserialising custom exception as JSON?

I have created a new exception class in my Dropwizard service that extends BadRequestException.
public class CustomBadRequestException extends BadRequestException {
private static final long serialVersionUID = 1L;
private List<ValidationFailureDto> validationFailures;
public CustomBadRequestException() {
super();
}
public CustomBadRequestException(final List<ValidationFailureDto> validationFailures) {
super();
this.validationFailures = validationFailures;
}
#ApiModelProperty(value = "List of validationFailures")
public List<ValidationFailureDto> getValidationFailures() {
return validationFailures;
}
}
When I throw that exception at first I was only getting back the deserialised BadRequestException, minus the additional property (validationFailures)
{
code: "400",
message: "Bad request"
}
This is because Dropwizard's internals have a default exception mapper that allows Jetty/Jackson to understand domain exceptions and how to send the appropriate HTTP response.
To overcome this you can implement your own ExceptionMapper class and register it with Dropwizard.
public class CustomBadRequestExceptionMapper implements ExceptionMapper<SamplePackOrderBadRequestException> {
/**
* Allows jackson to deserialise custom exceptions and its properties to JSON response
*
* #param exception exception
* #return response object
*/
#Override
public Response toResponse(final SamplePackOrderBadRequestException exception) {
if (exception instanceof SamplePackOrderBadRequestException) {
SamplePackOrderBadRequestException samplePackOrderBadRequestException
= (SamplePackOrderBadRequestException) exception;
return Response
.status(400)
.entity(samplePackOrderBadRequestException)
.build();
}
return Response.status(400).build();
}
}
However this issue with this is that it deserializes super (Throwable), so you get every single inherited property added in the response which I do not want.
To combat this I tried adding Jackson annotations like so:
#JsonIgnoreProperties(value = "stackTrace")
This is not an optimal solution as there are several properties other than stackTrace that I will need to ignore.
So to summarise, how can I get Dropwizard to properly deserialize my CustomException class without all the additional clutter that I do not need?
I think the easier option is to transform exception to a Error bean and return it as shown below.
public class CustomBadRequestExceptionMapper implements ExceptionMapper<SamplePackOrderBadRequestException> {
#Override
public Response toResponse(final SamplePackOrderBadRequestException exception) {
if (exception instanceof SamplePackOrderBadRequestException) {
SamplePackOrderBadRequestException ex
= (SamplePackOrderBadRequestException) exception;
return Response
.status(400)
.entity(new ErrorBean(400,ex.getMessage,ex.getgetValidationFailures()))
.build();
}
return Response.status(400).build();
}
}
And ErrorBean.java
public static class ErrorBean{
private int code;
private String message;
private List<ValidationFailureDto> failures;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public List<ValidationFailureDto> getFailures() {
return failures;
}
public void setFailures(List<ValidationFailureDto> failures) {
this.failures = failures;
}
}

Rails flash messages in Java

What's the best way to achieve Rails-like flash messages such as "Update successful" http://api.rubyonrails.org/classes/ActionController/Flash.html) in the Java world? I'm using Spring MVC.
I have done just that in Spring MVC with a session scoped bean.
public class FlashImpl implements Flash, Serializable{
private static final long serialVersionUID = 1L;
private static final String ERROR = "error";
private static final String WARNING = "warning";
private static final String NOTICE = "notice";
private String message;
private String klass;
public void message(String klass, String message) {
this.klass = klass;
this.message = message;
}
public void notice(String message) {
this.message(NOTICE, message);
}
public void warning(String message) {
this.message(WARNING, message);
}
public void error(String message) {
this.message(ERROR, message);
}
public boolean isEmptyMessage() {
return message == null;
}
public void clear() {
this.message = null;
this.klass = null;
}
public String getMessage() {
String msg = message;
this.clear();
return msg;
}
public void setMessage(String message) {
this.message = message;
}
public String getKlass() {
return klass;
}
public void setKlass(String klass) {
this.klass = klass;
}}
The trick is in consumming the message once it's been read for the first time. This way it can survive to a redirect after post.
I am assuming that there will be only one type of message for request!. If you don't want this you could create a hashmap as already suggested.
I inject this bean in my controllers (actually I inject it in a base controller inherited by all the others).
In your JSP you have to add some code like this:
<c:if test="${!flash.emptyMessage}" >
<div class="${flash.klass}">${fn:escapeXml(flash.message)}</div>
</c:if>
I would recommend implementing this as a session-wide HashTable, with string keys mapping to custom FlashItem objects. The FlashItem will simply contain the object or string you're storing plus a boolean value, possibly called IsNew, which should be set to true when you insert a new item into the HashTable.
On each page load you then iterate the HashTable, set any IsNew = true items to false, and delete any items where IsNew is already false. That should give you a work-alike to Rails's flash feature.
This has been added to Spring MVC 3.1.RC1:
3.1.15 Flash Attributes and RedirectAttributes
Flash attributes can now be stored in a FlashMap and saved in the HTTP session to survive a redirect. For an overview of the general support for flash attributes in Spring MVC see Section 16.6, “Using flash attributes”.
In annotated controllers, an #RequestMapping method can add flash attributes by declaring a method argument of type RedirectAttributes. This method argument can now also be used to get precise control over the attributes used in a redirect scenario. See Section 16.3.3.10, “Specifying redirect and flash attributes” for more details.
(JIRA issue: SPR-6464)
I've used Manolo Santos' example with with Spring MVC as follows:
Annotate the Flash class with #Component, and add a boolean variable to indicate if the message should live for one more request.
#Component
public class Flash {
private static final String INFO = "info";
private static final String SUCCESS = "success";
private static final String ERROR = "error";
private static final String WARNING = "warning";
private static final String NOTICE = "notice";
private final Map msgs = new HashMap();
private boolean isKept; // keep msg for one more request (when the controller method redirects to another)
private void message(String severity, String message) {
msgs.put(message, severity);
}
public void info(String message) {
this.message(INFO, message);
}
public void success(String message) {
this.message(SUCCESS, message);
}
public void notice(String message) {
this.message(NOTICE, message);
}
public void warning(String message) {
this.message(WARNING, message);
}
public void error(String message) {
this.message(ERROR, message);
}
public boolean isEmptyMessage() {
return msgs.isEmpty();
}
public void clear() {
msgs.clear();
isKept = false;
}
public Map getMessage() {
return msgs;
}
public boolean isKept() {
return isKept;
}
public void keep() {
isKept = true;
}
public void unKeep() {
isKept = false;
}
}
Use an interceptor to add the flash message to the model object.
public class FlashMessageInterceptor extends HandlerInterceptorAdapter {
#Resource
Flash flash;
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if (!flash.isKept()) {
modelAndView.addObject("flash", flash);
}
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
if (flash.isKept()) {
flash.unKeep();
}
else {
flash.clear();
}
}
}
In your controller, if you have a method that redirects to another method you could just say; flush.keep(), to have the flash message displayed.
#Controller
public class ComputerCampLove {
#Resource
private Flash flash;
#RequestMapping(method = RequestMethod.GET)
public String takeMeToAnotherPlace(Model model) {
flash.info("Fa-fa-fa!");
flash.keep();
return "redirect:somewhere";
}
}
If you have not invested a huge amount of work into your spring java app, you could look at running rails on jruby. The beauty of running jRuby on Rails is that you can mix and match ruby gems and java libs.
If you have already put a fair amount of work into your application then this is more then likely not an option.

Categories