#PostMapping()
public ResponseEntity<?> getCall(#Valid #RequestBody Request request) {
String requestJson = null;
try {
requestJson = ObjectMapperUtil.writeValueAsString(request);
log.info(requestJson) // will this introduce latency in my api.
return ResponseEntity.ok(service.getData(request));
} catch (Exception e) {
log.error(requestJson);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Request.builder().errors(INTERNAL_SERVER_ERROR)).build());
}
}
Just want to know that if we print the request body in json format after converting using ObjectMapper, what will be the impact on the latency on the api. Should we go ahead with #toString logging only. What's the good trade-off here.
If you're worried about latency, add an if statement around that code (most logging frameworks have such check methods):
String requestJson = null;
try {
if (log.isInfoEnabled()) {
requestJson = ObjectMapperUtil.writeValueAsString(request);
log.info(requestJson);
}
return ResponseEntity.ok(service.getData(request));
} catch (Exception e) {
if (requestJson != null) {
log.error(requestJson, e);
} else {
log.error("Failed to convert '{}' to JSON", request, e);
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Request.builder().errors(INTERNAL_SERVER_ERROR)).build());
}
Note that if the conversion of the object to JSON fails, requestJson will remain null, and there's no point in logging it. I also didn't add a check for log.isErrorEnabled() because a) that's almost always the case, and b) there's no logic involved in the error logging; any filtering will be done by the logger itself. Also note that I included the exception in the logging as well - you really want to know why the failure occurred.
There will still be latency, but only if needed. You can also consider moving the conversion into the catch (which needs its own try-catch). That way, the request JSON will only be logged if there's an error.
Related
I am calling a REST API and capturing response code and message in a table in case of any exception occurs.
catch (HttpClientErrorException ex) {
logger.error("HttpClientErrorException occures in calling API");
System.out.println(ex.getStatusCode());
System.out.println(ex.getMessage());
Output:
HttpClientErrorException occures in calling API
400 BAD_REQUEST
400 : [{"errors":[{"status":"400","code":"1002","title":"Missing parameter","detail":"Id element is mandatory."}]}]
It seems I can get the required value StatusCode and Message from ex.getMessage() but how can I extract these values?
You see an answer from server being called in message property, so format depends on server/endpoint you are calling.
In this particular case you can parse JSON and work with it like an object and access all props. Simpler (but uglier) way is to extract all you need by regex.
But again, you will not have universal solution, since it depends on the servers answer.
Parsing as JSON with Jackson library example:
public class ErrorMessage { // getter/setter are omitted
String title;
String detail;
String code;
String status;
}
catch (HttpClientErrorException ex) {
logger.error("HttpClientErrorException occures in calling API");
ObjectMapper objectMapper = new ObjectMapper();
ErrorMessage errorMessage = objectMapper.convertValue(ex.getMessage(), ErrorMessage.class);
System.out.println(errorMessage.getTitle); // <- you have an object now, so you can do whatever you want
}
I am new to spring and I am working on a fairly simple REST API.
I am getting a request without any data, and I need to send back some info. What would be the better way of doing so?
Entity
#RequestMapping(value = "/ping", consumes = "application/json", method = RequestMethod.GET)
public ResponseEntity<?> checkServerStatus() throws Exception {
Ping ping = new Ping();
ping.setStatus("alive");
ping.setVersion("v1");
try {
return ResponseEntity.ok().body(ping);
} catch (Exception e) {
throw new Exception("Service is not reachable at the moment", e);
}
}
OR
Response
#RequestMapping(value = "/ping2", consumes = "application/json", method = RequestMethod.GET)
public ResponseEntity<?> checkServerStatus2() throws Exception {
try {
return ResponseEntity.ok(new PingResponse("alive", "v1"));
} catch (Exception e) {
throw new Exception("Service is not reachable at the moment", e);
}
}
I figured no need to show PingResponse and Ping. They are typical get/set classes.
If both ways aren't ideal then maybe there is a better way of doing so?
The only difference is that you have your object in the try block or not. And that PingResponse could be an immutable object (no setters).
Entity/Response? they are both Objects.
So there is no difference worth taking notice in your provided code examples.
From your example it seems that, you just need to let the caller know that your service is up. Why are you creating an object? You can just notify the caller.
return new ResponseEntity<>("success", HttpStatus.OK);
you can just return the object from back end
#GetMapping("/ping")
public Ping checkServerStatus(){
Ping ping = new Ping();
ping.setStatus("alive");
ping.setVersion("v1");
return ping;
}
I'm writing a kind of a wrapper around my request handlers to make them stream HTTP response. What I've got now is
Handler response wrapper:
#Component
public class ResponseBodyEmitterProcessor {
public ResponseBodyEmitter process(Supplier<?> supplier) {
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
Executors.newSingleThreadExecutor()
.execute(() -> {
CompletableFuture<?> future = CompletableFuture.supplyAsync(supplier)
.thenAccept(result -> {
try {
emitter.send(result, MediaType.APPLICATION_JSON_UTF8);
emitter.complete();
} catch (IOException e) {
emitter.completeWithError(e);
}
});
while (!future.isDone()) {
try {
emitter.send("", MediaType.APPLICATION_JSON_UTF8);
Thread.sleep(500);
} catch (IOException | InterruptedException e) {
emitter.completeWithError(e);
}
}
});
return emitter;
}
}
Controller:
#RestController
#RequestMapping("/something")
public class MyController extends AbstractController {
#GetMapping(value = "/anything")
public ResponseEntity<ResponseBodyEmitter> getAnything() {
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(process(() -> {
//long operation
}));
}
What I'm doing is just send empty string every half a second to keep a request alive. It's required for some tool to not shut it down by timeout. The problem here that I don't see any Content-Type header in a response. There's nothing at all, despite I return ResponseEntity from my controller method as it's said in this thread:
https://github.com/spring-projects/spring-framework/issues/18518
Looks like only TEXT_HTML media type is acceptable for streaming. Isn't there a way to stream json at all? I even manually mapped my dtos to json string using objectMapper, but still no luck. Tried with APPLICATION_JSON and APPLICATION_STREAM_JSON - doesn't work. Tried in different browsers - the same result.
I also manually set Content-Type header for ResponseEntity in the controller. Now there's the header in a response, but I'm not sure, if my data is actually streamed. In Chrome I can only see the result of my operation, not intermediate chars that I'm sending (changed them to "a" for test).
I checked the timing of request processing for two options:
Without emitter (just usual controller handler)
With emitter
As I understand Waiting status means: "Waiting for the first byte to appear". Seems like with emitter the first byte appears much earlier - this looks like what I need. Can I consider it as a proof that my solution works?
Maybe there's another way to do it in Spring? What I need is just to notify the browser that a request is still being processed by sending some useless data to it until the actual operation is done - then return the result.
Any help would be really appreciated.
Looking at the source of ResponseBodyEmitter#send it seems that the specified MediaType should have been set in the AbstractHttpMessageConverter#addDefaultHeaders method but only when no other contentType header is already present.
protected void addDefaultHeaders(HttpHeaders headers, T t, MediaType contentType) throws IOException{
if (headers.getContentType() == null) {
MediaType contentTypeToUse = contentType;
// ...
if (contentTypeToUse != null) {
headers.setContentType(contentTypeToUse);
}
}
// ...
}
I would suggest to set a break point there and have a look why the header is not applied. Maybe the #RestController sets a default header.
As a workaround you could try the set the contentType header via an annotation in the MVC controller.
E.g.
#RequestMapping(value = "/something", produces = MediaType.APPLICATION_JSON_UTF8)
I have been trying to use this cordova plugin, which uses NanoHttpd to handle requests.
By default, Nanohttpd handles some of the HTTP methods, like GET, POST, CONNECT, PROPFIND, PATCH, etc.
I have been trying to figure out how to implement a custom handler so that nanohttpd can handled more HTTP methods like: NOTIFY and SUBSCRIBE
#Override
public Response serve(IHTTPSession session) {
Log.d(this.getClass().getName(), "New request is incoming!");
String requestUUID = UUID.randomUUID().toString();
PluginResult pluginResult = null;
try {
pluginResult = new PluginResult(
PluginResult.Status.OK, this.createJSONRequest(requestUUID, session));
} catch (JSONException e) {
e.printStackTrace();
}
pluginResult.setKeepCallback(true);
this.webserver.onRequestCallbackContext.sendPluginResult(pluginResult);
while (!this.webserver.responses.containsKey(requestUUID)) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
JSONObject responseObject = (JSONObject) this.webserver.responses.get(requestUUID);
Log.d(this.getClass().getName(), "responseObject: " + responseObject.toString());
Response response = null;
try {
response = newFixedLengthResponse(
Response.Status.lookup(responseObject.getInt("status")),
getContentType(responseObject),
responseObject.getString("body")
);
Iterator<?> keys = responseObject.getJSONObject("headers").keys();
while (keys.hasNext()) {
String key = (String) keys.next();
response.addHeader(
key,
responseObject.getJSONObject("headers").getString(key)
);
}
} catch (JSONException e) {
e.printStackTrace();
}
return response;
}
I added a simple notify Response to handle any incoming request, referring from here - https://stackoverflow.com/a/27645191/2096740
public Response notify(IHTTPSession session) {
StringBuilder text = new StringBuilder("<html><body>");
text.append("<h1>Url: ");
text.append(session.getUri());
text.append("</h1><br>");
Map<String, String> queryParams = session.getParms();
if (queryParams.size() > 0) {
for (Map.Entry<String, String> entry : queryParams.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
text.append("<p>Param '");
text.append(key);
text.append("' = ");
text.append(value);
text.append("</p>");
}
} else {
text.append("<p>no params in url</p><br>");
}
return newFixedLengthResponse(text.toString());
}
But this returnsBAD REQUEST: Syntax error. HTTP verb NOTIFY unhandled.
Documentation is not clear and there is not much info circulating on extending Nanohttpd behavior on SO or via web results.
What is the correct way to do this? How can I extend it ?
The check for Method is actually locked in an enum. It is hardcoded and there is no other method to expand.
The getMethod instance itself is a enum type of Method.
Since, I couldn't find any other solution, I therefore conclude it is not possible to do this stuff in Nanohttpd. All its versions in Maven dont support this.
The reason they have
Some built-in support for HEAD, POST and DELETE requests. You can
easily implement/customize any HTTP method, though.
mentioned in their feature list is because the original version had method as a String. It has changed since.
Feature list not been updated to reflect this change.
I am currently working with the Restlets framework, and I cannot find a way to manually set the HTTP response code within a service method. Consider the following snippet of code:
public class MyResource extends ServerResource {
#Post("json")
public Representation doSomething(Representation entity) throws IOException {
int status = 200;
try {
// do something which might throw an exception
}
catch (Exception e) {
// log the exception
// *** I would like to assign HTTP status 500 here ***
status = 500;
}
JSONObject responseJSON = new JSONObject();
responseJSON.put("result", "some data");
Representation rep = new JsonRepresentation(responseJSON.toJSONString());
return rep;
}
}
I have the ability to catch and log an exception, should one occur, but it is not clear how I can change the HTTP response code. As far as I know, returning from doSomething will automatically be handled by Restlets with an 200 HTTP response code.
I know how to assign the status code directly from a filter or servlet, but is it possible to do this within Restlets, without going down the servlet layer?
As far as I know, there is an object called ResponseEntity which you can use to operate with microservices and a request-response programming model, which allows you to specify the returning HTTP return code. However, you need entities for this, and I think this goes below your abstraction level of Servlets.
You can change them to some predefined values such as HTTP.INTERNAL_SERVER_ERROR and such, which translate to a value in the end, which you can Google in the end.
I hope this was of some help
EDIT:
Import the necessary resource for a ResponseEntity object. In STS, it is
import org.springframework.http.ReponseEntity;
import org.springframework.http.HttpStatus;
public class MyResource extends ServerResource {
#Post("json")
public ResponseEntity<Representation> doSomething(Representation entity) throws IOException {
int status = 200;
try {
// do something which might throw an exception
}
catch (Exception e) {
ResponseEntity<Representation> response = null;
response = new ResponseEntity<Representation>(HttpStatus.INTERNAL_SERVER_ERROR);
return response;
}
JSONObject responseJSON = new JSONObject();
responseJSON.put("result", "some data");
Representation rep = new JsonRepresentation(responseJSON.toJSONString());
return rep;
}
And sorry for the delay. I am new to Stack Overflow