In my GAE endpoint, in my user register API method, I check whether a user already exists with the given username. If a user already exists, I need to send an error to the endpoint client.
Currently I throw an exception like below from endpoint.
User user = ofy().load().key(Key.create(User.class, username)).now();
if (user != null) {
throw new BadRequestException("Username already exists");
}
Then in endpoint client, I catch the exception like below.
try {
gaeEndpoint.registerUser(mEmail, mPassword).execute();
} catch (HttpResponseException e) {
mErrorMessage = e.getContent();
} catch (IOException e) {
mErrorMessage = "Failed to communicate with server. Please check your internet connection.";
}
When endpoint throws a BadRequestException, client gets HttpResponseException e and e.getContent() contains a json string including the error message string sent from endpoint. I need to parse json to get the actual error message sent from server.
Even though this works, I feel that there should be a better way to send an error message to the client from the endpoint. So, does anyone know a better (or a recommended) way of doing this?
I think you're doing everything right.
HTTP is designed to send a response with 4xx code and meaningful content to a user. It can be HTML for a browser, JSON for an api client or anything that can provide some context to the client.
If you're worried about using exceptions, that's fine too. Of course, you can prepare a response with a manually set status code, but that doesn't change anything, it's just more code. It's also less likely that you'll create a bug when you're using exceptions in this case.
Related
I see a weird issue. I have two webapps. One for the rest webservice that we exposed using Jersey. Another has the JSF Front End implementation which calls above webservice to fetch the details. We are using Tomcat as a container.
The issue that i am facing is when i call a rest webservice, i throw WebApplicationException like this
catch (CustomExceptions e) {
ResponseBuilder response = new ResponseBuilderImpl();
response.status(500);
response.entity(e.getStackTrace());
throw new WebApplicationException(e, response.build());
}
And on the other hand on FE webapps, i do the following:
try {
r.get(MyClass.class);
return "SUCCESS";
} catch (WebApplicationException wae) {
return "FAILURE";
} catch (UniformInterfaceException wae) {
return "FAILURE";
}
Here in the catch block i was expecting the WebApplicationException but its throwing UniformInterfaceException which is weird. Also if it throws UniformInterfaceException, It does not maintain the stacktrace. Even the response that i passed in from rest call is lost. Can somebody help me how can i get the original stacktrace from the rest call?
Thanks in advance.
As specified in both the user guide and the api documentation, a UniformInterfaceException is the expected result when you attempt to retrieve a specific entity by type but receive a non-200-series response. The WebApplicationException is a convenience exception for your use on the server side to embed custom responses in an exception.
Nothing is "lost". Keep in mind you're making an HTTP request here. If you want to inspect the status code and response yourself, use
ClientResponse response = r.get(ClientResponse.class);
if (response.getStatus() == <expected status code>) [
response.getEntity(MyClass.class);
// do happy path stuff
} else {
// bad stuff here
}
Alternately, assume everything will go well, and only check the response on exceptions:
try {
r.get(MyClass.class);
} catch (UniformInterfaceException e) {
ClientResponse response = e.getResponse();
// do whatever with the raw response
}
I had same issue before. I think it's because Jersey WebResource.get method throws only UniformInterfaceException which is not in the inheritance hierarchy of WebApplicationExcpetion. What I did is to abandon Jersey client side api but use plain ApacheHttpClient. Will be able to get the right stacktrace in the httpResponse contents.
Hope this helps.
Thanks, Ryan. I tried your solution. Very nice.
I am having a problem where when I put the querying URL into a web browser on my Mac the API send the data back as I am expecting but when I use the exact same URL within my app on android I get the following error: {"SC":"400","VSM":"Incorrect parameter passed on URL","VERNUM":"1.2"}. It is just a simple test to see if the database in my app is up to date. Is this an API problem or something i'm doing wrong in the app? I have no control over the API functionality so if it's a problem with that then I will have to get somebody else to sort it out. the code I am using is below.
String URL = "http://Url.com/?p=api&request=check_for_changes";
Log.v("Value", URL);
JSONObject jsonObjSend = new JSONObject();
try
{
jsonObjSend.put("dataset_version_number", 1.1);
// Output the JSON object we're sending to Logcat:
Log.v("Response", jsonObjSend.toString(2));
}
catch (JSONException e)
{
Log.i("Response", "Error happened here");
}
try
{
jsonObjRecv = HttpClient.SendHttpPost(URL, jsonObjSend);
Log.v("SC", jsonObjRecv.get("SC").toString());
}
catch (JSONException e){
}
The HttpClient object is from a class I have used many times before to just read the response from the server so i'm not going to post the code on here as it's not relevent and quite long.
The Answer come in the form of me being an idiot and trying to use a POST Request on a URL which doesnt accept JSON and only needs a GET Request. So I only need to query the URL and get the response rather than try and send the data via a POSTusing JSON.
I have csv files stored on my server. If I enter the right key (which is a part of URL) I get what I want, but if the entered key was wrong my app crashes. I want to be able to catch the error.
String url="http://mysite.com/template";
url=url+et.getText().toString().toLowerCase()+".csv";
csv.setURL(url);
if(csv.checkURL()){
enterToDB();
}
else{
tv.setText("Wrong key");
}
and my CSVReader looks like:
public void setURL(String file){
try {
URL url = new URL(file);
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
success=true;
in.close();
} catch (MalformedURLException e) { success=false;} catch (IOException e) { success=false; }
}
public boolean checkURL(){
return success;
}
}
Without the complete minimal code necessary to replicate the problem, and without information about what the specific error is (like a stack trace), I'm just guessing that the setURL/checkURL routines don't exactly do what you want. They appear to assume that openStream will throw an exception if the key in the URL is wrong, but that's not the case. Even if the path or key in the URL is wrong, the HTTP server will still provide a response. The response might not be 200 OK and the response body might not include what you're looking for, but it'll still give a response, and the open stream can be used to read the response.
So, if I understand correctly, you actually want to inspect the contents of the response (including probably the HTTP status code), before deciding whether the "success" is true or false.
Thane posted some code I gave him over in How should I handle server timeouts and error code responses to an http post in Android App?. I recommend reviewing it and seeing if it provides the structure you need to handle successful and failed responses accordingly.
Making sense?
I used to just use Tomcat and JSP pages which I can execute query, then assign query result into the array or object then pass that data onto client side via response.
request.setAttribute("errorMessage", "this is error!!");
request.getRequestDispatcher("report.jsp").forward(request, response);
In client jsp code, I could do something like:
${errorMessage}
Then the "this is error!!" message would show up.
I want to do the same thing with REST JAX-RS GlassFish v3.
#Path("schedule/test")
#POST
#Consumes("application/x-www-form-urlencoded")
#Produces("application/vnd.ms-excel")
public Object tmpTest(String content) {
try {
//just my method to execute query and get result
Vector out = (Vector)QueryManager.executeQuery;
//if query result is empty, I want user to redirect to report.jsp page
if(out.isEmpty()) {
request.setAttribute("errorMessage", "This is error!!");
request.getRequestDispatcher("report.jsp").forward(request, response);
return null;
}
....continue code......
}
This results in mysterious exception I've never seen.
java.lang.ClassCastException: $Proxy109 cannot be cast to org.apache.catalina.core.ApplicationHttpRequest
at org.apache.catalina.core.ApplicationHttpRequest.getRequestFacade(ApplicationHttpRequest.java:1001)
at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:472)
at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:379)
at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:336)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:314)
So how can I redirect a user to report.jsp and also pass message like "This is error" ?
The client jsp expects the error msg variable to have a value:
<b>${errorMessage}</b>
That's not RESTful. You need to throw a WebApplicationException with a specific status code so that the client understands what exactly went wrong. E.g. when it's actually the server's mistake:
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
Or when it was after all client's mistake:
throw new WebApplicationException(Response.Status.BAD_REQUEST);
See also HTTP status code definitions for an overview.
The ClassCastException which you're facing is by the way occurring because the dispatched request is actually not an instance of the servletcontainer-provided implementation (in this particular case, the one of Tomcat or a Tomcat-fork). After all, you shouldn't be doing it this way. You're developing a REST webservice, not a JSP/Servlet website. It are two distinct worlds.
As mentioned before, you should try WebApplicationException.
I believe you this would give you your desired answer:
Try this:
if(out.isEmpty()) {
java.net.URI location = new java.net.URI("report.jsp");
throw new WebApplicationException(Response.seeOther(location).build());
}
I want to be able to verify that the connection is still alive (client not dead) when sending a response from a Jersey (jax-rs) resource. The reason is that if I just return the response object and the client is dead, the response is lost without knowing that the client did not receive it.
httpContext.getResponse().setResponse(myResponse)
try {
httpContext.getResponse().getOutputStream().write(new byte[]{})
} catch (Exception e) {
// writing of headers failed.
}