Change response code in AbstractHttpMessageConverter - spring

Is it possible to change the response status code in a AbstractHttpMessageConverter writeInternal() method?
In my AbstractHttpMessageConverter (extending MappingJackson2HttpMessageConverter) I want to change error responses to 200 and add the actual status code (e.g. 400) to a status field in the json.
EDIT1
I'm returning code like:
JSONObject json= new JSONObject();
json.put("name", "My Name");
return new ResponseEntity<JSONObject>(json, HttpStatus.OK);
or in case of an error:
JSONObject json= new JSONObject();
json.put("error", "My Error");
return new ResponseEntity<JSONObject>(json, HttpStatus.BAD_REQUEST);
Somewhere I want to intercept the response body and:
a) wrap the original response body (enity) with status code
b) change response status code to 200
So for both variants this would mean:
{ "status": 200, "response": { "name": "My Name" } }
{ "status": 400, "response": { "error": "My Error" } }
and in both case a http status 200 will be returned.
I was thinking of doing this by extending the MappingJackson2HttpMessageConverter and overiding the writeInternal method, but unfortunately there I cannot change the status code.
Note I don NOT want to this in my controller classes. They should just return the base json structure.

No, it is not possible. The HttpMessageConverter can set certain headers and write the body of a message, but it cannot set the status code. Perhaps you can change the status code to 200 and set a property to 400 before sending the object to the HttpMessageConverter.
I like to use the ResponseEntity object, which allows you to set a status code along with an object, but without seeing your code, I don't know if it will work for you.

Related

Spring WebClient throw error based on response body

I am using Spring WebClient to call REST API. I want to throw an error based on the response. For example, if there is an error (400) with body
{"error": "error message1 "}
then I want to throw an error with "error message1". Same way if there is an error(400) with the body
{"error_code": "100020"}
then I want to throw an error with error_cde 100020. I want to do it in a non-blocking way.
public Mono<Response> webclient1(...) {
webClient.post().uri(createUserUri).header(CONTENT_TYPE, APPLICATION_JSON)
.body(Mono.just(request), Request.class).retrieve()
.onStatus(HttpStatus::isError, clientResponse -> {
//Error Handling
}).bodyToMono(Response.class);
}
A body from ClientResponse should be extracted in a reactive way (javadoc) and lambda in onStatus method should return another Mono (javadoc). To sum up, take a look at below example
onStatus(HttpStatus::isError, response -> response
.bodyToMono(Map.class)
.flatMap(body -> {
var message = body.toString(); // here you should probably use some JSON mapper
return Mono.error(new Exception(message));
})
);

Spring-boot ResponseEntity body missing for non-200 status codes

TLDR; How can I send a text message in the body along with a 304 status code using Spring ResponseEntity ?
Context
I am writing a REST API with Spring-boot. In some endpoints, I want to return:
either status 200 OK with body Success,
or status 304 NOT MODIFIED with body Not modified.
My endpoints use ResponseEntity (in kotlin) in the following way:
#PutMapping("/test")
fun modifyStuff(): ResponseEntity<String> {
if (someCondition)
// "not modified" not sent in the body
return ResponseEntity("not modified", HttpStatus.NOT_MODIFIED)
// using OK, it works
return ResponseEntity("success", HttpStatus.OK)
}
Problem
Whenever I create a ResponseEntity with a status code != 200, the body is not sent (empty body). Changing the HttpStatus to OK makes the message show again... I don't want to create error handlers for not modified, as this is definitely not an error.
Earlier there was earlier standard RFC2616 and now you can refer to newer RFC 7230-7237 and both of them mention 304 response should not include body.
Specifically older RFC2616 says it "must not include body" and newer RFC7230 "All 1xx (Informational), 204 (No Content), and 304 (Not Modified) responses do not include a message body"
In the end some servers might send or accept body with this status but it is not the case for spring ResponseEntity.

Spring post method "Required request body is missing"

#PostMapping(path="/login")
public ResponseEntity<User> loginUser(#RequestBody Map<String, String> userData) throws Exception {
return ResponseEntity.ok(userService.login(userData));
}
I have this method for the login in the UserController. The problem is when i try to make the post request for the login i get this error:
{
"timestamp": "2018-10-24T16:47:04.691+0000",
"status": 400,
"error": "Bad Request",
"message": "Required request body is missing: public org.springframework.http.ResponseEntity<org.scd.model.User> org.scd.controller.UserController.loginUser(java.util.Map<java.lang.String, java.lang.String>) throws java.lang.Exception",
"path": "/users/login"
}
You have to pass that as JSON in your body, if it's a POST request.
I had a similar issue, was getting this error in my Spring Boot service
HttpMessageNotReadableException: Required request body is missing:...
My issue was that, when I was making requests from Postman, the "Content-Length" header was unchecked, so service was not considering the request body.
This is happening because you are not passing a body to you server.
As can I see in your screenshot you are passing email and password as a ResquestParam.
To handle this values, you can do the following:
#PostMapping(path="/login")
public ResponseEntity<User> loginUser(#RequestParam("email") String email, #RequestParam("password") String password) {
//your imp
}
In order to accept an empty body you can use the required param in the RequestBody annotation:
#RequestBody(required = false)
But this will not solve your problem. Receiving as RequestParam will.
If you want to use RequestBody you should pass the email and password in the body.
You need to send data in Body as JSON
{ "email":"email#email.com", "password":"tuffCookie"}
If it's still not working, try adding additional information UTF-8 in Headers.
key : Content-Type
value : application/json; charset=utf-8
For my case, I must adding UTF-8 in Headers.
In my case it was poorly defined JSON that I sent to my REST service.
Attribute that was suppose to be an object, was in my case just string:
Changed from:
"client" = "",
to:
"client" = { ... },
In my case String did not add additional information about value in different format.

Spring + Angular: How to parse ResponseEntity in angular?

I'm using Spring Boot to create an API that needs to be consumed in Angular 4. Spring and Angular are on different ports.
The problem is that Spring's ResponseEntity raises an error in Angular.
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity getFlow(#PathVariable int id) {
Flow flow = flowService.findById(id);
return new ResponseEntity(flow, HttpStatus.FOUND);
}
Now, I can perfectly use Postman to test the API and it works.
But when I make a request from Angular, it returns an error:
Strangely, it returns an error alongside the requested object.
Now, the cause of the problem is that the Spring Boot application returns a ResponseEntity and not a normal object (like String), and Angular doesn't know how to interpret it. If the controller returns just a Flow object, it works.
How can it be solved using ResponseEntity? Or, how else can I send the object alongside the HTTP status code?
Also, in #RequestMapping put produces = "application/json", and in get request in angular, add http options :
const httpOptions = {
headers: new HttpHeaders({
'Accept': 'application/json',
'Content-Type': 'application/json'
})
};
So your get request looks like this:
this.http.get(url, httpOptions)
As per the document mentioned here
https://docs.angularjs.org/api/ng/service/$http
A response status code between 200 and 299 is considered a success status and will result in the success callback being called. Any response status code outside of that range is considered an error status and will result in the error callback being called. Also, status codes less than -1 are normalized to zero. -1 usually means the request was aborted, e.g. using a config.timeout. Note that if the response is a redirect, XMLHttpRequest will transparently follow it, meaning that the outcome (success or error) will be determined by the final response status code.
As you are sending an instance of ResponseEntity(HttpStatus.Found) whose Http status code is 302 which doesnt fall under the success range thats why error callback is called.
Try returning the content like this
return new ResponseEntity(flow, HttpStatus.OK);

How can I insert an interceptor with AngularJS to redirect to login page?

I have a login system that will hold the session for 2 hours. After 2 hours, when the client makes API calls, they get back an error:
{
"Success": false,
"Error": {
"ErrorCode": "002",
"ErrorMessage": "Session expired"
}
}
I need to then redirect the client to /login when that happens. The problem is that I get that error from individual API calls, so rather than change EVERY API call, is it possible to have a global interceptor?
Following the example for Interceptors found here, you can do something like the following in the response section of the interceptor:
response: function(resp){
// resp.data will contain your response object from the server
if(resp.data && !resp.data.Success){
// check for error code
if(resp.data.Error && resp.data.ErrorCode === '002'){
$location.path('/login');
return $q.reject(resp);
}
}
return resp || $q.when(resp);
},
This is assuming that your server is returning a 200 when you get this error message, not on a 401 or 403, but that's what it looks like you're doing. Hope that helps.

Resources