Please understand my English is bad.
I use Spring MVC and I replaced this source
#RequestMapping("/ajax/add_server")
public void addServer(HttpServletRequest request, HttpServletResponse response) throws Exception {
String host = request.getParameter("host");
String port = request.getParameter("port");
String state = request.getParameter("state");
serverService.addServer(host, port, state);
}
to
#RequestMapping("/ajax/add_server")
public void addServer(
#RequestParam("host") String host,
#RequestParam("port") String port,
#RequestParam("state") String state) throws Exception {
serverService.addServer(host, port, state);
}
addServer() Method is called by AJAX.
My ajax loading image is gone in case of using req.getParameter(), but the images isn't gone when i use #RequestParam..
I guess Ajax XMLRequest Object doesn't get any success MSG.
but I don't know why and is this normal?
Additional Discovery!!
#RequestMapping("/ajax/add_server")
public void addServer(
#RequestParam("host") String host,
#RequestParam("port") String port,
#RequestParam("state") String state,
HttpServletResponse response) throws Exception {
serverService.addServer(host, port, state);
}
I added response to Parameter then the image's gone. I don't know Why.
I leave this for my Reference.
The Controller method with void return type uses URI-BASED VIEW.
For example, this following source uses ajax/add_server.jsp as view.
#RequestMapping("/ajax/add_server")
public void addServer(
#RequestParam("host") String host,
#RequestParam("port") String port,
#RequestParam("state") String state) throws Exception {
serverService.addServer(host, port, state);
}
The default for #RequestParam is that the value is required, and it will throw an exception if nothing is there. On the other hand with getParameter it would just be passing a null down into the next method. So if there are some times that you're not supplying all three parameters, then it would not work properly with the change.
Edit:
Regarding the additional information you posted:
There is some special handling inside AnnotationMethodHandlerAdapter that changes the routing when a void method takes in HttpServletResponse as a parameter. Basically it assumes that since you took in the response, you're handling any output that needs to be generated and it disables default view resolution. This would cause the server to simply reply 200 with an empty response body.
In the case where you have a void method, but weren't reading in the HttpResponse object, it was reverting to default view resolution. This probably led to an error being generated since I doubt you have a .jsp file named add_server anywhere! :) The request sill "works" since your service call is done and committed before the method returns and Spring attempts view resolution. The ajax call ends up going to the error handler instead of the success handler though.
tl;dr sometimes annotated controller "magic" is a little bit too magical :)
Related
I am trying to determine why a webserver response that initially throws an exception in processing; then returns a 200 OK client side. The details are as follows:
a request is sent to the webserver from the web application and if an error occurs an exception is caught and the relevant code &/or message is returned as follows:
public void dispatchRequest(HttpServletRequest req, HttpServletResponse
res)
{
if (method.equalsIgnoreCase("get")) {
doGet(req, res);
} else {
res.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
return;
}
}
void doGet(HttpServletRequest request, HttpServletResponse response)
throws
IOException,
HTTPServerException {
handleGetClient(request, response);
}
#SuppressWarnings("unchecked")
private void handleGetClient(HttpServletRequest request,
HttpServletResponse
response)
throws IOException, HTTPServerException {
...
} catch (IOException e) {
logger("I/O Error during playback with parameters (additional
parameters logged) {0}: {1}",traceParams,e.toString());
logger(Level.FINER, "I/O Error during playback with parameters {0}:
{1}", parameters, e.getMessage());
logger(Level.FINER, "I/O Error during playback with parameters {0}:
{1}", parameters, e);
sendError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
...
}
protected void sendError(HttpServletResponse response, int errCode) {
response.setContentType("text/plain");
try {
response.sendError(errCode,"ERROR");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
The handleGetClient method handles the process and in the event of an error throws exceptions which are caught. The method then uses the sendError method to set the error code returned and when log debugging I could see that this was set to in this specific error (500). But once the call returns to the dispatchRequest method the httpservletResponse status is actually (200). I cannot see where this happening and why. Initially I thought I could just change the method to int to return the error code but I am limited to the changes I can make on this code.
Any ideas?
You could try one of the following:
response.resetBuffer ();
response.setStatus (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.flushBuffer ();
or if you have an error page matching the 500 code in your web.xml:
response.reset ();
response.setError (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
You could also consider using an exception mapper, for any generic error, so instead of playing yourself with the error code, you could just throw an exception which would take care of the status code return.
i.e: throw new InternalServerException ("Your message");
Possibly a return statement might be in the catch block that eventually ends up in normal functioning of sending response. I am only stating a possibility. More you can do is check what is being returned after the request is processed successfully from server (either after exception handling or normal functioning).
Either your pasted code is missing a piece or the original code does not set the actual error on the response object. Somewhere is the sendError method there should be a line like:
response.setStatus(errCode);
Once you have started streaming response to the client, you can no longer change the response code, since it is returned in the first line of response and can never be changed afterwards. Same thing with response headers - once you've started streaming body content, you can't change them.
Now, servlet containers use buffering. This means that you can write some data to response and then change your mind (as #MehdiB. has indicated). But once you have overflown that buffer, the data is written to client (first status code, then headers, then body) and you can no longer change status at this point.
The probable solution here is to avoid writing body until you are sure there are no errors. If your body is long, but you can figure out its full lenght, you can add Content-Length header - in this case client will know if you don't deliver the response in whole without relying on status codes.
I remember in my practice adding servlet filters which will intercept HttpServletResponse to make sure that servlets behave nicely with regards to this constraint.
As stated in title, what does it mean that HttpServletResponse is committed?
I have some request interceptor, extending HandlerInterceptorAdapter, that overrides postHandle method. Post handle method takes parameter final HttpServletResponse response. In method body there is an if statement checking if response.isCommitted(), what exactly does that check?
private static final String voidResponse = "null";
#Override
public void postHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler,
final ModelAndView modelAndView) throws IOException {
if (!response.isCommitted()) {
if (DefaultServletHttpRequestHandler.class == handler.getClass()) {
return;
}
response.setStatus(200);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
try (final Writer writer = response.getWriter()) {
writer.write(voidResponse);
}
log.info("void method called, respond with 200 and null");
response.flushBuffer();
}
}
ServlerResponse.isCommited() checks if the response has been already committed to the client or not (Means the servlet output stream has been opened to writing the response content).
The committed response holds the HTTP Status and Headers and you can't modify it.
It's important also to note that in this case the response content has NOT been written yet, as the headers and status are committed before the content itself.
In such examples as yours the check is required to prevent situations when the response has already been commited but someone is trying to modify it, in which case you will get an IllegalStateException stating that response has already been committed.
UPDATE: I see that you are using Spring controllers. The story differs a bit here.
Case 1: If you are using #ResponseBody in your controller method or returning ResponseEntity Spring writes to and commits the response before the postHandle() is called, which makes it impossible to change the response later. That said in this case response.isCommited() statement will always return true and you are not able to modify the response.
Case 2: If you don't have the above mentioned annotation and don't return ResponseEntity or controller returns NULL the postHandle() method of interceptor is called after the controller method has been processed, but the response has not been committed yet. This means you can modify the response as you want (e.g. return 200 OK).
It is response committed not request. It means response already sent to output stream/client.
I can't retrieve the values from a request.
Servlet:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String location_id = request.getReader().readLine(); // <---- null
location_id = request.getParameter("location_id"); // <--- null
PrintWriter out = response.getWriter();
out.write(this.get_events_json(location_id));
}
On the client-side:
$.get("EventServe", {location_id : location_id}).done(function() {
var events = JSON.parse(responseText);
outer_this.events = events.map(function(event){
var event = new Event(event.address, event.name, event.event_start, event.event_end)
return event;
});
outer_this.events.map(function(event){outer_this.insert_event(event)});
});
I've also tried to pass it in directly without jQuery, using only literals.
When you use $.get('EventServe', {location_id: location_id}, ...) to make a HTTP GET request, you are passing the value of location_id as a query string parameter to the specified URL. Essentially you are requesting: EventServe?location_id=4, where 4 would be the value of location_id.
On the server side, you can access the query string parameters via getParameter(String name):
public void doGet(...) {
String locationId = request.getParameter("location_id");
}
A few extra notes:
You should remove your call to request.getReader().readLine(). (Also, doesn't readLine(byte[] b, int off, int len) require arguments?)
As a followup to the previous point, manually reading from the request via a BufferedReader, InputStream, or anything similar is a bad (used loosely) habit to get into, as doing so may interfere with getParameter(String name) in some cases:
If the parameter data was sent in the request body, such as occurs with an HTTP POST request, then reading the body directly via getInputStream() or getReader() can interfere with the execution of this method.
Source for the above quote.
Your client side code has a error where you define the function to run when the Ajax call is completed. The function should take events as an argument, as jQuery will automagically parse a JSON response:
.done(function (events) {
// Do things with the events
});
(Puts on pedant hat.) Your method name get_events_json does not follow Java conventions. Consider renaming it to getEventsJson or something to that effect.
Servlet Request Doc
Just look at getAttribute(String name) or getParameter(String name).
Edit: getParameter(String) is for POST request, but you perform a GET request. Use getAttribute(String) instead
My action looks like:
public String add() {
return "/WEB-INF/views/add.jsp";
}
In this case I need access to a posted form field parameter. Once I receive the posted parameter value, I will save it to the db and then return an http response code of 200 if there are no errors. If an error has occured, I will return HTTP 500 response code.
How can I set the http response code in the return (instead of the view unless I can do both?).
Assuming this is a #RequestMapping-annoted controller, then just declare the form field as a parameter:
public String add(String myformFieldName) {
To return an explicit status code on the response, then declare the response and set it:
public String add(String myformFieldName, HttpServletResponse httpResponse) {
httpResponse.setStatus(...);
}
Forgive me, but I may not be familiar with all the lingo necessary to ask this question properly.
I'm working on a fairly simple REST web service in Java using the org.apache.cxf.jaxrs.ext implementation of jax-rs. The method header is like this:
#GET
#Path("json/{fullAlias}")
#Produces({"application/json"})
public String json(#PathParam("fullAlias") String fullAlias, #Context MessageContext req)
where MessageContext is org.apache.cxf.jaxrs.ext.MessageContext.
There are two things I'm trying to accomplish that I can't seem to figure out:
Change the content-type if certain conditions are met (e.g. for an error)
Change the status code of the response
I've tried using changing the response by accessing it through the MessageContext:
HttpServletResponse response = req.getHttpServletResponse();
response.setContentType("text/plain")
response.setStatus("HttpServletResponse.SC_BAD_REQUEST);
But these changes have no bearing on the response sent; with or without the #Produces annotation, setting the content type inside the method doesn't affect the actual content type (With the annotation, it of course returns "application/json", without it defaults to "text/html").
I am returning a simple String as the body. I've entertained trying to return a javax.ws.rs.core.Response object to do what I want, but I don't know much about it.
How would I change the content type and/or the status codes from inside this method?
One approach is to throw a WebApplicationException, as described by Pace, which will work if you are looking to specifically handle an error condition. If you are looking to be able to change your content at any time for any reason, then you will want to take a look at returning a Response as the result of your service method rather than a String. Returning a Response gives you the greatest amount of control over how your service responds to the client request (it does require more code than returning a simple string).
Here is an example of how you would can make use of the Response object:
#GET
#Path("json/{fullAlias}")
public Response json(#PathParam("fullAlias") String fullAlias, #Context MessageContext req) {
...
if (success) {
ResponseBuilder rBuild = Response.ok(responseData, MediaType.APPLICATION_JSON);
return rBuild.build();
}
else {
ResponseBuilder rBuild = Response.status(Response.Status.BAD_REQUEST);
return rBuild.type(MediaType.TEXT_PLAIN)
.entity("error message")
.build();
}
}
I'm not sure if it's the best approach but I've done the following to solve your question #1.
public WebApplicationException createStatusException(String statusMessage) {
ResponseBuilder rb = Response.noContent();
rb = rb.type(MediaType.TEXT_PLAIN);
rb = rb.status(Status.BAD_REQUEST);
rb = rb.entity(statusMessage);
return new WebApplicationException(rb.build());
}
EDIT: I then threw the resulting WebApplicationException.
You can write your own Response Filter to change the content-type header.
#Provider
public class MimeAddingFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
responseContext.getHeaders().add("Content-Type", "image/png");
}
}
This filter will add the "image/png" content-type header. You can also change or remove headers in JAX-RS response filters.