I am building a registration form (passport-local as authentication, forms as form helper).
Because the registration only knows GET and POST I would like to do the whole handling in one function.
With other words I am searching after something like:
exports.register = function(req, res){
if (req.isPost) {
// do form handling
}
res.render('user/registration.html.swig', { form: form.toHTML() });
};
The answer was quite easy
exports.register = function(req, res) {
if (req.method == "POST") {
// do form handling
}
res.render('user/registration.html.swig', { form: form.toHTML() });
};
But I searched a long time for this approach in the express guide.
Finally the node documentation has such detailed information:
http://nodejs.org/api/http.html#http_http_request_options_callback
Now you can use a package in npm => "method-override", which provides a middle-ware layer that overrides the "req.method" property.
Basically your client can send a POST request with a modified "req.method", something like /registration/passportID?_method=PUT.
The
?_method=XXXXX
portion is for the middle-ware to identify that this is an undercover PUT request.
The flow is that the client sends a POST req with data to your server side, and the middle-ware translates the req and run the corresponding "app.put..." route.
I think this is a way of compromise. For more info: method-override
Related
I have an app where users can create posts. There is no login or user account needed! They submit content with a form as post request. The post request refers to my api endpoint. I also have some other api points which are fetching data.
My goal is to protect the api endpoints completely except some specific sites who are allowed to request the api ( I want to accomplish this by having domain name and a secure string in my database which will be asked for if its valid or not if you call the api). This seems good for me. But I also need to make sure that my own application is still able to call the api endpoints. And there is my big problem. I have no idea how to implement this and I didn't find anything good.
So the api endpoints should only be accessible for:
Next.js Application itself if somebody does the posting for example
some other selected domains which are getting credentials which are saved in my database.
Hopefully somebody has an idea.
I thought to maybe accomplish it by using env vars, read them in getinitalprops and reuse it in my post request (on the client side it can't be read) and on my api endpoint its readable again. Sadly it doesn't work as expected so I hope you have a smart idea/code example how to get this working without using any account/login strategy because in my case its not needed.
index.js
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
export default function Home(props) {
async function post() {
console.log(process.env.MYSECRET)
const response = await fetch('/api/hello', {
method: 'POST',
body: JSON.stringify(process.env.MYSECRET),
})
if (!response.ok) {
console.log(response.statusText)
}
console.log(JSON.stringify(response))
return await response.json().then(s => {
console.log(s)
})
}
return (
<div className={styles.container}>
<button onClick={post}>Press me</button>
</div>
)
}
export async function getStaticProps(context) {
const myvar = process.env.MYSECRET
return {
props: { myvar },
}
}
api
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
export default function handler(req, res) {
const mysecret = req.body
res.status(200).json({ name: mysecret })
}
From what I understand, you want to create an API without user authentication and protect it from requests that are not coming from your client application.
First of all, I prefer to warn you, unless you only authorize requests coming from certain IPs (be careful with IP Spoofing methods which could bypass this protection), this will not be possible. If you set up an API key that is shared by all clients, reverse engineering or sniffing HTTP requests will retrieve that key and impersonate your application.
To my knowledge, there is no way to counter this apart from setting up a user authentication system.
I am following this Twilio tutorial on how to reply to SMS messages with my app:
https://www.twilio.com/docs/sms/tutorials/how-to-receive-and-reply-node-js
The tutorial assumes you're using Express, but I am doing this with a Cloud Function, so my code looks a bit different:
exports.sms = functions.https.onCall((req: any, res: any) => {
const twiml = new MessagingResponse();
if (req.body.Body === 'hello') {
twiml.message('Hi!');
} else if (req.body.Body === 'bye') {
twiml.message('Goodbye');
} else {
twiml.message(
'No Body param match, Twilio sends this in the request to your server.',
);
}
res.writeHead(200, { 'Content-Type': 'text/xml' });
res.end(twiml.toString());
});
When I text my Twilio #, it hits that endpoint, but I get the following error:
Request has incorrect Content-Type. application/x-www-form-urlencoded
How do I get around this?
It looks like you're mixing up callable type functions and normal HTTP type functions. Please read the documentation to understand the difference. Callable functions are intended to be invoked directly from your mobile app using the provided client SDK. They provide two arguments: an input data object, and a context. Callables do NOT provide "req" and "res". If you want control over the the response, you should be using a normal HTTP function with "onRequest" instead of "onCall".
I need to be able to intercept XHR requests on page loaded with Puppeteer and return mock responses in order to organize backendless testing for my web app. What's the best way to do this?
It seems that the way to go is request.respond() indeed, but still, I couldn't find a concrete example in the web on how to use it. The way I did it was like this:
// Intercept API response and pass mock data for Puppeteer
await page.setRequestInterception(true);
page.on('request', request => {
if (request.url() === constants.API) {
request.respond({
content: 'application/json',
headers: {"Access-Control-Allow-Origin": "*"},
body: JSON.stringify(constants.biddersMock)
});
}
else {
request.continue();
}
});
What happens here exactly?
Firstly, all requests are intercepted with page.setRequestInterception()
Then, for each request I look for the one I am interested in, by matching it by URL with if (request.url() === constants.API) where constants.API is just the endpoint I need to match.
If found, I pass my own response with request.respond(), otherwise I just let the request continue with request.continue()
Two more points:
constants.biddersMock above is an array
CORS header is important or access to your mock data will not be allowed
Please comment or refer to resources with better example(s).
Well. In the newest puppeteer,it provide the request.respond() method to handle this situation.
If anyone is interested I ended up creating special app build for my testing needs, which adds Pretender to the page. And I communicate with Pretender server using Puppeteer's evaluate method.
This is not ideal, but I couldn't find a way to achieve what I need with Puppeteer only. There is a way to intercept requests with Puppeteer, but seems to be no way to provide fake response for a given request.
UPDATE:
As X Rene mentioned there is now native support for this in Puppeteer v0.13.0 using request.respond() method. I'm going to rewrite my tests to use it instead of Pretender, since this will simplify many things for me.
UPDATE 2:
There is pptr-mock-server available now to accomplish this. Internally it relies on request interception and request.respond() method. Library is pretty minimal, and may not fit your needs, but it at least provides an example how to implement backendless testing using Puppeteer. Disclaimer: I'm an author of it.
I created a library that uses Puppeteer's page.on('request') and page.on('response') to record and respond with mocked requests.
https://github.com/axiomhq/puppeteer-request-intercepter
npm install puppeteer-request-intercepter
const puppeteer = require('puppeteer');
const { initFixtureRouter } = require('puppeteer-request-intercepter');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Intercept and respond with mocked data.
const fixtureRouter = await initFixtureRouter(page, { baseUrl: 'https://news.ycombinator.com' });
fixtureRouter.route('GET', '/y18.gif', 'y18.gif', { contentType: 'image/gif' });
await page.goto('https://news.ycombinator.com', { waitUntil: 'networkidle2' });
await page.pdf({ path: 'hn.pdf', format: 'A4' });
await browser.close();
})();
You may want to try out Mockiavelli - request mocking library for Puppeteer. It was build exactly for backendless testing of webapps. It integrates best with jest and jest-puppeteer, but works with any testing library.
I have registered my web address (let's just call it https://mywebaddress/callbacks) with this external API and it will now send me JSON when it completes an action. I don't need to initiate anything outbound to it, I just need to receive the JSON and store it.
EDIT:
JSON data will be receive via POST
Paul's link sent me in the right direction. (http://www.meteorpedia.com/read/REST_API).
Then I found the section titled "WebApp.connectHandlers and connect".
I used the code found there, but in my instance there was an error in the code. I had to change the first line from var connect = Npm.require('connect'); to var connect = Meteor.require('connect');
Here is the code below.
// necessary to parse POST data
var connect = Meteor.require('connect');
// necessary for Collection use and other wrapped methods
var Fiber = Npm.require('fibers');
WebApp.connectHandlers
.use(connect.urlencoded()) // these two replace
.use(connect.json()) // the old bodyParser
.use('/getUserProfile', function(req, res, next) {
// necessary for Collection use and other wrapped methods
Fiber(function() {
var userId = req.body.userId;
var user = Meteor.users.findOne(userId);
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify(user.profile));
}).run();
});
}
Then to test that this was working I used http://www.hurl.it/. I changed the destination to POST and added a header of content-type - application/json. I then pasted in the body some JSON that I knew came from balanced. If you need a tool to see what is actually being posted to your server you can use http://requestb.in/.
Currently, I use the built-in meteor http method (see http://docs.meteor.com/#http) for issuing http calls, on both my client and my server.
However, I'm experiencing two issues:
is it possible to cancel a request?
is it possible to have multiple query parameters which share the same key?
Are these just Meteor limitations, or are there ways to get both to work using Meteor?
I know I could you jquery on the clientside, and there must be a server-side solution which supports both as wel, but I'd prefer sticking with meteor code here.
"is it possible to cancel a request?"
HTTP.call() does not appear to return an object on which we could call something like a stop() method. Perhaps a solution would be to prevent execution of your callback based on a Session variable?
HTTP.call("GET", url, function(error, result) {
if (!Session.get("stopHTTP")) {
// Callback code here
}
});
Then when you reach a point where you want to cancel the request, do this:
Session.set("stopHTTP", true);
On the server, instead of Session perhaps you could use an environment variable?
Note that the HTTP.call() options object does accept a timeout key, so if you're just worried about the request never timing out, you can set this to whatever millisecond integer you want.
"is it possible to have multiple query parameters which share the same key?"
Yes, this appears to be possible. Here's a simple test I used:
Meteor code:
HTTP.call("GET", "http://localhost:1337", {
query: "id=foo&id=bar"
}, function(error, result) {
// ...
});
Separate Node.js server: (just the basic example on the Node.js homepage, with a console.log line to output the request URL with query string)
var http = require('http');
http.createServer(function(req, res) {
console.log(req.url); // Here I log the request URL, with the query string
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
When the Meteor server is run, the Node.js server logged:
/?id=foo&id=bar
Of course, this is only for GET URL query parameters. If you need to do this for POST params, perhaps you could store the separate values as a serialized array string with EJSON.stringify?