Handling JSON parse errors of third party services in play applications - scala

I'm wondering what's an acceptable approach to parsing JSON from third-party services considering deserialization errors.
For example, this service method:
def signInWithEmailAndPassword(email: String, password: String): Future[ApiResponse[SignInResponse]] =
request("/signin").post(Json.obj("email" -> email, "password" -> password))
.map(_.json.as[ApiResponse[SignInResponse]])
Will throw a server exception if json.as fails which play will catch in the default error handler.
Is that an OK structuring of the client? Seems like a JSON parse error is not really recoverable anyway, so uses the generic error handler is appropriate?

Here is some sample to help you get started. This is a method that you normally write in your Play framework controller.
def dispatchPowerPlant(id: Int) = Action.async(parse.tolerantJson) { request =>
request.body.validate[DispatchCommand].fold(
errors => {
Future.successful{
BadRequest(
Json.obj("status" -> "error", "message" -> JsError.toJson(errors))
)
}
},
dispatchCommand => {
actorFor(id) flatMap {
case None =>
Future.successful {
NotFound(s"HTTP 404 :: PowerPlant with ID $id not found")
}
case Some(actorRef) =>
sendCommand(actorRef, id, dispatchCommand)
}
}
)
}
So what it does is to check the validity of the JSON payload and send the response accordingly! Hope this helps!
You could probably have a similar setup to validate the JSON and return the response accordingly.

Assuming ApiResponse is going to hold any client errors (wrong password, etc) and the Future is going to hold server errors (couldn't establish connection, 500 from remote service, etc), then yes it's appropriate for the exception in the Future to bubble up to the error handler and return a 500 to your caller (also assuming there are no resources you need to clean up before returning).

Related

Handling and manipulating a list of http responses

I'm currently trying to implement API logic to fetch multiple images from a server.
This server accepts an image id and return an HTTP response that contains the image in PNG format as an entity.
Right now, we want to add a new endpoint that accepts a list of images IDs and return a list of all the images:
I have done the following:
def getImagesFromIds(IdsList: List[String]): Future[List[HttpResponse]] = {
Future.sequence {
IdsList.map(
id => getImageById(id)
)
}
}
this function will receive a list of ids and will call the getImageById to fetch all the images, it will return a list of HttpResponse.
And for the route definition, I have done the following:
def getImagesByIdsListRoute: Route = get {
path("by-ids-list") {
entity(as[List[String]]){
upcs =>
complete(getImagesFromIds(upcs))
}
}
}
But I'm getting the following error message:
no implicits found for parameter m: marshalling.toresponsemarshallable[list[httpresponse]]
Does Any one know how we can marshall a list of http responses, or if there is any way to improve this logic to fetch multiple http responses ?
If I understand correctly, you want to download multiple images and return them as a HTTP response.
The problems with your current attempt
The call to the API made via getImageById returns a HttpResponse. You can't be sure what is the result of this API call. If it fails, the response won't contain any image at all.
You are trying to return List[HttpResponse] as your response. How should this response be serialized? Akka doesn't know what you mean by that and tries to find a marshaller which will serialize your object (for example to JSON) but can't find one.
Returning a list of images requires zipping them. You can't return multiple entities in a single HTTP response.
Possible approach
You have to change getImageById so that it checks what is in the HttpResponse and returns the entity bytes.
Example:
response match {
case HttpResponse(StatusCodes.OK, _, entity, _) =>
entity.dataBytes
case resp # HttpResponse(code, _, _, _) =>
// Response failed and we don't care about the response entity
// Details: https://doc.akka.io/docs/akka-http/current/implications-of-streaming-http-entity.html
resp.discardEntityBytes()
// Decide yourself how you want to handle failures
throw new RuntimeException("Request failed, response code: " + code)
}
dataBytes returns a Source so you'll end up with a List of Sources. You have to concatenate them via, for example via concat.
The result stream has to be zipped via Compression.gzip.
Finally, the stream can be put in the complete method of getImagesByIdsListRoute.

Unit testing NancyFX API - ConfigurableBootstrapper Exception

Im trying to get nunit test setup for my Nancy API. I have a very simpLe end point:
this.Get["/"] = _ =>
{
return Negotiate
.WithModel(" API is running")
.WithStatusCode(HttpStatusCode.OK);
};
When I try to test it with this test:
this._browser = new Browser(with => {
with.Module(new IndexModule());
});
var result = this._browser.Get("/", with => { with.HttpRequest(); });
Assert.That(result.StatusCode, Is.EqualTo(HttpStatusCode.OK));
I get ConfigurableBootstrapper Exception and with message of "OhNoes".
If I change the return to:
return "API is running";
It works. I think I might be missing something in the test setup to allow the negotiated return.
Does anyone have an idea of what I'm doing wrong? Thanks.
There will be a clue in the "Oh Noes" exception - probably something like;
Nancy.ViewEngines.ViewNotFoundException
Try adding
with.Header("Accept", "application/json")
or similar to your request setup. By default I think the testing browser requests HTML content, which Negotiate will want to render in a view. See here https://github.com/NancyFx/Nancy/wiki/Content-Negotiation under the section "Default response processors"

Angular 2 how to read Custom error message from backend

My problem with Angular 2 that was not exist in AngularJS, that I was sending the error message as a string with backend API call in case I have error, with error status 401 as example, the problem now that I can't read this message from Angular2 http response message, while I can do that from AngularJS:
I tried the following codes and nothing was helpful:
Promise:
this._http.post('/login',{email: 'email#example.com', password: '123'})
.toPromise()
.then((resp) => console.log(resp), (error) => console.log(error));
Observable:
this._http.post('/login',{email: 'email#example.com', password: '123'})
.subscribe(response =>console.log(response), (error) => console.log(error));
And from back-end I send response as a text, for OK or Unauthorized, for OK i send back String token == UUID.randomUUID().toString();, for error I send back message like String error = " Invalid credentials ";, the problem is that the console.log works and print the text for success (token in this case), but in case error, its just prints: Response with status: 200 for URL: null.
If I change code to JSON.stringify(error) I get something like this:
{"_body":{},"status":401,"ok":false,"statusText":"Unauthorized","headers":{"null":["HTTP/1.1 401 Unauthorized"],"Access-Control-Allow-Headers":["Origin"," X-Requested-With"," Content-Type"," Accept"," Referer"," User-Agent"],"Access-Control-Allow-Met
hods":["POST"," GET"," PUT"," DELETE"," OPTIONS"],"Access-Control-Allow-Origin":["*"],"Allow":["*"],"Content-Length":["36"],"Content-Type":["text/plain; charset=utf-8"],"Date":["Tue"," 23 Aug 2016 14:53:25 GMT"]},"type":2,"url":null}
As you can see the error test not even mentioned inside the Object !!
I tried to change the response for error from backend to return json like this:
{
"message": "invalid email or password"
}
I can get the result inside _body, and I can only read it like this: console.log(error._body.message) ! but i feel its something wrong this way, and I don't want to response as a json in this case.
For angularjs (angular 1), its so simple just to print the response and everything is cool, while in angular 2 its a really problem.
What the problem, and how I can solve this issue without any refactor to backend?
Edit:
I'm using Angular 2.0.0-rc.4 and same for http : `"#angular/http": "2.0.0-rc.4"
Mothanfar
In my case I'm working with the Asp Web Api as back end,this thing is making me crazy as well, the only solution I found is transform in a json and read the message, I know is really ugly but works for me.
Regards.
CheckError(error: any) {
let servermsg: any = JSON.parse(error._body)["ModelState"]["Login"][0];
if (servermsg) {
this.showMsg = true;
this.msg = servermsg;
}
}
If you are returning JSON object from the server, you may use the below code at client side:
let errMsg: ErrorMessage = err.json();
console.log(errMsg.message)
export class ErrorMessage {
message:string;
}

How can I redirect all unknown URLs in lift framework?

I'm not very familiar with lift framework and wanted to know if the following use case is possible using lift framework.
On server1, Lift is serving REST webservice at following url "/contact/"
However, if the client sends request to the following URL https://server1/contact/meet/" then it is not implemented on this specific server but "might" be implemented by another server. Can Lift redirect any such unsupported URLs to some specific server? Eg, in 302 response, can Location be specified by Lift to https://server2/contact/meet/ ?
Please note that these are unknown URLs and can't be configured statically.
Yeah, I get it. Maybe you need LiftRules.dispatch and net.liftweb.http.DoRedirectResponse. Following is the code I try to solve your trouble.
// The code should in the server1; JsonDSL will be used by JsonResponse
class Boot extends Bootable with JsonDSL {
def boot {
initDispatch
}
def initDispatch {
LiftRules.dispatch.append {
case Req("contact" :: url :: Nil, _, GetRequest) => {
() => Full(
if (url == "join") {
// or other url that match what will be implemented in server1
// your implementation, say JsonResponse
JsonResponse("server1" -> true)
} else {
// if the url part does not match simply redirect to server2,
// then you have to deal with how to process the url in server2
DoRedirectResponse("https://server2/contact/meet/")
}
)
}
}
}
}
Anyway, hope it helps.

Spray Authentication method with BasicAuth

Spray is hard!! I now know that my knowledge on HTTP protocol is not nearly enough and API design isn't easy. However, I still very much want my practice app to work. I'm writing this authentication for POST/PUT/DELETE method. It appears that there are at least two ways to do this: BasicAuth or write a custom directive.
I found this article:
BasicAuth: https://github.com/jacobus/s4/blob/master/src/main/scala/s4/rest/S4Service.scala
I'm trying it out because it looks simple.
The compile and run stages are fine, and the server runs. However, when I'm trying to send a PUT request to test the implementation (using Python's Httpie: http PUT 127.0.0.1:8080/sec-company/casper username=username token=123), the feedback is:HTTP/1.1 404 Not Found
Here's my route:
pathPrefix("sec-company") {
path("casper") {
//first examine username and token
authenticate(BasicAuth(CustomUserPassAuthenticator, "company-security")) {userProfile =>
post { ctx =>
entity(as[Company.Company]) { company =>
complete {
company
}
}
}
}
Here is my implementation of UserPassAuthenticator:
object CustomUserPassAuthenticator extends UserPassAuthenticator[UserProfile] {
def apply(userPass: Option[UserPass]) = Promise.successful(
userPass match {
case Some(UserPass(user, token)) => getUserProfile(user, token)
case _ => None
}
).future
}
First of all, is this the right way to implement authentication? Second, where does UserPassAuthenticator find the username and password?? Can I send back a better HTTP header other than 404 to indicate failed authentication?
If this is far from correct, is there any tutorial on authentication that I can follow? TypeSafe's Spray templates are more about overall patterns and less about Spray's functionality!
Thank you!
I had the same problem, even after looking at https://github.com/spray/spray/wiki/Authentication-Authorization (which says it's for an older version of Akka but it still seems to apply) I came up with the following:
trait Authenticator {
def basicUserAuthenticator(implicit ec: ExecutionContext): AuthMagnet[AuthInfo] = {
def validateUser(userPass: Option[UserPass]): Option[AuthInfo] = {
for {
p <- userPass
user <- Repository.apiUsers(p.user)
if user.passwordMatches(p.pass)
} yield AuthInfo(user)
}
def authenticator(userPass: Option[UserPass]): Future[Option[AuthInfo]] = Future { validateUser(userPass) }
BasicAuth(authenticator _, realm = "Private API")
}
}
I mix in this trait into the Actor that runs the routes and then I call it like this:
runRoute(
pathPrefix("api") {
authenticate(basicUserAuthenticator) { authInfo =>
path("private") {
get {
authorize(authInfo.hasPermission("get") {
// ... and so on and so forth
}
}
}
}
}
}
The AuthInfo object returned by the validateUser method is passed as a parameter to the closure given to the authorize method. Here it is:
case class AuthInfo(user: ApiUser) {
def hasPermission(permission: String) = user.hasPermission(permission)
}
In Spray (and HTTP), authentication (determining whether you have a valid user) is separate from authorization (determining whether the user has access to a resource). In the ApiUser class I also store the set of permissions the user has. This is a simplified version, my hasPermission method is a bit more complex since I also parametrize permissions, so it's not just that a particular user has permission to do a get on a resource, he might have permission to read only some parts of that resource. You might make things very simple (any logged-in user can access any resource) or extremely complex, depending on your needs.
As to your question, when using HTTP BASIC authentication (the BasicAuth object), the credentials are passed in the request in an Authorization: header. Your HTTP library should take care of generating that for you. According to the HTTP standard, the server should return a 401 status code if the authentication was incorrect or not provided, or a 403 status code if the authentication was correct but the user doesn't have permissions to view the content.