Handling API call errors with Axios - api

Hi I'm using Axios to build my first API call app , the API I'm trying to get data from is the Pokemon API database pokeapi.co. The code in my app.js document to make the API call and use the data looks like this:
app.get("/", function(req, res){
res.render("home.ejs");
});
app.get("/data", async(req, res) => {
var inputSearch = req.query.searchTerm;
axios.get('https://pokeapi.co/api/v2/pokemon/' + inputSearch) //The API
.then((body) => {
var pokeData = body.data;
res.render("data.ejs", {EJSpokeData: pokeData});
})
.catch((err) => {
res.send('Data not found', err.statusCode);
})
});`
This links to a form in an ejs document that looks like this:
<form action="/data" method="GET" id="searchForm">
<input type="text" id="searchBox" placeholder="Enter Pokemon name or ID number.." name="searchTerm">
<input type="submit" value="Submit" id="submit">
</form>
The API is called when the user enters either the Pokémon's name or its ID number into the input to be passed to Axios, my system works fine and returns the data I need, however the name can't be capitalized as the name values in the central API are all lower case so capitalizing a name will cause the system to search for a value that isn't in the API and eventually time out the app giving me the error message "localhost didn’t send any data".
This will also occur if the user spells a name wrong or enters an ID number that isn't present in the API. Also, if the user leaves the input field blank a crash occurs as my ejs document tries to process data that is not present. Is there any way to launch some kind error page if the get request doesn't return any data? Is there any way to prevent the submit request being activated if the input field is blank?
I've tried to res.render an error page in the .catch section but it doesn't see to work, can anyone help?

I don't know anything about express specifically so I can't help you with how to render things, but your API questions I can help with.
If we want to call the API with a lower case name that's easy! We don't need to care about what the user types into the input because we can convert it to lower case before calling the API:
var inputSearch = req.query.searchTerm.toLowerCase();
If we want to ignore empty strings, we can use a conditional statement. There are lots of ways to check for empty strings but the easiest is to just say if (myString) {...} because an empty string will evaluate to false while all other strings are true.
if (inputSearch) {
/* ...axios... */
} else {
res.send("Empty search term");
}

Related

How to change keys lable of yii2 advanced API response

I have created API module in my yii2 advanced application and also added HttpBearerAuth in controller file and it is working.
On Unauthorized I'm getting below response :
{"name":"Unauthorized","message":"Your request was made with invalid credentials.","code":0,"status":401,"type":"yii\\web\\UnauthorizedHttpException"}
I want to change key label of this response like below :
{"error":"Unauthorized","errorMessage":"Your request was made with invalid credentials.","code":0,"status":401}
How do I update these keys?
Attach an event handler to yii\web\Response::EVENT_BEFORE_SEND and examine the $data attribute of the yii\web\Response class. Not sure, but guess you'll find an array where the keys are exactly those that you want to change.
You just need to filter out the responses you want to handle (eg everything except status codes 200 & 201).
Maybe something like this... probably bugs included :-)
Event::on(yii\web\Response::className(), yii\web\Response::EVENT_BEFORE_SEND, function ($event) {
if (Yii::$app->response->getStatusCode() > 201) {
if (isset(Yii::$app->response->data['name']) {
Yii::$app->response->data['error'] = Yii::$app->response->data['name'];
unset(Yii::$app->response->data['name']);
}
}
});

Handle different markets (language / locale) in Angular 2 application using an Web Api

I could use some advice how I should handle different markets in my angular 2 application. By that I mean a new market (like the German market) where the language is in German, as an example. Right now, I have hardcoded the text inside the html (in english ofc) to make it easy for myself.
An example you see here:
<div class="row">
<h2>Booking number: {{bookingNumber}}</h2>
Your changes has been confirmed.
</div>
I have read something about pipes in angular 2, and i guess I should be using something like that. My problem is, that I really don't know where to start.
Already have an Web Api application created in Visual Studio 2015 which I can use and call.
I'm thinking of making two lists in my Web Api project (one for english, one for german), but there should still be some sort of indicator. By that I mean something like:
BOOKING_NUMBER_TEXT, 'the text in english or german'
CONFIRMATION_TEXT, 'the text...'
That list should have two params like, string string or something like that.. any idea how I could make this?
From my angular 2 application, I'm thinking of calling the api and given it an id (number, lets say 1 and 2, where 1 = english, 2 = germany)
My Web Api finds the correct list and sends it back as JSON.
Then I'm guessing of building a pipe my own where I can filter the words I set in the html. I'm thinking of something like:
<div class="row">
<h2>{{BOOKING_NUMBER_TEXT | 'PIPE NAME' }}: {{bookingNumber}}</h2>
{{CONFIRMATION_TEXT | 'PIPE NAME' }}.
</div>
So when it has name BOOKING_NUMBER_TEXT, it should look into the pipe which has the list object, and take out the text from the right one and place it instead.
Is that a good plan or can you maybe give any advice? (I'm don't want to use any translate angular 2 frameworks, because I have to do different things on each market)
Cheers :)
UPDATE
Ok.. I have created some test data and allowed it to be send via my Web Api. Here is how it looks.
public Dictionary<string, string> createEnglishLocaleKeys()
{
Dictionary<string, string> Locale_EN = new Dictionary<string, string>();
// Account Component
Locale_EN.Add("ACCOUNT_LOGIN_TEXT", "Login");
Locale_EN.Add("ACCOUNT_LOGOUT_TEXT", "Logout");
// Booking Component
Locale_EN.Add("BOOKING_ACTIVE_HEADER_TEXT", "ACTIVE BOOKINGS");
Locale_EN.Add("BOOKING_LOADING_TEXT", "Loading bookings");
Locale_EN.Add("BOOKING_NONACTIVE_HEADER_TEXT", "NON ACTIVE BOOKINGS");
Locale_EN.Add("BOOKING_NOPREBOOKING_TEXT", "You currently do not have any previous bookings");
// Booking List Component
Locale_EN.Add("BOOKING_LIST_BOOKINGNUMBER_TEXT", "Booking number");
Locale_EN.Add("BOOKING_LIST_LEAVING_TEXT", "Leaving");
Locale_EN.Add("BOOKING_LIST_RETURNING_TEXT", "Returning");
Locale_EN.Add("BOOKING_LIST_ROUTE_TEXT", "Route");
Locale_EN.Add("BOOKING_LIST_PASSENGERS_TEXT", "Passengers");
Locale_EN.Add("BOOKING_LIST_VEHICLETYPE_TEXT", "Vehicle type");
// Menu Component
// Passenger Component
// DepartureDate Component
// Confirmation Component
Locale_EN.Add("KEY_NOT_FOUND", "KEY NOT FOUND");
return Locale_EN;
}
Have created an LocaleController which takes a string locale "EN" or "DE" as parameter. Then I'm injecting a service for the controller, which will, based on the locale string choose which method to run (For now I'm only sending back the LocaleEN dictionary).
How can I create an value in my Angular 2 application which should be EN as default and should be changeable?
By changeable, you should be able to set it in the URL or some sort of, like:
localhost:3000/amendment?locale=DE
There are several things here:
You could HTTP content negotiation Conneg - See this link: http://restlet.com/blog/2015/12/10/understanding-http-content-negotiation/) and the Accept-Language to tell the server which messages to return.
You need to wait for messages to be there before displaying the screen with for example: <div ngIf="messages">…</div>
I don't think you need to implement a pipe to display messages if they are defined in a map (key / value): {{messages['SOME_KEY']}}
If messages correspond to list a custom filtering pipe can be implemented and used like that: {{messages | key:'SOME_KEY'}}
The implementation of this pipe could be:
#Pipe({name: 'key'})
export class KeyPipe implements PipeTransform {
transform(value, args:string[]) : any {
// Assuming the message structure is:
// { key: 'SOME_KEY', value: 'some message' }
return value.find((message) => {
return (message.key === args[0]);
});
}
}

Dynamically changing jQuery unobtrusive validation attributes

I have a page built in ASP.NET MVC 4 that uses the jquery.validate.unobtrusive library for client side validation. There is an input that needs to be within a range of numbers. However, this range can change dynamically based on user interactions with other parts of the form.
The defaults validate just fine, however after updating the data-rule-range attribute, the validation and message are still triggered on the original values.
Here is the input on initial page load:
<input id="amount" data-rule-range="[1,350]" data-msg-range="Please enter an amount between ${0} and ${1}">
This validates correctly with the message Please enter an amount between $1 and $350 if a number greater than 350 is entered.
After an event fires elsewhere, the data-rule-range is updated and the element looks as such:
<input id="amount" data-rule-range="[1,600]" data-msg-range="Please enter an amount between ${0} and ${1}">
At this point if 500 is entered into the input it will fail validation with the same previous message stating it must be between $1 and $350. I have also tried removing the validator and unobtrusiveValidation from the form and parsing it again with no luck.
$('form').removeData('validator');
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");
Is there a clean way to change the validation behavior based on the input attributes dynamically?
As Sparky pointed out changing default attributes dynamically will not be picked up after the validation plugin has been initialized. To best work around this without rewiring how we register validated fields and rules, I found it easiest to register a custom adapter in the unobtrusive library:
jQuery.validator.unobtrusive.adapters.add("amount", {}, function (options) {
options.rules["amount"] = true;
options.messages["amount"] = function () { return $(options.element).attr('data-val-amount'); };
});
jQuery.validator.addMethod("amount", function (val, el, params) {
try {
var max = $(el).attr('data-amount-max');
var min = $(el).attr('data-amount-min');
return val <= max && val >= min;
} catch (e) {
console.log("Attribute data-amount-max or data-amount-min missing from input");
return false;
}
});
Because the message is a function, it will be evaluated every time and always pick up the latest attribute value for data-val-amount. The downside to this solution is that everytime there is a change we need to change all three attributes on the input: data-amount-min, data-amount-max, and data-val-amount.
Finally here is the input markup on initial load. The only attribute that needs to be present on load is data-val-amount.
<input id="amount" data-val-amount="Please enter an amount between ${0} and ${1}" data-val="true">
You cannot change rules dynamically by changing the data attributes. That's because the jQuery Validate plugin is initialized with the existing attributes on page load... there is no way for the plugin to auto re-initialize after dynamic changes are made to the attributes.
You must use the .rules('add') and .rules('remove') methods provided by the developer.
See: http://jqueryvalidation.org/rules/
you can try this one:
// reset the form's validator
$("form").removeData("validator");
// change the range
$("#amount").attr("data-rule-range", "[1,600]");
// reapply the form's validator
$.validator.unobtrusive.parse(document);
charle's solution works! you cannot have model attributes to use it though, I build my inputs like:
#Html.TextBoxFor(m => Model.EnterValue, new
{
#class = "form-control",
id="xxxx"
data_val = "true",
data_val_range = String.Format(Messages.ValueTooBig, Model.ParamName),
data_val_range_max = 6,
data_val_range_min = 2,
data_val_regex_pattern = "^\\d+(,\\d{1,2})?$"
})
and then in javascript:
$("form").removeData("validator");
$("#xxxx").attr('data-val-range-max', 3)
$("#xxxx").attr('data-val-range-min', 0)
$.validator.unobtrusive.parse(document);

EmberJS Route to 'single' getting JSONP

I'm having trouble with EmberJS to create a single view to posts based on the ID, but not the ID of the array, I actually have a ID that comes with the json I got from Tumblr API.
So the ID is something like '54930292'.
Next I try to use this ID to do another jsonp to get the post for this id, it works if you open the api and put the id, and actually if you open the single url with the ID on it, works too, the problem is:
When, on the front page for example, I click on a link to go to the single, it returns me nothing and raise a error.
But if you refresh the page you get the content.
Don't know how to fix and appreciate some help :(
I put online the code: http://tkrp.net/tumblr_test/
The error you were getting was because the SingleRoute was being generated as an ArrayController but the json response was not an Array.
App.SingleController = Ember.ObjectController.extend({
});
Further note that the model hook is not fired when using linkTo and other helpers. This because Ember assumes that if you linked to a model, the model is assumed to be as specified, and it directly calls setupController with that model. In your case, you need to still load the individual post. I added the setupController to the route to do this.
App.SingleRoute = Ember.Route.extend({
model: function(params) {
return App.TKRPTumblr.find(params.id);
},
setupController: function(controller, id) {
App.TKRPTumblr.find(id)
.then(function(data) {
controller.set('content', data.response);
});
}
});
I changed the single post template a bit to reflect how the json response. One final change I made was to directly return the $.ajax. Ember understands jQuery promises directly, so you don't need to do any parsing.
Here is the updated jsbin.
I modified: http://jsbin.com/okezum/6/edit
Did this to "fix" the refresh single page error:
setupController: function(controller, id) {
if(typeof id === 'object'){
controller.set('content', id.response);
}else{
App.TKRPTumblr.find(id)
.then(function(data) {
controller.set('content', data.response);
});
}
}
modified the setupController, since I was getting a object when refreshing the page and a number when clicking the linkTo
Dont know if it's the best way to do that :s

How do I get data from a background page to the content script in google chrome extensions

I've been trying to send data from my background page to a content script in my chrome extension. i can't seem to get it to work. I've read a few posts online but they're not really clear and seem quite high level. I've got managed to get the oauth working using the Oauth contacts example on the Chrome samples. The authentication works, i can get the data and display it in an html page by opening a new tab.
I want to send this data to a content script.
i'm having a lot of trouble with this and would really appreciate if someone could outline the explicit steps you need to follow to send data from a bg page to a content script or even better some code. Any takers?
the code for my background page is below (i've excluded the oauth paramaeters and other )
` function onContacts(text, xhr) {
contacts = [];
var data = JSON.parse(text);
var realdata = data.contacts;
for (var i = 0, person; person = realdata.person[i]; i++) {
var contact = {
'name' : person['name'],
'emails' : person['email']
};
contacts.push(contact); //this array "contacts" is read by the
contacts.html page when opened in a new tab
}
chrome.tabs.create({ 'url' : 'contacts.html'}); sending data to new tab
//chrome.tabs.executeScript(null,{file: "contentscript.js"});
may be this may work?
};
function getContacts() {
oauth.authorize(function() {
console.log("on authorize");
setIcon();
var url = "http://mydataurl/";
oauth.sendSignedRequest(url, onContacts);
});
};
chrome.browserAction.onClicked.addListener(getContacts);`
As i'm not quite sure how to get the data into the content script i wont bother posting the multiple versions of my failed content scripts. if I could just get a sample on how to request the "contacts" array from my content script, and how to send the data from the bg page, that would be great!
You have two options getting the data into the content script:
Using Tab API:
http://code.google.com/chrome/extensions/tabs.html#method-executeScript
Using Messaging:
http://code.google.com/chrome/extensions/messaging.html
Using Tab API
I usually use this approach when my extension will just be used once in a while, for example, setting the image as my desktop wallpaper. People don't set a wallpaper every second, or every minute. They usually do it once a week or even day. So I just inject a content script to that page. It is pretty easy to do so, you can either do it by file or code as explained in the documentation:
chrome.tabs.executeScript(tab.id, {file: 'inject_this.js'}, function() {
console.log('Successfully injected script into the page');
});
Using Messaging
If you are constantly need information from your websites, it would be better to use messaging. There are two types of messaging, Long-lived and Single-requests. Your content script (that you define in the manifest) can listen for extension requests:
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.method == 'ping')
sendResponse({ data: 'pong' });
else
sendResponse({});
});
And your background page could send a message to that content script through messaging. As shown below, it will get the currently selected tab and send a request to that page.
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, {method: 'ping'}, function(response) {
console.log(response.data);
});
});
Depends on your extension which method to use. I have used both. For an extension that will be used like every second, every time, I use Messaging (Long-Lived). For an extension that will not be used every time, then you don't need the content script in every single page, you can just use the Tab API executeScript because it will just inject a content script whenever you need to.
Hope that helps! Do a search on Stackoverflow, there are many answers to content scripts and background pages.
To follow on Mohamed's point.
If you want to pass data from the background script to the content script at initialisation, you can generate another simple script that contains only JSON and execute it beforehand.
Is that what you are looking for?
Otherwise, you will need to use the message passing interface
In the background page:
// Subscribe to onVisited event, so that injectSite() is called once at every pageload.
chrome.history.onVisited.addListener(injectSite);
function injectSite(data) {
// get custom configuration for this URL in the background page.
var site_conf = getSiteConfiguration(data.url);
if (site_conf)
{
chrome.tabs.executeScript({ code: 'PARAMS = ' + JSON.stringify(site_conf) + ';' });
chrome.tabs.executeScript({ file: 'site_injection.js' });
}
}
In the content script page (site_injection.js)
// read config directly from background
console.log(PARAM.whatever);
I thought I'd update this answer for current and future readers.
According to the Chrome API, chrome.extension.onRequest is "[d]eprecated since Chrome 33. Please use runtime.onMessage."
See this tutorial from the Chrome API for code examples on the messaging API.
Also, there are similar (newer) SO posts, such as this one, which are more relevant for the time being.