I am trying to get the query parameters in the url.
There doesn't seem to be an easy way to do this...
which leaves me with the feeling that I must have missed a something in the doc.
Just call
Router.current().params //params is the dict you wanted
in Iron Router 7.1+
Interestingly three answers and no one offered the complete answer.
Iron-Router 1.0.x
From within a route, use:
// URL: http://example.com/page/?myquerykey=true
this.params.query // returns the full query object
this.params.query.myquerykey // returns a particular query value
Similarly, outside of the route (but still inside the client code), and inside your template, use:
// URL: http://example.com/page/?myquerykey=true
Router.current().params.query
Router.current().params.query.myquerykey
Query parameters, not to be confused with parameters passed via the URL.
iron router >= 1.0
A route's query parameters are available as properties of this.params.query.
If your URL looked like:
/posts/5?sort_by=created_at
then this.params.query.sort_by would equal 'created_at'.
iron router < 1.0
A route's query parameters are available as properties of this.params.
If your URL looked like:
/posts/5?sort_by=created_at
then this.params.sort_by would equal 'created_at'.
In Iron Router 1.0.0, you need to use
this.params.query.YOUR_PARAMETER_NAME
to get it
For example, if you route is /xxx/?a=b
this.params.query.a
outputs 'b'
try tihs:
Router.current().params.parametername;
and in router.js file routing must be:
route(routername/:parametername)
Ensure that if you are using Router.go that your first parameter is a template name, and not a path. query parameters are not passed if you specify a path.
Encoded URI undefined Solution:
The better way to get the query parameters object is:
this.request.query.MyParam
Using the suggested option of:
this.params.query.MyParam
Is ok as long as you are not working with encodedURI parameters, when using this option with encodedURI parameter, the parameter will be equal to undefined.
Example below:
{ // console.log(this.params.query)
product: 'Chair',
ip: '172.0.1.183',
message: 'My Little Chair',
request: '100% Discount',
severity: '4',
api_key: 'XXXXX'
}
{ // console.log(this.params.query)
product: 'Chair',
ip: '172.0.1.183',
message: 'My Little Chair',
request: 'undefined', // NOTICE THIS CHANGED TO UNDEFINED!
severity: '4',
api_key: 'XXXXX'
}
Original Query String:
?product=Chair&ip=172.0.1.183&message=My Little Chair&request=100%25%20Discount&severity=4&api_key=XXXXX
You can pass queries like this depending on where you accessing the router:
In the template
{{pathFor 'routeName' query='queryName=queryValue'}}
In the helper
Router.go ('routeName',{},{query: 'queryName=queryValue'}
Note: the empty object between the routeName and the query is if you want to specify any parameters (refer to the full docs to see the difference).
If you would like to pass multiple queries do it like this:
query: 'queryName1=queryValue&queryName2=queryValue'
Don't use spaces and remember to use the & sign.
Related
I have a Dynamodb table with a few fields - my_id is the PrimaryKey. In the API gateway I set up a response with a method that takes in a parameter {my_id}.
Then I have an Integration Request mapping template that takes the passed in parameter and queries the table to return all the fields that match.
Then I have an Integration response mapping template that cleans up the returned items the way I want.
This all works perfect.
The thing I can't figure out how to do is if the parameter that is passed in doesn't match anything in the table, how do I get it to change from a 200 status into a 404?
From what I can tell when the passed in parameter doesn't match anything it doesn't cause an error, it just doesn't return anything.
It seems like I need to change the mapping template on the Integration response to first check if the params are empty and then somehow tell it to change the response status.
I can find info about this type of thing with people using Lambda, but I am not using Lambda - just the Dynamodb table and the API Gateway.
You can use Mapping Template to convert the response that you get from DDB and overrride the response code. You can get more details in the link https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-override-request-response-parameters.html
If you are using cloud formation, you can do this by using below snippet
IntegrationResponses:
- StatusCode: "200"
ResponseTemplates:
application/json: |
{
"payload" : {
}
},
}
IntegrationResponses:
- StatusCode: "200"
ResponseTemplates:
application/json: |
#set($inputRoot = $input.path('$'))
#if($inputRoot.toString().contains("Item"))
$input.json("$")
#set($context.responseOverride.status = 200)
#else
#set($context.responseOverride.status = 404)
#end
Api gateway currently supports mapping the status code using the status code of the integration response (Here dynamodb response code). The only workaround is to use a lambda function which outputs different error messages that can be mapped using a error regex http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-method-settings-execution-console.html.
I am attempting to return an object from a AWS Lambda function instead of a simple string.
// ...
context.fail({
"email": "Email address is too short",
"firstname": "First name is too short"
});
// ...
I have already used the errorMessage for mapping error responses to status codes and that has been great:
// ...
context.fail('That "username" has already been taken.');
// ...
Am I simply trying to do something that the AWS API Gateway does not afford?
I have also already found this article which helped: Is there a way to change the http status codes returned by Amazon API Gateway?.
Update
Since time of writing, lambda has updated the invocation signature and now passes event, context, callback.
Instead of calling context.done(err, res) you should use callback(err, res). Note that what was true for context.done still applies to the callback pattern.
Should also add that with API Gateways proxy and integration implementation this entire thread is pretty much obsolete.
I recommend reading this article if you are integrating API Gateway with Lambda: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html
Original response below
First things first, let's clear a few things up.
context.done() vs. context.fail()/context.success
context.done(error, result); is nothing but a wrapper around context.fail(error); and context.success(response);
The Lambda documentation clearly states that result is ignored if error is non null:
If the Lambda function was invoked using the RequestResponse (synchronous) invocation type, the method returns response body as follows:
If the error is null, set the response body to the string representation of result. This is similar to the context.succeed().
If the error is not null, set the response body to error.
If the function is called with a single argument of type error, the error value will be populated in the response body.
http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html
What this means is that it won't matter whether you use a combination of fail/success or done, the behaviour is exactly the same.
API Gateway and Response Code Mapping
I have tested every thinkable combination of response handling from Lambda in combination with Response code mapping in API Gateway.
The conclusion of these tests are that the "Lambda Error RegExp" is only executed against a Lambda error, i.e: you have to call context.done(error);or context.fail(error); for the RegExp to actually trigger.
Now, this presents a problem as, has already been noted, Lambda takes your error and sticks it in an object and calls toString() on whatever you supplied:
{ errorMessage: yourError.toString() }
If you supplied an error object you'll get this:
{ errorMessage: "[object Object]" }
Not very helpful at all.
The only workaround I have found thus far is to call
context.fail(JSON.stringify(error));
and then in my client do:
var errorObject = JSON.parse(error.errorMessage);
It's not very elegant but it works.
As part of my error I have a property called "code". It could look something like this:
{
code: "BadRequest",
message: "Invalid argument: parameter name"
}
When I stringify this object I get:
"{\"code\":\"BadRequest\",\"message\":\"Invalid argument: parameter name\"}"
Lambda will stick this string in the errorMessage property of the response and I can now safely grep for .*"BadRequest".* in the API Gateway response mapping.
It's very much a hack that works around two somewhat strange quirks of Lambda and API Gateway:
Why does Lambda insist on wrapping the error instead of just giving
it back as is?
Why doesn't API Gateway allow us to grep in the
Lambda result, only the error?
I am on my way to open a support case with Amazon regarding these two rather odd behaviours.
You don't have to use context.fail, use success but send different statusCode and an errorMessage, here is an example of how i format my output:
try {
// Call the callable function with the defined array parameters
// All the function called here will be catched if they throw exceptions
result.data = callable_function.apply(this, params);
result.statusCode = 200;
result.operation = operation;
result.errorMessage = ""
} catch (e) {
result.data = [];
result.statusCode = 500;
result.errorMessage = e.toString();
result.method = method;
result.resource = resource;
}
// If everything went smooth, send back the result
// If context succeed is not called AWS Lambda will fire the function
// again because it is not successfully exited
context.succeed(result);
Use the consumer logic to handle different errors case logic, don't forget that you pay for the time your function is running...
You should replace the use of your context.fail with context.done and use context.fail only for very serious Lambda function failures since it doesn't allow more than one output parameter. Integration Response is able to match mapping template by performing regex on the first parameter passed to context.done this also maps HTTP status code to the response. You can't pass this response status code directly from Lambda since it's the role of API Gateway Integration Response to abstract the HTTP protocol.
See the following:
context.done('Not Found:', <some object you can use in the model>);
and the Integration Response panel this setting:
You can replicate similar approach for any kind of error. You should also create and map the error model to your response.
For those who tried everything put on this question and couldn't make this work (like me), check the thedevkit comment on this post (saved my day):
https://forums.aws.amazon.com/thread.jspa?threadID=192918
Reproducing it entirely below:
I've had issues with this myself, and I believe that the newline
characters are the culprit.
foo.* will match occurrences of "foo" followed by any characters
EXCEPT newline. Typically this is solved by adding the '/s' flag, i.e.
"foo.*/s", but the Lambda error regex doesn't seem to respect this.
As an alternative you can use something like: foo(.|\n)*
I know that I can put :id in the path of my route so that I get a specific URL. Thats what I can get form tutorials I found online. But how can I make use of this feature? What is the benefit?
I assume that the ID is passed internally as like an url parameter, since in the data function seems to be specific return, based on an ID. But I am not sure.
this.route('projectView',{
path:'/projects/:id',
layoutTemplate:'mainLayout',
loginRequired:'entrySignIn',
waitOn:function(){
Meteor.subscribe('customers');
return Meteor.subscribe('projects');
},
data:function(){
Session.set('active_project',this.params.id);
return Projects.findOne({_id:this.params.id});
},
In your example, the path looks like /projects/:id. Under the hood, the router converts the contents of :id into this.params.id which is what you are using in your data hook.
In other words, if the path /projects/abc123 was encountered by the router, it would know that it should use the projectView route and this.params.id should equal abc123 when loading the corresponding data.
Is there a way to get the previous page location before going to the next page in IronRouter?
Is there an event I can use to fetch this information?
Thanks in advance.
Since Iron Router uses the usual History API, you can just use the plain JS method:
history.go(-1);
or
history.back();
Edit: or to check the previous path without following it:
document.referrer;
You can achieve the behavior you want by using hooks.
// onStop hook is executed whenever we LEAVE a route
Router.onStop(function(){
// register the previous route location in a session variable
Session.set("previousLocationPath",this.location.path);
});
// onBeforeAction is executed before actually going to a new route
Router.onBeforeAction(function(){
// fetch the previous route
var previousLocationPath=Session.get("previousLocationPath");
// if we're coming from the home route, redirect to contact
// this is silly, just an example
if(previousLocationPath=="/"){
this.redirect("contact");
}
// else continue to the regular route we were heading to
this.next();
});
EDIT : this is using iron:router#1.0.0-pre1
Apologies for bumping an old thread but good to keep these things up to date saimeunt's answer above is now deprecated as this.location.path no longer exists in Iron Router so should resemble something like the below:
Router.onStop(function(){
Session.set("previousLocationPath",this.originalUrl || this.url);
});
Or if you have session JSON installed (see Session JSON)
Router.onStop(function(){
Session.setJSON("previousLocationPath",{originalUrl:this.originalUrl, params:{hash:this.params.hash, query:this.params.query}});
});
Only caveats with thisis that first page will always populate url fields (this.url and this.originalUrl there seems to be no difference between them) with full url (http://...) whilst every subsequent page only logs the relative domain i.e. /home without the root url unsure if this is intended behaviour or not from IR but it is currently a helpful way of determining if this was a first page load or not
sorry, I'm new to webdev and Meteor and I'm not quite sure of the correct terminology. I am using Meteor-Router to create routes in my Meteor app.
I'm trying to create a test restaurant app, so an entry in my database might be:
name: "Kentucky Fried Chicken"
type: "Fast Food"
On the main page of the app, you see a list of restaurants. But the user can click on any item on that list to get to a more detailed page.
I would rather that the urls don't look like:
/restaurant/123
but more so like:
/fast-food/kentucky-fried-chicken
/japanese/sushi-r-us
/italian/some-italian-restaurant-name
Is this possible to do with Meteor & Meteor-Router? Thank you!
Btw, right now my routes are very simple:
Meteor.Router.add({
'/': 'home',
'/admin': 'admin',
'/403': 'unauthorized'
});
You can use more complex routes than the one you're using now, like this:
Meteor.Router.add({
'/:type/:restaurant': function(type, restaurantName) {
var restaurant = Retaurants.findOne({type: type, name: restaurantName});
Session.set('restaurantFromUrl', restaurant);
// Now your restaurant is in the "restaurantFromUrl" Session
return 'restaurantPage';
}
});
The /:type and /:restaurant will be passed into the callback and be whatever you set them to in your URL. Oh, and you might want to add a /show-restaurant/type/name/ also, else all urls (that aren't set up in other routes) that match the patter "/whatever/url" will try to get a restaurant.
Everything you need to know is here: https://github.com/tmeasday/meteor-router
Oh, and this is just an example. Haven't tested it but it should work.
Current route package for Meteor that most people use is: Iron Router