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/
Related
I have a simple program that consumes IMDB api, I'm getting the result, but it was shown as error because the result is not a structured json.
MovieService.ts
export class MovieService {
constructor(private http:HttpClient) { }
getMovie(movie:string){
return this.http.get(this.generateURL(movie));
}
private generateURL(movie:string){
return "https://v2.sg.media-imdb.com/suggests/titles/"+movie.charAt(0)+"/"+movie+".json?callback=imdb$"+movie;
}
}
addmovie.component.ts
private _filterMovies(value: string) {
this.movieService.getMovie(value).subscribe(
movies => {
console.log(movies);
return movies;
}
);
}
ngOnInit() {
this.addMovieForm.get('movie').valueChanges.subscribe(val => {
this._filterMovies(val)
});
}
I'm getting error like
the response is of bad json. How can I format the json upon receiving? How to solve this? Any leads would be helpful.
The result is not JSON, but rather JSONP. It is essentially returning you a script that is trying to execute the callback method specified.
Instead of http.get() you should call http.jsonp(url, "imbdIgnoresThisParam").
However, according to this answer, the callback query string parameter is ignored by IMDB. The answer suggests dynamically creating the expected callback function, whose name contains the title for which you are searching. In that callback you could do a few different things.
Use the closure to call / set something in your MovieService. This will result in your call to the API throwing an error, as the Angular framework's callback will not be called as expect. You could ignore the error.
Try to call the expected Angular callback, ng_jsonp_callback_<idx>. This will prevent the API call from throwing, but it may not be reliable. The callback name is dynamic and increments with each jsonp() call. You could try to track the number of jsonp() calls in your app. And of course, the framework may change and break this solution. Concurrent calls to getMovie() may break, as the next one may step on the previous callback on the window. Use with caution!
In typescript, your getMovie() function and related helpers might look like so:
private imdbData: any = null;
private jsonpIdx = 0;
private setImdb(json: any) {
this.imdbData = json;
// or do whatever you need with this
}
getMovie(movie:string) {
// dynamically create the callback on the window.
let funcName = `imdb$${movie}`;
window[funcName] = (json: any) => {
// use the closure
this.setImdbData(json);
// or try to call the callback that Angular is expecting.
window[`ng_jsonp_callback_${this.jsonpIdx++}`](json);
}
// go get your data!
let url = this.generateURL(movie)
return this.http.jsonp(url, "ignored").subscribe((json) => {
// this happens if you successfully trigger the angular callback
console.log(json);
}, (err) => {
// this happens if the angular callback isn't called
console.log(this.imdbData); // set in closure!
});
}
Edit for Angular 4
For Angular 4, it looks like you will need to import the JsonpModule along with the HttpModule. Then, you'd inject jsonp just like you'd inject http into your service. The call to IMDB becomes this.jsop.request(url).subscribe(...) and your dynamic callback name needs to change, too.
window[funcName] = (json: any) => {
// or try to call the callback that Angular is expecting.
window["__ng_jsonp__"][`__req${this.jsonpIdx++}`]["finished"](json);
}
I don't have an Angular 5 or 6 project immediately set up, so hard to say if there are any differences with the callback in those versions.
Sort of a hack, but hope it helps!
I have a camel REST endpoint which receives a request of the form:-
{"method" : "getHello"}
Based on the value in the method field in the request body I want to route the request to different routes. So I tried something like this below:-
from("jetty:http://localhost:8888/hello").unmarshal().json(JsonLibrary.Jackson, RouteRequest.class)
.choice()
.when(method(DynamicRouter.class, "route").isEqualTo("getHello")).to("stream:out")
.otherwise()
.log("Processing Failed");
Dynamic Router class is like below:-
public class DynamicRouter {
public String route(RouteRequest req) {
switch (req.getMethod()) {
case "getHello":
return "xxxx";
}
return null;
}
}
Even though I am returning xxxx from route() but the below Predicate is always evaluating to true and it is never going to .otherwise
isEqualTo("getHello")).to("stream:out")
Can someone let me know what I am doing wrong? Also is there some better way of what I am trying to achieve?
I am on camel 2.16.3
This is working as expected. My log4j.properties was not properly configured on the classpath. So the log was not showing up.
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.
I have a Web API controller that looks like this:
[HttpPost]
public IHttpActionResult Test()
{
return Ok();
}
This is correct syntax, However when I try to call this from a service in Angular 2, I get the error message: "json parsing error syntax error unexpected end of input." To resolve this issue, I have to put a value into the ActionResult such as
return Ok(1)
Am I missing some configuration? My Angular 2 service call looks like this:
return this.http.post(API/Controller/Test).map(res => res.json());
Another way:
All my http.get, post or put call (for uniformity):
.map(this.extractData)
In extractData (UPDATED: thanks to raykrow feedback, short code):
private extractData(res: Response) {
return res.text() ? res.json() : {}; ;
}
I guess that when you receive an empty response (without payload) you don't need to call the json method. Under the hood, the XHR response is undefined, JSON.parse(undefined) is called and an error is thrown.
You could skip the call of the map operator:
return this.http.post(API/Controller/Test)/*.map(res => res.json())*/;
Strictly speaking if your API is returning an OK code with no content you should return 204 (no content) - Angular would also be happy with this and not try to parse anything. So your controller method would become:
[HttpPost]
public IHttpActionResult Test()
{
return NoContent();
}
I am getting time out from using JsonpRequestBuilder.
The entry point code goes like this:
// private static final String SERVER_URL = "http://localhost:8094/data/view/";
private static final String SERVER_URL = "http://www.google.com/calendar/feeds/developer-calendar#google.com/public/full?alt=json-in-script&callback=insertAgenda&orderby=starttime&max-results=15&singleevents=true&sortorder=ascending&futureevents=true";
private static final String SERVER_ERROR = "An error occurred while "
+ "attempting to contact the server. Please check your network "
+ "connection and try again.";
/**
* This is the entry point method.
*/
public void onModuleLoad() {
JsonpRequestBuilder requestBuilder = new JsonpRequestBuilder();
// requestBuilder.setTimeout(10000);
requestBuilder.requestObject(SERVER_URL, new Jazz10RequestCallback());
}
class Jazz10RequestCallback implements AsyncCallback<Article> {
#Override
public void onFailure(Throwable caught) {
Window.alert("Failed to send the message: " + caught.getMessage());
}
#Override
public void onSuccess(Article result) {
// TODO Auto-generated method stub
Window.alert(result.toString());
}
The article class is simply:
import com.google.gwt.core.client.JavaScriptObject;
public class Article extends JavaScriptObject {
protected Article() {};
}
The gwt page, however, always hit the onFailure() callback and show this alert:
Failed to send the message. Timeout while calling <url>.
Fail to see anything on the Eclipse plugin console. I tried the url and it works perfectly.
Would appreciate any tip on debugging technique or suggestion
Maybe you should set the callback function explicitly via setCallbackParam, since you have callback=insertAgenda in your url - I presume that informs the server what should be the name of the callback function that wraps the JSON.
Also, it's worth checking Firebug's console (or a similar tool for your browser) - even if GWT doesn't report any exceptions, Firebug still might.
PS: It's useful to use a tool like Firebug to see if the application does in fact receive the response from the server (that would mean that, for example, you do need the setCallbackParam call) or maybe there's something wrong on the server side (for whatever reason).
You have to read the callback request-Parameter (default callback, value something like __gwt_jsonp__.P0.onSuccess) on serversite and have to modify the output to
<callback>(<json>);
In this case:
__gwt_jsonp__.P0.onSuccess(<json>);
Both of these guys are absolutely correct, but here is a concrete example to help you understand exactly what they are referring too.
This is a public JSON api. Take a look at the results:
http://ws.geonames.org/postalCodeLookupJSON?postalcode=M1&country=GB&maxRows=4
This public API supports JSONP through the predefined parameter 'callback'. Basically whatever value you pass into callback, will be used as the function name to wrap around the JSON data you desire. Take a look at the results of these few requests:
http://ws.geonames.org/postalCodeLookupJSON?postalcode=M1&country=GB&maxRows=4&callback=totallyMadeUp
http://ws.geonames.org/postalCodeLookupJSON?postalcode=M1&country=GB&maxRows=4&callback=trollingWithJSONP
It could be happening because of another reason, that the webservice call is returning a JSON object and but the callback is expecting JSONP object (note there is a difference).
So if you are dealing with google maps api, and you are seeing this exception, you need to change it to api provide by maps api, something like
final GeocoderRequest request = GeocoderRequest.create();
request.setAddress(query);
try {
GWT.log("sending GeoCoderRequest");
if (m_geocoder == null) {
m_geocoder = Geocoder.create();
}
m_geocoder.geocode(request, new Geocoder.Callback() {
#Override
public void handle(final JsArray<GeocoderResult> results,
final GeocoderStatus status) {
handleSuccess(results, status);
}
});
} catch (final Exception ex) {
GWT.log("GeoCoder", ex);
}
Or else you could use RequestBuilder as in gwt library.