Get raw body from HttpMessageNotReadableException - java

I have a spring web service and sometimes a client calls it with a bad body, and spring throws HttpMessageNotReadableException.
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler({ HttpMessageNotReadableException.class })
public GenericErrorResp exceptionHandler(HttpMessageNotReadableException e) {
log.error("Invalid body", e);
return new GenericErrorResp();
}
Is it possible to get the original body? I have try with:
InputStream body = e.getHttpInputMessage().getBody();
but inputStream have no more bytes and mark/reset are not supported.

Update controller code like:
#ExceptionHandler(UnsatisfiedServletRequestParameterException.class)
private ResponseEntity<TransactionInfo> exception(UnsatisfiedServletRequestParameterException exception)
{
TransactionInfo transactionInfo = new TransactionInfo(ErrorStatus.FAIL);
log.error(exception.getMessage());
transactionInfo.addErrorList(new ErrorField(ErrorMessageBuilder.retriveCode(exception.getMessage()),
ErrorMessageBuilder.retriveMessage(exception.getMessage())));
transactionInfo.setStatusCode(HttpStatus.BAD_REQUEST.value());
return new ResponseEntity<>(transactionInfo, HttpStatus.BAD_REQUEST);
}
This is my TransactionInfo:
public class TransactionInfo implements Serializable {
private static final long serialVersionUID = -5920854604468221743L;
private ErrorStatus status;
private Object responseData;
private Integer statusCode;
private List<ErrorField> errorList = new ArrayList<ErrorField>();
}
Let me know if you have any concern.

Related

How to manipulate data form microservice response in Java, Spring Boot [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
I am working on a project in Spring Boot that has a microservice architecture. I needed to make a call from one microservice to another and with data that I get as response do something. I am very new to all microservice architecture and Spring Boot, so I figured I need a small push :)
So I have this class:
HttpDataClient.java
public class HttpDataClient implements DataClient{
private final static Logger LOGGER = LoggerFactory.getLogger(HttpDataClient.class);
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper = new ObjectMapper();
public HttpDataClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
#Override
public DataResponse getData(String dataId) {
try{
JsonNode node = restTemplate.exchange(
String.format("/data/%s", dataId),
HttpMethod.POST,
new HttpEntity<>(buildRequest(dataId), headers()),
JsonNode.class
).getBody();
return dataResponse(node);
}catch (HttpStatusCodeException e) {
String msg = String.format(
"Error getting data for dataId: %s",
dataId,
e.getStatusCode(),
e.getResponseBodyAsString());
LOGGER.error(msg);
return dataResponse.failed();
}
}
private MultiValueMap<String, String> headers() {
final LinkedMultiValueMap<String, String> mv = new LinkedMultiValueMap<>();
mv.set(HttpHeaders.CONTENT_TYPE, "application/json");
return mv;
}
private DataResponse dataResponse(JsonNode node) {
return DataResponse.dataResponse(
asString(node, "dataId"),
asString(node, "author"),
asString(node, "authorDataId"),
asString(node, "serverSideDataId")
);
}
private JsonNode buildRequest(String dataId) {
ObjectNode root = objectMapper.createObjectNode();
root.put("dataId", dataId);
return root;
}
}
And the interface
public interface DataClient {
DataResponse getData(String dataId);
}
And from this class when I get response I should be able to do next if author is "Philadelphia" then athorDataId and serverSideId are the same and if author is not "Philadelphia" then athorDataId and serverSideId are not the same.
For now, I have created this class:
public class DataResolver {
private final HttpDataClient client;
public DataResolver(HttpDataClient client) {
this.client = client;
}
}
And in this class, I should execute all of this:
And from this class when I get response I should be able to do next if author is "Philadelphia" then athorDataId and serverSideId are the same and if author is not "Philadelphia" then athorDataId and serverSideId are not the same.
But I don't know how to start. I know I suppose to use client to get the response data. I am not sure how to use it.. And then I should probably do something like this :
if (author == Philadelphia) {
authorDataId == serverSideDataId
} elseif(author != Philadelphia) {
authorDataId != serverSideDataId
}
UPDATE
Is it possible to create new class like this
public class DataResolver {
private final HttpDataClient client;
public DataResolver(HttpDataClient client) {
this.client = client;
}
}
And in this class access data from response and manipulate it in some way?
Something like this:
public class DataResolver {
private final HttpDataClient client;
public DataResolver(HttpDataClient client) {
this.client = client;
}
public DataIdResolver idResolver() {
if (author == Philadelphia) {
authorDataId == serverSideDataId
} elseif(author != Philadelphia) {
authorDataId != serverSideDataId
}
}
}
If the response that you expect should contain the mentioned attributes, then you should create the following class and use it as the return type in restTemplate.exchange instead of JsonNode.class (assuming you have Jackson in your classpath):
public class DataClient {
private String dataId;
private String author;
private String authorDataId;
private String serverSideDataId;
// getters and setters
}
So you would have something like in the HttpDataClient class:
#Override
public DataClient getData(String dataId) {
try{
DataClient data = restTemplate.exchange(
String.format("/data/%s", dataId),
HttpMethod.POST,
new HttpEntity<>(buildRequest(dataId), headers()),
DataClient.class).getBody();
return data;
}catch (HttpStatusCodeException e) {
String msg = String.format(
"Error getting data for dataId: %s",
dataId,
e.getStatusCode(),
e.getResponseBodyAsString());
LOGGER.error(msg);
return dataResponse.failed();
}
}
And DataResolver:
#Component
public class DataResolver {
private final HttpDataClient client;
public DataResolver(HttpDataClient client) {
this.client = client;
}
public DataClient idResolver(String dataId) {
DataClient data = client.getData(dataId);
// Whatever logic you need
return data;
}
}
Maybe one important thing here is that you most likely would want to make HttpDataClient a Spring bean by adding the #Service annotation. By doing this you can autowire it to any other Spring bean you need.

Return JSON on error when header {"Accept":"application/octet-stream"} in request

I´m having some issues when returning some errors from a rest WebService.
Making a request with the header {"Accept":"application/octet-stream"}
(the service returns a document ResponseEntity<InputStreamResource> if all the process goes well).
When all the process goes well the document is downloaded fine, but when an error is occurred and the code jumps to the #ControllerAdvice and tries to return a JSON error. Here comes the problem, when trying to return the JSON springs crashes:
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
Here is a example of some code:
Controller
#RequestMapping(value = "/test", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_OCTET_STREAM_VALUE })
public ResponseEntity<CustomError> test() throws Exception {
throw new Exception();
}
ControllerAdvice
#ControllerAdvice
public class ExceptionHandlerAdvice {
private static final Logger logger = LogManager.getLogger(ExceptionHandlerAdvice.class);
#ExceptionHandler({Exception.class,Throwable.class})
#ResponseBody
public ResponseEntity<CustomError> handleUnhandledException(Exception exception) {
CustomError error = new CustomError(exception.getMessage());
return new ResponseEntity<CustomError>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
CustomError:
public class CustomError {
private String errorDescription;
public CustomError(String errorDescription) {
super();
this.errorDescription = errorDescription;
}
public String getErrorDescription() {
return errorDescription;
}
public void setErrorDescription(String errorDescription) {
this.errorDescription = errorDescription;
}
}
I´ve also tried returning new headers on #controllerAdvice
#ExceptionHandler({Exception.class,Throwable.class})
#ResponseBody
public ResponseEntity<CustomError> handleUnhandledException(Exception exception) {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
CustomError error = new CustomError(exception.getMessage());
return new ResponseEntity<CustomError>(error,headers, HttpStatus.INTERNAL_SERVER_ERROR);
}
Any idea how can I make this work or ignore Accept header on response?
It´s possible?
Thanks in advance
This exception means your response type not match with your request header. If you are expecting JSON/Stream to be returned, your request header should be {"Accept":"application/octet-stream,application/json"}.

Define custom error message in REST using Spring on Weblogic

I am hosting Spring Boot app on weblogic 10.3.6 for REST api
I'd like to implement these 2 features:
Whenever my custom exception occurs I would like to send a http response with message e.g. 500 - "request couldn't be parsed because of something..."
Whenever any error is thrown I would like to get the stack trace that is usually printed to console (for debugging purposes)
I tried to solve the first part the following way:
#ControllerAdvice
public class ExceptionHandlerAdvice {
#ExceptionHandler(MyException.class)
public ResponseEntity handleException(MyException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
}
However as a response I only get 500 Internal Server Error with no message
As for the second part I tried simmilar approach but there was no error message either.
How can I solve this?
EDIT:
The best I could achieve was removing ExceptionHandlerAdvice and using annotation on my exception class instead like this:
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR, reason="This is a message I'd like to send")
It works, but I have to add some variables from code to the message and this method does not allow it
EDIT2:
This is a bit weird, perhaps a behavior of weblogic server, but when I set the httpStatus to HttpStatus.ACCEPTED I can see the message, if it is HttpStatus.Forbidden or any other 4xx error I just get the error without message
Create 'ResponseEntity' object with message and status and return it, it will display with error message.
/**
* HTTP_STATUS - 500 -Service Unavailable.
*
* #param exception
* Catches the following: MyException
* #return
*/
#ExceptionHandler({ MyException.class})
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
public static ResponseEntity<?> handleConnectionErrorResponse(MyException exception) {
return new ResponseEntity<String>("Some error occurred at server", HttpStatus.INTERNAL_SERVER_ERROR);
}
Do not return something, throw an :
#ResponseStatus(value = HttpStatus.UNPROCESSABLE_ENTITY)
public class UnprocessableEntity extends RuntimeException {
public UnprocessableEntity(String string) {
super(string);
}
}
Or another thing like that.
I also went through the same requirement.
Below is the code which is working for me:
String errMsg = "{\"errorMessage\":\"Parking Slot is not available\"}";
return new ResponseEntity<String>(errMsg, HttpStatus.INTERNAL_SERVER_ERROR);
Whereas errMsg should be written in the format which you want. Like I had requirment for response in JSON.
Hope this will help some of you
Well In my case I have done custom error handling logic.
We can define a custom Base Response class wich accepts generic type(Eg: user desired model)
Return BaseResponse as a response for each REST Methods
(GET, PUT, POST, DELETE, etc)
BaseResponse.java
public class BaseResponse<T> {
int statusCode;
String message;
T data;
public int getStatusCode() {
return statusCode;
}
public BaseResponse<T> getValidResponse(String message, T data) {
BaseResponse<T> baseResponse = new BaseResponse<T>();
baseResponse.statusCode = 200;
baseResponse.message = message;
baseResponse.data = data;
return baseResponse;
}
public BaseResponse<T> getErrorResponse(int StatusCode, String message) {
BaseResponse<T> baseResponse = new BaseResponse<T>();
baseResponse.statusCode = StatusCode;
baseResponse.message = message;
return baseResponse;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
User.java
public class User {
String userName;
String userAddress;
String userEmail;
String userPhoneNumber;
...
//Getters & Setters
}
YourController.java
....
#PostMapping("/addUser")
public BaseResponse<User> addUser(User user) {
if (user.getUserName() != null && !user.getUserName().equals("")) {
UserEntity userEntity = new UserEntity();
userEntity.setName(user.getUserName());
...
userRepository.save(userEntity);
return new BaseResponse<User>().getValidResponse("Successfully Added User", user);
} else {
return new BaseResponse<User>().getErrorResponse(400, "Name field is required");
}
}
...
#DeleteMapping("/deleteUser/{userId}")
//Using ? for uncertain Response.Eg: Some response might have data response and some may not have data response...
public BaseResponse<?> deleteUser(#PathVariable(value = "userId") int userId) {
//After delete operation...we don't require data response.
return new BaseResponse<User>().getValidResponse("Successfully deleted the User", null);
}
This might not be an exact solution for the question asked but will surely help someone else.

Best way to model Android REST connection

I am making an Android app that connects to a web service via REST API and I have a dilemma with the design of the internal architecture.
Now I have class Client.java whose purpouse is make connect with the server (ConnectionMethod is Enum that contains GET|POST values):
public class Client {
private AsyncHttpClient client = new AsyncHttpClient(); //I use com.loopj.AsyncHttpClient to connect
private ConnectionMethod method;
private RequestParams params = new RequestParams();
private AsyncHttpResponseHandler responseHandler = new JsonHttpResponseHandler(){
#Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
//Actions when connection success
}
#Override
public void onFailure(int statusCode, Header[] headers, JSONObject response, Throwable error) {
//Actions when connection fails
}
};
public Client (RequestParams params, ConnectionMethod method) {
this.params = params;
this.method = method;
}
public void addParameters (Map<String, String> parameters) {
for (Map.Entry<String, String> entry : parameters.entrySet()) {
this.params.put(entry.getKey(), entry.getValue());
}
}
public ServerResponse connect () {
RequestHandle handle;
if (this.method==ConnectionMethod.POST) {
handle = postRequest();
}
else {
handle = getRequest();
}
//How can I treat here different type of responses homogeneously?
}
private RequestHandle getRequest () {
return client.get(Constants.getEndpoint(), this.params, this.responseHandler);
}
private RequestHandle postRequest () {
return client.post(Constants.getEndpoint(), this.params, this.responseHandler);
}
}
A sample method that requests info from the server is this:
public static void login (String login, String password) {
//This classes should be static or dynamic?
Map<String, String> map = new HashMap<String, String>();
map.put("login", login);
map.put("password", password);
map.put("method", "site_login");
Client c = new Client();
c.addParameters(map);
c.getRequest();
}
All server responses are JSON: {status:0, result:array/int/string} when response is correct and {status:-1, message:string} when response is incorrect.
Additionaly I want to make classes to model components from JSON result (User.java, Message.java...) and intermediate methods between UI and API to implement the logic of the app and classes.
What is the best way to design an homogeneous connection system that manages automatically correct/fail response and independent of model (user, message...)?
There is a bunch of frameworks which can make this whole process much easier.
For example Retrofit is very simple framework for mapping java classes to REST calls. It comes with gson which will automatically deserialize response from json to plain java objects.
It also allows use callbacks as well as rxJava Observables. It allows to handle errors as well.
You can check sample app: https://github.com/JakeWharton/u2020
You are describing tools that already exist. My favorite happens to be Retrofit but there are others out there. Retrofit can handle the success and fail responses and even map JSON directly to a POJO.
My API client
public class ApiClient {
private static ApiInterface sApiInterface;
public static ApiInterface getApiClient(Context context) {
//build the rest adapter
if (sApiInterface == null) {
final RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("example.com")
.build();
sApiInterface = restAdapter.create(ApiInterface.class);
}
return sApiInterface;
}
public interface ApiInterface {
#GET("/program/{id}")
void getProgram(#Path("id") int id, RetrofitCallback<Program> callback);
}
My RetrofitCallback
public class RetrofitCallback<S> implements Callback<S> {
private static final String TAG = RetrofitCallback.class.getSimpleName();
#Override
public void success(S s, Response response) {
}
#Override
public void failure(RetrofitError error) {
Log.e(TAG, "Failed to make http request for: " + error.getUrl());
Response errorResponse = error.getResponse();
if (errorResponse != null) {
Log.e(TAG, errorResponse.getReason());
if (errorResponse.getStatus() == 500) {
Log.e(TAG, "Handle Server Errors Here");
}
}
}
}
My model
public class Program {
#Expose
private doublea.models.Airtime Airtime;
#Expose
private String id;
#Expose
private String title;
#SerializedName("short_name")
#Expose
private String shortName;
#SerializedName("full_description")
#Expose
private String fullDescription;
#SerializedName("short_description")
#Expose
private String shortDescription;
#Expose
private doublea.models.Image Image;
#SerializedName("image")
#Expose
private String imageName;
#Expose
private List<Host> hosts = new ArrayList<Host>();
#Expose
private List<Category> categories = new ArrayList<Category>();
#Expose
private List<Airtime> airtimes = new ArrayList<Airtime>();
/** Getters and Setters */
public Program() {
}
How it is used.
private void executeProgramApiCall(int programId) {
ApiClient.getApiClient(this).getProgram(programId, new RetrofitCallback<Program>() {
#Override
public void success(Program program, Response response) {
super.success(program, response);
addDataToAdapter(program);
}
});
}

Documenting a wrapped REST response using swagger UI

I have a WidgetDto that I have annotated with swagger UI annotations. The final response wraps a list of WidgetDtos with a layer of metadata (per page 21 of this RESTful best practices document). For example:
{
"data" : [
{
"id" : 1234,
"prop1" : "val1"
...
},
{
"id" : 5678,
"prop1" : "val2"
...
},
...
]
}
My java code looks like this:
#GET
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(
value = "Get all widgets.",
response = WidgetDto.class
)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Returns the list of widgets.")
})
public Response getWidgets() {
List<WidgetDto> widgets;
...
Map<String, Object> responseBody = new HashMap<>();
responseBody.put("data", widgets);
return Response.ok(responseBody).build();
}
I'd like to reuse this pattern on multiple resources, and I don't want to create list DTOs for every response type. Is there an elegant way to use swagger to document these types of response bodies?
Your metadata is not a part of your resource but it's a part of your resource's representation.
In my case, responses types are 'application/hal+json' and 'application/json', each of them use a different wrapper with different metadatas.
To solve this problem, I created an extern document to explain these two wrappers and for each of them, how a single resource and a list of resources are represented with metadata.
I think my choice is correct because I separate the resource of its representations (per page 7 'Manipulation of Resources Through Representations' of this RESTful best practices document)
In your case, you returns a list of WidgetDtos, the layer of metadata is a part of the representation of your resource.
However, you can use a generic class like Resource and Resources used by spring-hateoas :
public class Resources<T> implements Iterable<T> {
private final Collection<T> content;
Resources(Iterable<T> content) {
this.content = new ArrayList<T>();
for (T element : content) {
this.content.add(element);
}
}
}
And use it like this:
#GET
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(
value = "Get all widgets.",
response = WidgetDto.class
)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Returns the list of widgets.")
})
public Response getWidgets() {
List<WidgetDto> widgets;
...
return Response.ok(new Resources<WidgetDto>(widgets)).build();
}
I faced a similar problem a few months ago when I was developing a project for school. The solution is to create an envelope and always return it. The envelope will contain a feild "data" which is a generic; so you will be able to bind it to any data type.
Note that even though I used it I later on read that it should be used scarecly (I think your case is a good example of usage) but technically an Exception object should be thrown if the request failed.
Anyway this is my Response class which I used to return all my responses:
public class Response <AnyData> {
private static final String SUCCESS = "success";
private static final String FAILURE = "failure";
private String status;
private AnyData data;
private String error;
private Response(String status, AnyData data, String error) {
this.status = status;
this.data = data;
this.error = error;;
}
private Response(String status, AnyData data) {
this(status, data,"");
}
private Response(String status, String error) {
this(status, null, error);
}
public static <AnyData> Response<AnyData> success(AnyData data) {
return new Response<AnyData>(SUCCESS, data);
}
public static <AnyData> Response<AnyData> failure(String error) {
return new Response<AnyData>(FAILURE, error);
}
public static <AnyData> Response<AnyData> unimplemented() {
return new Response<AnyData>(FAILURE, "Missing implementation in the backend.");
}
public static <AnyData> Response<AnyData> failureUserNotFound() {
return Response.failure("User not found!");
}
public static <AnyData> Response<AnyData> failureBusinessNotFound() {
return Response.failure("Business not found!");
}
// Removed getters and setters for simplicity.
}
After this is set we will just create the responses right from the Comtroller. I changed it a bit to make it work with the sample is should be legible enough. Note that I have static methods for my responses: 'success()', 'error()'...
#RestController
#Api(tags={"Widgets"})
public class WidgetController {
#RequestMapping(value="/api/widgets", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON)
#ApiOperation(value = "Get all widgets.")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Returns the list of widgets.")
})
public Response<List<WidgetDto>> getWidgets() {
List<WidgetDto> widgets = new LinkedList<>();
widgets.add(new WidgetDto(1234, "val1"));
widgets.add(new WidgetDto(5678, "val2"));
return Response.success(widgets);
}
}
And here is a sample of the response body:
Hope this helps.
You can define the responseContainer attribute in the #ApiOperation annotation.
The value List will wrap your WidgetDto in a container.
#ApiOperation(
value = "Get all widgets.",
response = WidgetDto.class,
responseContainer = "List"
)

Categories