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

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.

Related

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);

REST Assured returns response of "No HTTP resource was found that matches the request URI '****/api/Document/getBrowserData'"

I have implemented my REST Assured code like below
String tempUrl = "***/api/Document/getBrowserData";
Response rjson = given()
.param("dsId",1108)
.param("isNew", false)
.param("oToken","eed0361d314888a3f153534844869d9f")
.param("projId",837)
.param("tId",-1)
.param("type",2195)
.param("userId",104)
.post(tempUrl)
.then()
.extract()
.response();
System.out.println(rjson.asString());
and when I perform some action on the response:
System.out.println(rjson.asString()); // "Message":"No HTTP resource was found that matches
the request URI '
System.out.println(rjson.getHeaders()); // header value
System.out.println(rjson.getStatusCode()); // 404
But when I do the same thing with PostMan I am able to get a valid response.

Codeception Laravel sendAjaxGetRequest return empty string

Do you know why this return an empty string
public function tryToTest3(AcceptanceTester $I)
{
$I->wantTo('To see a 200 status code and a json response');
$I->haveHttpHeader('Content-Type', 'application/json');
$I->sendAjaxGetRequest('/users',array());
$I->seeResponseCodeIs('200');
$I->seeResponseIsJson();// until here green bar
dd($I->grabResponse()); // empty string
}
Acceptance Tests (5) ----------------------------------------------------------------------------------------------------------------------------
Modules: PhpBrowser, AcceptanceHelper, Db, REST
-------------------------------------------------------------------------------------------------------------------------------------------------
Trying to try to test (LoginCest::tryToTest)
Scenario:
PASSED
Trying to try to test2 (LoginCest::tryToTest2)
Scenario:
PASSED
Trying to To see a 200 status code and a json response (LoginCest::tryToTest3)
Scenario:
* I have http header "Content-Type","application/json"
* I send ajax get request "/users",
[Response] 200
[Page] http://localhost/laravel/phpunit/public/users
[Cookies] []
[Headers] {"Date":["Thu, 31 Jul 2014 08:49:58 GMT"],"Server":["Apache/2.2.22 (Ubuntu)"],"X-Powered-By":["PHP/5.4.6-1ubuntu1.4"],"Vary":["Accept-Encoding"],"Content-Length":["120"],"Content-Type":["text/html"]}
* I see response code is "200"
* I see response is json
* I grab response
string(0) ""
NB
in my app/bootstrap/start.php I've got
if ((gethostname() === 'homestead') && (isset($_SERVER['REMOTE_ADDR'])) && ($_SERVER['REMOTE_ADDR'] === '127.0.0.1'))
{
dd('homestead');
$env = $app->detectEnvironment(['codeception' => ['homestead']]);
}
else
{
$env = $app->detectEnvironment(['local' => ['homestead']]);
}
homestead is never printed
while this print the actual response and print homestead
public function tryToTest3(AcceptanceTester $I)
{
$I->wantTo('To see a 200 status code and a json response');
$I->haveHttpHeader('Content-Type', 'application/json');
$I->sendGET('/users',array());
$I->seeResponseCodeIs('200');
$I->seeResponseIsJson();
dd($I->grabResponse()); // the good response
}
Scenario:
PASSED
Trying to To see a 200 status code and a json response (LoginCest::tryToTest3)
Scenario:
* I have http header "Content-Type","application/json"
* I send get "/users",
[Request] GET http://localhost/laravel/phpunit/public//users
[Response] [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
[Headers] {"Date":["Thu, 31 Jul 2014 09:12:43 GMT"],"Server":["Apache/2.2.22 (Ubuntu)"],"X-Powered-By":["PHP/5.4.6-1ubuntu1.4"],"Cache-Control":["no-cache"],"Set-Cookie":["laravel_session=eyJpdiI6IjdxSVE3RzFEYUw4Q0VmbWNWTzlVVkE9PSIsInZhbHVlIjoiSXY5c2xZbVlmdTdSU1hjNTJuVU1cLzlkZXRwQmRGOEc2R3dscTBtY3JHRVwvR3V5TWM2K1BkVkpmdERDY3h0NkloU09CaUFNN0cwSk56TDBZbmhIOVVLZz09IiwibWFjIjoiNzE2YTlhZGEyMTIyZTdlODQ0ZjA0ZGEzZjE5NDg0MTBlMWNkY2IzMzRkN2NhNjEzOTg5ZTQ5NmRkNDU2OTE4MyJ9; expires=Thu, 31-Jul-2014 11:12:43 GMT; path=/; httponly"],"Transfer-Encoding":["chunked"],"Content-Type":["application/json"]}
[Status] 200
* I see response code is "200"
* I see response is json
* I grab response
string(52) "[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]"
try this instead of $I->grabResponse()
$response = $I->grabDataFromJsonResponse();
I have the same problem, but i think that in my case i have mistunderstood the testing tools.
In my case i was trying to test a JSON response provided by a controller that accepted AJAX POST requests.
I was trying to test that a successfull-style flash message is displayed to the user in a div, but as i wasn't able to test Javascript, i was looking for the JSON in the response. And then i reached your case when null is given as a JSON.
I tried with all the possible headers and combinations and then i reached this response in PHPtest.club and i think that it maybe can help you as it helped me.
It's about the difference between sendPOST and sendAjaxPostRequest, and then i realised that i was using in a wrong way the sendAjaxPostRequest.
http://phptest.club/t/what-is-the-difference-between-sendpost-and-sendajaxpostrequest/212
Basically:
sendAjaxXRequest is used to test an HTML response, so you should use, see, seeElement and those methods.
sendX is designed to work with JSON response and not with HTML, so you should use seeResponeIsJSON or all the other methods.
I hope to be helpful to you or someone else.
Greetings!
I'd like to say thanks to davert for such a meaningful response.

Change response code in AbstractHttpMessageConverter

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.

Status of ajax or post request

status - contains the status of the request like
("success", "notmodified", "error", "timeout", or "parsererror") /ajax or post
I know what is success,not modified,error meant here but I am unable to find out how to handle this errors. If the call back is success then update my div, if there is any error not modified, error, time out, parse error then let me alert a pop up some error occurred.
What would be the cause for each type of error? I mean the situations where not modified,timeout,error and parseerror occurs.
If it results success, then does it mean my post request has successfully worked?
My local-server xampp never results any error, the status is always success. I guess, since its limited to my system but when I put my website online there exists several issues like traffic on server.
So how do I find out, whether my post request to some sample.php page was successfully sent and else pop out an alert to user if something went wrong?
The error types are a little self-explanatory. They simply provide a string for you to easily handle the different errors.
error callback option is invoked, if the request fails. It receives the jqXHR, a string indicating the error type, and an exception object if applicable. Some built-in errors will provide a string as the exception object: "abort", "timeout", "No Transport".
Source: jQuery.Ajax documentation
Codes Explained:
Error: Any of the HTTP response codes, like the well-know 404 (not found) or other internal server errors.
Notmodified: Compares the cached version of the browser with the server's version. If they are the same, the server responds with a 304
Timeout: Ajax requests are time-limited, so errors can be caught and handled to provide a better user experience. Request timeouts are usually either left at their default or set as a global default using $.ajaxSetup() rather than being overridden for specific requests with the timeout option.
Parse Error: The jQuery data (JSON) cannot be parsed (usually due to syntax errors)
Handling these error codes:
Here is some example of handling the errors
$(function() {
$.ajaxSetup({
error: function(jqXHR, exception) {
if (jqXHR.status === 0) {
alert('Not connect.\n Verify Network.');
} else if (jqXHR.status == 404) {
alert('Requested page not found. [404]');
} else if (jqXHR.status == 500) {
alert('Internal Server Error [500].');
} else if (exception === 'parsererror') {
alert('Requested JSON parse failed.');
} else if (exception === 'timeout') {
alert('Time out error.');
} else if (exception === 'abort') {
alert('Ajax request aborted.');
} else {
alert('Uncaught Error.\n' + jqXHR.responseText);
}
}
});
});
Source: Blog Post - jQuery Error Handling
Success
Response code is between 200-299 or is 304 Not Modified.
Not Modified
Response code is 304. If you employ caching, the browser can tell the server which version it currently has, and the server compares this with its version and if there has been no change, it can send a 304 Not Modified response, to indicate to the client that their version is up to date. In jQuery ajax, a 304 Not Modified response will still fire the success handler.
Error
Response code is between 400-599. This could be for example 404 not found, 403 forbidden, 500 internal server error etc.
Parse Error
This is a jQuery internal, not actually a HTTP response. This will happen if jQuery is trying to parse JSON or XML that is not in the valid format or has syntax errors.
Timeout
Again, this isn't a HTTP response. The ajax request has a timeout which if is exceeded before the server responds, will abort the request.
If you control the server side, in your example a PHP script, and you never change the response code using header() then your ajax will always receive 200 OK responses unless there is an unhandled exception in the PHP which will trigger a 500 internal server error.
It is acceptable to always send 200 OK response codes. For example, if the server outputs a JSON object which contains its own success/error flag then any errors can be handled by looking at the flag.
As far as I know
not modified: Server sends a Not Modified(304) response status
timeout: the server has not responded within the time period specified by the timeout property
error: server response with a error status like 4xx or 5xx
parseerror: there was an client side error when processing server response like an invalid json format/xml format

Resources