I'm working on a node.js server using express and a android native app, using Retrofit 1.9.
For a login API that returns only a true/false answer to the client, should JSON still be used?
As I see it, the server has only to send a status code response:
if(isLegal) {
res.sendStatus(200);
dbConnector.updateUser(token);
}
else{
console.log('Token is not legal');
res.sendStatus(403);
}
But the Retrofit framework tries to convert the response to JSON, which makes me think I must send a JSON object with the answer, though it seems weird.
My retrofit restClient:
public class RestClient {
private static final String URL = SessionDetails.getInstance().serverAddress;
private retrofit.RestAdapter restAdapter;
private ServerAPI serverAPI;
public RestClient() {
restAdapter = new retrofit.RestAdapter.Builder()
.setEndpoint(URL)
.setLogLevel(retrofit.RestAdapter.LogLevel.FULL)
.build();
serverAPI = restAdapter.create(ServerAPI.class);
}
public ServerAPI getService() {
return serverAPI;
}
}
And usage:
restClient.getService().login(token.getToken(), token.getUserId(), new Callback<Void>() {
#Override
public void success(Void aVoid, Response response) {
Log.d("Chooser", "Successful login on server.");
}
#Override
public void failure(RetrofitError error) {
error.printStackTrace();
Log.d("Chooser", "Login failed on server.");
}
});
Using it as it is results with the following error:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
There are many topics on this issue but no certain answer about the correct (or better) method to use.
Any ideas about the best implementation in these cases?
Sending an empty body with your HTTP response is perfectly legal and some clients may care only about the response status but some clients may expect to get a response so sending a body never hurts and sometimes may be useful.
You can include a JSON response in addition to the HTTP response status:
// Express 4.x:
res.status(403).json({error: 'Token is not legal'});
// Express 3.x:
res.json(403, {error: 'Token is not legal'});
Such an error message can be very useful for the client development. You can get 403 for many reasons, illegal token, expired token, a legal not expired token but for the wrong user that doesn't have some privilege - adding a specific error message in addition to the HTTP response code can tell the client what exactly went wrong and allows the client-side code to show a better error message to the user.
Also, note that true and false are also valid JSON.
Related
Could you please give me an advice, what is the proper way to return HttpStatus.BAD_REQUEST from webflux handler?
Here's the code, it seems to work properly, exception message returns as needed, but the response status is 200, while needed to be 400. Please note, that returning media type is TEXT_EVENT_STREAM -
#Component
public class ResolveHandler {
public Mono<ServerResponse> resolve(ServerRequest request) {
return ServerResponse.ok().contentType(MediaType.TEXT_EVENT_STREAM)
.body(
new ResultsFluxer(
Integer.valueOf(request.queryParam("numberOfIterations").orElse("0")),
request.queryParam("function1").orElse(""),
request.queryParam("function2").orElse(""),
orderingType
)
.getResult()
.onErrorResume(e -> Flux.just(e.getMessage()))
, Flux.class);
}
}
I'm beggining to play with Angular2. I have developed a basic RESTful API using Jersey. I tested it and it works fine (with browser and SOAP UI). This is the code:
#GET
#Produces(MediaType.APPLICATION_JSON)
public TwoWordsMessage getMessage() {
TwoWordsMessage message = new TwoWordsMessage();
message.setFirstWord("hello");
message.setSecondWord("world");
return message;
}
I'm tryng to call the service from an Angular2 app:
this.http.request(this.url).subscribe((res: Response) => {
this.message = res.json();
});
I can see (debbuging) that "getMessage" method is called and it returns the TwoWordsMessage object but the Angular2 application never gets it. The same code with the url http://jsonplaceholder.typicode.com/posts/1 works fine.
What I'm doing wrong?
Thanks!
Are you calling the http request inside a component or a service? Does a function or method fire off the http request?
Also, can you see if there are errors coming back from the response? The subscribe method can take three functions as parameters, first one being on success, second on error, third on completion. If there's an error in the AJAX call (400s, 500s, etc), your code would never be able to handle it. Try this:
this.http.request(this.url).subscribe((res: Response) => {
this.message = res.json();
}, (error) => {
console.warn(error)
});
and see what is spit out. To further debug, you can even use the .do() method on the Observable:
this.http.request(this.url)
.do((res: Response) => console.log(res)) // or other stuff
.subscribe((res: Response) => {
this.message = res.json();
});
The .do() method will execute an arbitrary function with the response without actually affecting it.
If not, you could also try changing the http call to http.get(). I don't think that's the problem, but the Angular docs do not state what method is defaulted to with http.request() (although I would be almost certain it's a GET).
I finally got it working. It's a CORS problem.
The console showed the error:
"No 'Access-Control-Allow-Origin' header is present on the requested
resource"
I changed the resource method like this:
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getMessage() {
TwoWordsMessage message = new TwoWordsMessage();
message.setFirstWord("hello");
message.setSecondWord("world");
return Response.status(200).header("Access-Control-Allow-Origin", "*").entity(message).build();
}
You can find useful information here:
http://www.codingpedia.org/ama/how-to-add-cors-support-on-the-server-side-in-java-with-jersey/
Server Side
public class MyServices : Service
{
public object Get(Hello request)
{
throw new InvalidOperationException("test error message");
//return new HelloResponse { Result = "Hello, {0}!".Fmt(request.Name) };
}
}
Client Side
try
{
var client = new JsonServiceClient("http://localhost:28586/");
var response = client.Get<HelloResponse>(new Hello { Name = "DHJ" });
}
catch (WebServiceException ex)
{
// ex.ErrorCode = "InvalidOperationException" // No Problem.
// ex.ErrorMessage = null // always null. Why?
}
And i saw the docs of ServiceStack like below:
Throwing C# Exceptions
In most cases you won’t need to be concerned with ServiceStack’s error handling since it provides native support for the normal use-case of throwing C# Exceptions, e.g.:
public object Post(User request)
{
if (string.IsNullOrEmpty(request.Name))
throw new ArgumentNullException("Name");
}
Default Mapping of C# Exceptions to HTTP Errors
By Default C# Exceptions:
Inheriting from ArgumentException are returned with a HTTP StatusCode of 400 BadRequest
NotImplementedException or NotSupportedException is returned as a 405 MethodNotAllowed
AuthenticationException is returned as 401 Unauthorized
UnauthorizedAccessException is returned as 403 Forbidden
OptimisticConcurrencyException is returned as 409 Conflict
Other normal C# Exceptions are returned as 500 InternalServerError
This list can be extended with user-defined mappings on Config.MapExceptionToStatusCode.
Your HelloResponse class needs a ResponseStatus property, from the Error Handling docs:
Error Response Types
The Error Response that gets returned when an Exception is thrown varies on whether a conventionally-named {RequestDto}Response DTO exists or not.
If it exists:
The {RequestDto}Response is returned, regardless of the service method's response type. If the {RequestDto}Response DTO has a ResponseStatus property, it is populated otherwise no ResponseStatus will be returned. (If you have decorated the {ResponseDto}Response class and properties with [DataContract]/[DataMember] attributes, then ResponseStatus also needs to be decorated, to get populated).
Otherwise, if it doesn't:
A generic ErrorResponse gets returned with a populated ResponseStatus property.
I created a series of REST services in Java using Restlets. The majority of these services use JSON, and I have no problem accessing them using SOAP UI via a GET request. However, when I try to access POST based services using SOAP UI, the Representation entity parameter is always null. I have searched Stack Overflow as well as the web, but could find nothing which I either haven't already done, or which addresses my problem.
Here is the code for a POST resource which always seems to receive a null entity:
public class CreateAccountResource extends ServerResource {
#Post("json")
public Representation createAccount(Representation entity) throws IOException {
String message = null;
boolean result = true;
try {
String post = entity.getText();
Object obj = new JSONParser().parse(post);
JSONObject jsonObject = (JSONObject) obj;
String username = (String) jsonObject.get("username");
String password = (String) jsonObject.get("password");
String email = (String) jsonObject.get("email");
// more code
}
catch (Exception e) {
// handle exception here
}
}
}
And here is a screen shot from my SOAP UI showing the configuration I used when sending the request:
In case you are wondering, I am using IntelliJ in debug mode to inspect the value of the entity, and the project uses Maven.
I never use Restlet however I think that since you specify #Post("json") annotation for your createAccount method; the method is waiting for a json in the POST body instead of passing the values as a query parameters.
So probably you must change your actual POST with the query parameters to a POST call to your URL http://localhost:8080/MyApp/service/createAccount passing the parameters in the body as json:
{
"username" : "tim",
"password" : "password",
"email" : "tim#me.com"
}
In SOAPUI could be something like:
Hope it helps,
I have a jersey client that I am trying to unmarshall a response entity with. The problem is the remote web service sends back application/octet-stream as the content type so Jersey does not know how to unmarshall it (I have similar errors with text/html coming back for XML and such). I cannot change the web service.
What I want to do is override the content-type and change it to application/json so jersey will know which marshaller to use.
I cannot register application/octet-stream with the json marshaller as for a given content type I actually might be getting back all kinds of oddities.
As laz pointed out, ClientFilter is the way to go:
client.addFilter(new ClientFilter() {
#Override
public ClientResponse handle(ClientRequest request) throws ClientHandlerException {
request.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, "application/json");
return getNext().handle(request);
}
});
I'm not well-versed in the Jersey client API, but can you use a ClientFilter to do this? Perhaps you could add a property to the request via ClientRequest.getProperties().put(String, Object) that tells the ClientFilter what Content-Type to override the response with. If the ClientFilter finds the override property, it uses it, otherwise it does not alter the response. I'm not sure if the ClientFilter is invoked prior to any unmarshalling though. Hopefully it is!
Edit (Have you tried something like this):
public class ContentTypeClientFilter implements ClientFilter {
#Override
public ClientResponse handle(ClientRequest request) throws ClientHandlerException {
final ClientResponse response = getNext().handle(request);
// check for overridden ContentType set by other code
final String overriddenContentType = request.getProperties().get("overridden.content.type");
if (overriddenContentType != null) {
response.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, overriddenContentType);
}
return response;
}
}
Under Java 8 and Jersey 2 you can do it with a lambda:
client.register((ClientResponseFilter) (requestContext, responseContext) ->
responseContext.getHeaders().putSingle("Content-Type", "application/json"));