I have Implemented a Restful Webservice which produces JSON as my response by anotating #Produces("application/json")
Am Handling application errors with error codes. So i form an object with error code and error message. So when ever there is an application exception, it will produces in XML format. How can produce those in JSON format?
Yes You Can. Catch your Exception and handle it with your custom exception class and throw the exception class by setting response content type as application/json
try{
InitialContext ctx = new InitialContext();
TestIntf intf = (TestIntf)ctx.lookup("java:comp/env/ejb/TestImpl");
}catch(NamingException namingException) {
CustomException lookupException = new CustomException("SYSJNDIE001", namingException.getMessage());
throw lookupException;
}
And your Exception class would look like this
public class CustomException extends Exception {
private static final long serialVersionUID = -5358934896070563181L;
public CustomException(String errorCode, String message) {
super(errorCode, message);
}
}
Create a Provider class Mapper for Exception
#Provider
public class FaultResponseMapper implements ExceptionMapper<CustomException> {
#Context
private HttpHeaders headers;
#Override
public Response toResponse(CustomException customException) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(customException)
.type(MediaType.APPLICATION_JSON).build();
}
}
Related
Here is a code snippet i came across one of the youtube tutorials for api automation using rest assured:
public class getCallTest extends testBase {
public testBase testbase;
public String endpoint;
public String serviceurl;
public String uri;
public CloseableHttpResponse httpresponse;
public HttpEntity httpentity;
public RestClient restclient;
public String entitybody;
#BeforeMethod
public void setUp() {
testbase = new testBase();
endpoint = prop.getProperty("endpointurl");
serviceurl = prop.getProperty("serviceurl");
uri = endpoint + serviceurl;
System.out.println(uri);
}
#Test
public void getCall() {
// fetching status code from the http response
restclient = new RestClient();
try {
httpresponse=restclient.getCall(uri);
} catch (Exception exception) {
exception.getMessage();
exception.printStackTrace();
}
int statuscode = httpresponse.getStatusLine().getStatusCode();
System.out.println("Http response code is :" + statuscode);
// fetching the http entity(this http entity will have entity header and entity
// body)
httpentity = httpresponse.getEntity();
try {
entitybody = EntityUtils.toString(httpentity);
} catch (Exception exception) {
System.out.println("Some exception occurred while converting http entity to string");
exception.getMessage();
exception.printStackTrace();
}
// convert the entitybody json string to json object
JSONObject jsonresponse = new JSONObject(entitybody);
System.out.println(jsonresponse);
After fetching the response entity using getEntity(),why do we need to cconvert it into string using EntityUtils.toString() and then to a JSON object ?
I have a String that represents JSON:
[{"type":"ERROR","message":"some error message"}]
This String is returned by ex.contentUTF8() in the following code:
#RestControllerAdvice
#RequiredArgsConstructor
public class ControllerExceptionHandler {
#ExceptionHandler(FeignException.class)
#ResponseBody
public String afterThrowing(FeignException ex, HttpServletResponse response) {
response.setStatus(ex.status());
return ex.contentUTF8();
}
}
An HTTP client though doesn't get a JSON response though. Instead, it gets HTML with body of the response being that String. How can I instead have JSON returned? Do I need to deserialize that String?
Update
The response to the client must be exactly the JSON object that represent (would be serialized into) the String returned by ex.contentUTF8():
[{"type":"ERROR","message":"some error message"}]
So, a wrapper object like ErrorDto (as provided in a few answers) will not work.
You should return an Object that fit with your JSON representation :
public class ErrorDto {
private String type;
private String message;
}
And :
#RestControllerAdvice
#RequiredArgsConstructor
public class ControllerExceptionHandler {
#ExceptionHandler(FeignException.class)
#ResponseBody
public ErrorDto afterThrowing(FeignException ex, HttpServletResponse response) {
return new ErrorDto("your type", "your message");
}
}
Building from the answer by #Fabien, you can use the ResponseEntity<Object> class to return an application/json content type response instead. Using ResponseEntity essentially gives control over every aspect of the response.
You can try something like this instead:
#ExceptionHandler(FeignException.class)
public ResponseEntity<Object> afterThrowing(FeignException ex) {
ErrorDto payload = new ErrorDto(ex.status(), ex.contenUTF8());
return ResponseEntity
.status(ex.status())
.contentType("application/json")
.body(payload);
}
Am writing a Restful Webservice Impl, where i consume and produce response in JSON format by annotating #Produces("application/json"). Am producing JSON response as well. Here am handling exception with a class where it has error code and error message. When am getting exception it is not produced in application/json format. I used ExceptionMapper to find a solution but it is `text/plain format.
snippet
public Class Confiuration{
#Path("getData")
#Consumes("application/json")
#Produces("application/json")
public JSONGetDataResponseVo getData(GetDataRequestVo datarequestVO)
throws FaultResponse {
JSONGetDataResponseVo response=new JSONGetDataResponseVo ();
DataServiceValidator.validateGetConfigurationAndDataRequest(datarequestVO);
....
....
}catch(ApplicationException applicationException){
throw new FaultResponse(applicationException,locale);
}
}
FaultResponseMapper
#Provider
public class FaultResponseMapper implements ExceptionMapper<FaultResponse> {
#Context
private HttpHeaders headers;
public Response toResponse(FaultResponse faultResponse) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(faultResponse).type(MediaType.APPLICATION_JSON).build();
}
}
Application Exception
public abstract class ApplicationException extends Exception{
private java.lang.String errorCode;
public ApplicationException(String errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public ApplicationException(String message) {
super(message);
}
public java.lang.String getErrorCode() {
return this.errorCode;
}
public abstract String getLocaleMessage(Locale locale);
}
FaultResponse
public class FaultResponse extends WebApplicationException {
private String errorCode;
private String errorMessage;
private String localErrorMessage;
public FaultResponse(String errorCode, String errorMessage,
String localErrorMessage) {
this.errorCode = errorCode;
this.errorMessage = errorMessage;
this.localErrorMessage = localErrorMessage;
}
public FaultResponse(ApplicationException applicationException,
Locale locale) {
this.errorCode = applicationException.getErrorCode();
this.errorMessage = applicationException.getMessage();
if (locale != null
&& applicationException.getLocaleMessage(locale) != null) {
this.localErrorMessage = applicationException
.getLocaleMessage(locale);
} else {
this.localErrorMessage = applicationException.getMessage();
}
}
}
So here how can i produce my faultResponse in JSON format.
This has to do with the fact that you are returning an exception as a response. I would
Make an exception mapper for ApplicationException.
Refactor FaultResponse to not extend and exception. Just create it in the mapper.
In order to see the response, you will need to send a status other than No Content. You can't have a body in it. Send somethng like Bad Request.
You can just declare the resource method as throws ApplicationException. You don't need to catch it and rethrow.
I've made these changes, and it works fine.
UPDATE: with complete test
Added getters (required for marshalling) to FaultResponse and remove the exception extension
public class FaultResponse {
...
public String getErrorCode() { return errorCode; }
public String getErrorMessage() { return errorMessage; }
public String getLocalErrorMessage() { return localErrorMessage; }
...
}
Created a Service for testing and ApplicationException implementation
public class ApplicationExceptionImpl extends ApplicationException {
public ApplicationExceptionImpl(){
this("400", "Bad Request");
}
public ApplicationExceptionImpl(String errorCode, String message) {
super(errorCode, message);
}
#Override
public String getLocaleMessage(Locale locale) {
return "Bad Request";
}
}
public class FaultService {
public void doSomething() throws ApplicationException {
throw new ApplicationExceptionImpl();
}
}
Resource class
#Path("fault")
public class FaultResource {
FaultService service = new FaultService();
#GET
public Response getException() throws ApplicationException {
service.doSomething();
return Response.ok("Cool").build();
}
}
ExceptionMapper
#Provider
public class ApplicationExceptionMapper implements ExceptionMapper<ApplicationException> {
#Override
public Response toResponse(ApplicationException exception) {
FaultResponse response = new FaultResponse(exception, Locale.ENGLISH);
return Response.status(Response.Status.BAD_REQUEST)
.entity(response).type(MediaType.APPLICATION_JSON).build();
}
}
ApplicationException class is left the same
curl -v http://localhost:8080/api/fault
{"errorCode":"400","errorMessage":"Bad Request","localErrorMessage":"Bad Request"}
If after this you are still not seeing JSON, it's possible you do not have a provider configured. If this is the case, please show your application configuration, along with your project dependencies.
Can I handle Jackson UnrecognizedPropertyException for a #RequestBody parameter? How can I configure this?
I'm working on a spring MVC project, and I use jackson as json plugin. Any mis-spell of the field name in a json request will lead to a error page, which should be a json string consist of error message. I'm a newbie to spring, and I think this error handling can be done with some spring configuration, but failed after several attempts. Any help?
Here is my mvc configure:
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver resolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
return bean;
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
My controller:
#RequestMapping(value = "/Login", method = RequestMethod.POST,
consumes="application/json", produces = "application/json")
public #ResponseBody AjaxResponse login(
#RequestBody UserVO user, HttpServletRequest request) {
//do something ...
}
Normal request json is:
{"Username":"123123", "Password":"s3cret"}
But if I send the following request:
{"Username":"123123", "pwd":"s3cret"}
which field name is mis-spell, then Spring catch this UnrecognizedPropertyException, and returned a error page, but I want to catch this exception and return a json string. How can I achieve this?
Use #ExceptionHandler annotation. Some documentation about it: http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
#Controller
public class WebMvcConfig {
#RequestMapping(value = "/Login", method = RequestMethod.POST,
consumes="application/json", produces = "application/json")
public #ResponseBody AjaxResponse login(#RequestBody UserVO user, HttpServletRequest request) {
//do something ...
}
#ExceptionHandler(UnrecognizedPropertyException.class)
public void errorHandler() {
// do something. e.g. customize error response
}
}
public class RESTDataServiceClient{
private Client client;
private String dataServiceUri;
private String dataServiceResource;
private CustomData customData;
public RESTDataServiceClient(String dataServiceUri, String dataServiceResource, Client client){
this.client = client;
this.dataServiceUri = dataServiceUri;
this.dataServiceResource = dataServiceResource;
}
#Override
public CustomData getCustomData() {
WebTarget dataServiceTarget = client.target(dataServiceUri).path(dataServiceResource);
Invocation.Builder invocationBuilder = dataServiceTarget.request(MediaType.APPLICATION_JSON_TYPE);
Response response = invocationBuilder.get();
myCustomData = response.readEntity(CustomData.class);
return myCustomData;
}
}
CustomData.java
public class CustomData{
private TLongObjectMap<Map<String, TIntIntMap>> data;
public CustomData() {
this.data = new TLongObjectHashMap<>();
}
//getter and setter
}
sample json content
{"50000":{"testString":{"1":10}},"50001":{"testString1":{"2":11}} }
I am trying to get data from a data service which is going to return data in a JSON format. I am trying to write a client to read that JSON into a custom object. The CustomData contains a nested trove map datastructure. we wrote a custom serializer for that and the server part works fine. I am unable to get the rest client read the data into an object, but reading into string works. I tried above pasted code with the sample data and i get the error below.
javax.ws.rs.ProcessingException: Error reading entity from input stream.
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:866)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:783)
at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:326)
at org.glassfish.jersey.client.InboundJaxrsResponse$1.call(InboundJaxrsResponse.java:111)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:399)
at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:108)
at com.sample.data.RESTDataServiceClient.getCustomData(RESTDataServiceClient.java:42)
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "50000" (class com.sample.data.CustomData), not marked as ignorable (0 known properties: ])
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#2cb89281; line: 1, column: 14] (through reference chain: com.sample.data.CustomData["50000"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)
at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:671)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:773)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1297)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1275)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:247)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118)
at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:1233)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:677)
at com.fasterxml.jackson.jaxrs.base.ProviderBase.readFrom(ProviderBase.java:777)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:264)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:234)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:154)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1124)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:851)
... 38 more
TLongObjectMap is not deserializable out of the box, so how you made a custom serializer you also need to implement a custom deserializer. You can package these up nicely in a module and add it to your ObjectMapper.
It looks like there is a Trove module in development right now, which you can download and add to your ObjectMapper the same as the example below. The TIntObjectMapDeserializer implementation in that link is much more robust then my solution, so I would recommend using that class in your project if possible.
If you want to try and write it yourself, here's a starting point that properly deserializes your provided example:
public class FakeTest {
#Test
public void test() throws Exception {
ObjectMapper om = new ObjectMapper();
om.registerModule(new CustomModule());
String s = "{\"50000\":{\"testString\":{\"1\":10}},\"50001\":{\"testString1\":{\"2\":11}} }";
CustomData cd = om.readValue(s, CustomData.class);
System.out.println(cd.getData());
}
public static class CustomData {
private TLongObjectMap<Map<String, TIntIntMap>> data;
public CustomData() {
this.data = new TLongObjectHashMap<>();
}
public TLongObjectMap<Map<String, TIntIntMap>> getData() { return data; }
public void setData(TLongObjectMap<Map<String, TIntIntMap>> data) { this.data = data; }
}
public static class CustomModule extends SimpleModule {
public CustomModule() {
addSerializer(CustomData.class, new CustomSerializer());
addDeserializer(CustomData.class, new CustomDeserializer());
}
public static class CustomSerializer extends JsonSerializer<CustomData> {
#Override
public void serialize(CustomData value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
// add custom serializer here
}
}
public static class CustomDeserializer extends JsonDeserializer<CustomData> {
#Override
public CustomData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
TLongObjectMap<Map<String, TIntIntMap>> data = new TLongObjectHashMap<>();
ObjectNode node = jsonParser.getCodec().readTree(jsonParser);
Iterator<Map.Entry<String,JsonNode>> fields = node.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> entry = fields.next();
ObjectNode value = (ObjectNode) entry.getValue();
Map.Entry<String, JsonNode> innerField = value.fields().next();
ObjectNode innerNode = (ObjectNode) innerField.getValue();
Map.Entry<String, JsonNode> innerInnerField = innerNode.fields().next();
TIntIntMap intMap = new TIntIntHashMap();
intMap.put(Integer.parseInt(innerInnerField.getKey()), innerInnerField.getValue().asInt());
Map<String, TIntIntMap> innerMap = Collections.singletonMap(innerField.getKey(), intMap);
data.put(Long.parseLong(entry.getKey()), innerMap);
}
CustomData customData = new CustomData();
customData.setData(data);
return customData;
}
}
}
}