I'm kind of new with Spring. I have made following method:
public ResponseEntity<Borders> updateBorder(#Valid #RequestBody Borders borders) throws URISyntaxException {
log.debug("REST request to update Borders : {}", borders);
Boolean ok = deviceService.updateBorder(borders);
return new ResponseEntity(ok ? HttpStatus.CREATED : HttpStatus.BAD_REQUEST);
}
My method is now returning ResponseEntity with HttpStatus.CREATED or HttpStatus.BAD_REQUEST. When implementing frontEnd to my software I was wondering it would be very handy, if method could also return a String with HttpStatus. Like "Border is updated!" or "Border could not be updated because... pla pla".
What would be best way to return something more specific also to front-end?
you can return something like this , but your method return type should be ResponseEntity < String >, and if you want you pass headers as well.
http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html
return new ResponseEntity("your message", ok ? HttpStatus.CREATED : HttpStatus.BAD_REQUEST);
ResponseEntity allows to return three types of data:
HTTP Status code
Body
HTTP Header
For each combination of this data you will find a matching constructor. What you are looking for is the body which is an arbitrary object containing the data to be returned. Depending on the Accept header of your request the body will be returned in the requested data format, e.g. application/json. You can even return a simple String as body:
public ResponseEntity<String> updateBorder(#Valid #RequestBody Borders borders) throws URISyntaxException {
log.debug("REST request to update Borders : {}", borders);
Boolean ok = deviceService.updateBorder(borders);
return new ResponseEntity("Border could not be updated", ok ? HttpStatus.CREATED : HttpStatus.BAD_REQUEST);
}
Related
I have this endpoint method:
#RequestMapping(value = "/update")
public ResponseEntity(Response) update(
#ApiParam(required=true) #Valid #RequestBody Request request,
#RequestHeader #HeaderParam("key") String key
){
//execution of code here
return null;
}
I'm currently validating the Request Body using a custom JSON Deserializer.
Is there anyway that I could validate the Header Param "key" similar to the way I would validate a Request Body using a JSON Deserializer?
Maybe something like this that would execute before the "//execution of code here":
#Override
public String deserialize(String key){
if(!key.equals("ABC")){
throw new Exception("key is wrong");
}
return key;
}
Thank you!
Spring allows the definition of #ExceptionHandlers inside of #RestControllerAdvice.
I already defined a lot of other ExceptionHandlers in there for HTTP 400, 404, 405,... However the ExceptionHandler for HTTP 406 (NOT_ACCEPTABLE) does not seem to work. The handler is triggered, I checked that in the logs, but the result is not used.
My goal is it to return a HTTP 406 with a JSON body.
Variant 1
#ResponseStatus(HttpStatus.NOT_ACCEPTABLE)
#ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
public ErrorDTO requestMethodNotSupported(final HttpMediaTypeNotAcceptableException e) {
final ErrorDTO dto = new ErrorDTO(HttpStatus.NOT_ACCEPTABLE, "http.media_not_acceptable");
return dto;
}
Variant 2
#ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
public ResponseEntity<ErrorDTO> requestMethodNotSupported2(final HttpMediaTypeNotAcceptableException e) {
final ErrorDTO dto = new ErrorDTO(HttpStatus.NOT_ACCEPTABLE, "http.media_not_acceptable");
return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).contentType(MediaType.APPLICATION_JSON_UTF8).body(dto);
}
But I always get a HTML response similar to this from the Tomcat:
HTTP Status 406 -
type: Status report
message:
description: The resource identified by
this request is only capable of generating responses with
characteristics not acceptable according to the request "accept"
headers.
instead of
{
"errorCode": 406,
"errorMessage": "http.media_not_acceptable"
}
Request-Headers:
Accept: application/stuff-that-cannot-be-present
Actual-Response-Headers:
Content-Type: text/html
Expected-Response-Headers:
Content-Type: application/json
I know that I could simply "fix" the Accept-Header that is send by the client, however the server should always respond in JSON, if it does not know how to respond.
I use Spring 4.3.3.RELEASE and Jackson 2.8.4.
Finally I found a solution for this:
Instead of returning a serializable object just return the bytes directly.
private final ObjectMapper objectMapper = new ObjectMapper();
#ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
public ResponseEntity<byte[]> mediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException e) {
Object response = ...;
try {
return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(objectMapper.writeValueAsBytes(response));
} catch (Exception subException) {
// Should never happen!!!
subException.addSuppressed(e);
throw subException;
}
}
EDIT:
As an alternative you can create a custom HttpMessageConverter<ErrorResponse> for your ErrorResponse object.
Go to your or create a impl of WebMvcConfigurerAdapter#extendMessageConverters(converters)
Pick a HttpMessageConverter that is capable of creating your expected result/content type.
Wrap it in a way to fulfill the following conditions:
getSupportedMediaTypes() returns MediaType.ALL
canRead() returns false
canWrite()returns only true for your ErrorResponse
write() sets the forced CT and forward your expected content type to the wrapped converter.
Add your wrapper to the converters list.
If added as first element then it will always return your expected result (forced)
Requested: json , Returned: forced CT
Requested: xml , Returned: forced CT
Requested: image , Returned: forced CT
If added as last element then it will only return the result as your expected result, if there was no other matching converter (fallback)
Requested: json , Returned: json
Requested: xml , Returned: xml
Requested: image , Returned: forced CT
Building on #ST-DDT findings. If you are also extending ResponseEntityExceptionHandler then you cannot just add another method to handle HttpMediaTypeNotAcceptableException. However, there is an even simpler solution to the whole problem then:
#Override
public ResponseEntity<Object> handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
ResponseEntity<Object> response = super.handleHttpMediaTypeNotAcceptable(ex, headers, status, request);
// Workaround to return JSON response for 406
return ResponseEntity.status(NOT_ACCEPTABLE)
.contentType(APPLICATION_JSON)
.body(response.getBody());
}
Imagine a post REST end point such as:
#POST
#Path("/cbo/{param1}/{param2}")
public Response updateCbo() {
//do something
return Response.status(Response.Status.OK).build();
}
My question is: if everything goes well, is it better to return the OK response or is it the default anyway? I see the GET queries are usually not bothering returning the response, just the requested content, and web clients do fetch a 200 OK header.
Thanks.
You don't have to return a response at all, let's say if you have a POST (or any other function) type call that in the response request you want to return a String (or any object for that matter if you are using a serializer like Jackson)
You can just do:
#POST
#Path("/cbo/{param1}/{param2}")
public String updateCbo() {
//do something
return "My Response"
}
Jersey will automatically return 200 for this. If you set the function to void, Jersey will automatically return 204 (Success - no content).
If you want the call to fail you can just raise an exception.
When POSTing to create a new resource, the general accepted away is to send back a 201 Created status, with the Location header set the URI of the new resource. You can see an example of one to accomplish this, in this post.
If you are just updating a resource, which rather than POST, is generally done with PUT, then instead of a 201, the general way is to send a 204 No Content, on success. Example
#PUT
#Path("/cbo/{param1}/{param2}")
public Response updateCbo(Model updated,
#PathParam("param1") String param1,
#PathParam("param2") String param2) {
Model model = modelServive.lookup(param1, param2);
if (model == null)
return Response.notFound().build();
model = PropTranferUtils.transfer(model, updated);
modelService.update(model);
return Response.noContent().build();
}
I'm probably barking up the wrong tree with this, but I'm having some difficulty with Spring Integration and a http outbound-gateway.
I can configure it so that it makes a http POST and I get the response body as a simple String like this:
Spring Config
<int-http:outbound-gateway request-channel="hotelsServiceGateway.requestChannel"
reply-channel="hotelsServiceGateway.responseChannel"
url="http://api.ean.com/ean-services/rs/hotel/v3/list"
expected-response-type="java.lang.String"
http-method="POST"/>
Interface
public interface ExpediaHotelsService {
String getHotelsList(final Map<String, String> parameters);
}
And I can configure it so that I get a ResponseEntity back like this:
Spring Config
<int-http:outbound-gateway request-channel="hotelsServiceGateway.requestChannel"
reply-channel="hotelsServiceGateway.responseChannel"
url="http://api.ean.com/ean-services/rs/hotel/v3/list"
http-method="POST"/>
Interface
public interface ExpediaHotelsService {
ResponseEntity<String> getHotelsList(final Map<String, String> parameters);
}
Both versions of the code work. However, when returning a String I get the response body, but I don't get the http status and headers etc.
But when I use the ResponseEntity version I get the http status and headers, but I always get a null body via ResponseEntity#getBody
Is there anyway I can get both the body and the http status and headers?
(Ignoring the fact that the expedia hotels api returns JSON - at the moment I just want to get the raw body text)
Some further info which helps clarify the problem I am seeing. If I put a wire-tap on the response channel:
When I've configured it to return a simple String I get:
INFO: GenericMessage [payload={"HotelListResponse":{"EanWsError":{"itineraryId":-1,"handling":"RECOVERABLE","category":"AUTHENTICATION","exceptionConditionId":-1,"presentationMessage":"TravelNow.com cannot service this request.","verboseMessage":"Authentication failure. (cid=0; ipAddress=194.73.101.79)"},"customerSessionId":"2c9d7b43-3447-4b5e-ad87-54ce7a810041"}}, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#4d0f2471, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#4d0f2471, Server=EAN, Connection=keep-alive, id=5e3cb978-9730-856e-1583-4a0847b8dc73, Content-Length=337, contentType=application/json, http_statusCode=200, Date=1433403827000, Content-Type=application/x-www-form-urlencoded, timestamp=1433403827133}]
You can see the full response body in the payload, and notice the Content-Length being set to 337
Conversely, when I use a ResponseEntity<String> I get:
INFO: GenericMessage [payload=<200 OK,{Transaction-Id=[5f3894df-0a8e-11e5-a43a-ee6fbd565000], Content-Type=[application/json], Server=[EAN], Date=[Thu, 04 Jun 2015 07:50:30 GMT], Content-Length=[337], Connection=[keep-alive]}>, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#4d0f2471, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#4d0f2471, Server=EAN, Connection=keep-alive, id=9a598432-99c9-6a15-3451-bf9b1515491b, Content-Length=337, contentType=application/json, http_statusCode=200, Date=1433404230000, Content-Type=application/x-www-form-urlencoded, timestamp=1433404230465}]
The Content-Length is still set to 337, but there is no response body in the payload
Notice that you don't use any expected-response-type for the second case.
The RestTemplate works this way in case of no expected-response-type:
public ResponseEntityResponseExtractor(Type responseType) {
if (responseType != null && !Void.class.equals(responseType)) {
this.delegate = new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
}
else {
this.delegate = null;
}
}
#Override
public ResponseEntity<T> extractData(ClientHttpResponse response) throws IOException {
if (this.delegate != null) {
T body = this.delegate.extractData(response);
return new ResponseEntity<T>(body, response.getHeaders(), response.getStatusCode());
}
else {
return new ResponseEntity<T>(response.getHeaders(), response.getStatusCode());
}
}
As you see it really returns the ResponseEntity without body.
And Spring Integration can do nothing here on the matter...
From other side let's take a look if you really need a whole ResponseEntity as a reply back from the <int-http:outbound-gateway>.
Maybe headerMapper would be enough for you?.. For example http status is here already, even in your logs from the question:
Server=EAN, Connection=keep-alive, id=5e3cb978-9730-856e-1583-4a0847b8dc73, Content-Length=337, contentType=application/json, http_statusCode=200, Date=1433403827000, Content-Type=application/x-www-form-urlencoded,
I struggled with an extrange spring behavior using RestTemplate (org.springframework.web.client.RestTemplate) without success.
I use in my hole application below code and always receive an XML response, which I parse and evaluate its result.
String apiResponse = getRestTemplate().postForObject(url, body, String.class);
But can't figure out why a server response is in JSON format after executing:
String apiResponse = getRestTemplate().getForObject(url, String.class);
I've debugged at low level RestTemplate and the content type is XML, but have no idea why the result is in JSON.
When I access from a browser the response is also in XML, but in apiResponse I got JSON.
I tried many options after reading Spring documentation
http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/web/client/RestTemplate.html
Also tried to modify explicitly the headers but still can't figure it out.
I debugged RestTemplate class and noticed that this method is always setting application/json:
public void doWithRequest(ClientHttpRequest request) throws IOException {
if (responseType != null) {
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
if (messageConverter.canRead(responseType, null)) {
List<MediaType> supportedMediaTypes = messageConverter.getSupportedMediaTypes();
for (MediaType supportedMediaType : supportedMediaTypes) {
if (supportedMediaType.getCharSet() != null) {
supportedMediaType =
new MediaType(supportedMediaType.getType(), supportedMediaType.getSubtype());
}
allSupportedMediaTypes.add(supportedMediaType);
}
}
}
if (!allSupportedMediaTypes.isEmpty()) {
MediaType.sortBySpecificity(allSupportedMediaTypes);
if (logger.isDebugEnabled()) {
logger.debug("Setting request Accept header to " + allSupportedMediaTypes);
}
request.getHeaders().setAccept(allSupportedMediaTypes);
}
}
}
Could you give an idea?
I could solve my issue with RC.'s help. I'll post the answer to help other people.
The problem was that Accept header is automatically set to APPLICATION/JSON so I had to change the way to invoke the service in order to provide the Accept header I want.
I changed this:
String response = getRestTemplate().getForObject(url, String.class);
To this in order to make the application work:
// Set XML content type explicitly to force response in XML (If not spring gets response in JSON)
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_XML));
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> response = getRestTemplate().exchange(url, HttpMethod.GET, entity, String.class);
String responseBody = response.getBody();