I am interested about React.js for server side rendering for my web service, but I encounter one problem trying to port from my web service developed using Express.js. Currently, my web service is serving 2 platform
web (Angular.js client)
Android (native application - developed in Java)
My current architecture is all my client side are connected to my server using JSON. So, my client will send a HTTP GET/POST request to my server and I will return JSON back. My server doesnt server HTML page.
Below, are examples of my endpoints APIS using REST:
/api/books
GET - return JSON with array of books information
POST - send a JSOn to server, to add new book information
How should I change my server side to include React? Since React uses server side rendering instead of communicating using JSON?
My current Angular.js project, I placed them into a public folder and it serves as static files.
Possible solutions:
should I add a new set of URLs, one for rendering (/books), the second for JSON (/api/books)?
server detect the type of application (mobile or web) and serve accordingly?
UPDATE 1
Another possible solutions
res.format({
'text/html': function() {
var html = React.renderToString(React.createElement(BooksPage, {books: books}));
res.render('main-layout', {
html: html,
data: JSON.stringify({books:books})
});
},
'application/json': function() {
res.send({
book: books
})
}
})
is to use Express.js content negotiation (res.format), doing so i keep the same route for handline HTML and JSON?
You have your api routes, and you have your page routes. The user doesn't go to http://example.com/api/books to see a list of books, they go to something like http://example.com/books/ to see the books page.
When you get a request for a page like /books/, you serve html. Your api can still be used on the client.
The simplest way to do this is make very lean controllers, and keep most of the logic in plain functions (example with promises, but not important).
function getBooks(){ return /* database thing returns promise */ };
app.get('/api/books', function(req, res){
getBooks().then(function(books){ res.send(books); });
});
app.get('/books/', function(req, res){
getBooks().then(function(books){
var html = React.renderToString(React.createElement(BooksPage, {books: books}));
res.render('main-layout', {
html: html,
data: JSON.stringify({books:books})
});
});
});
Let's say your main-layout template ends up producing this:
<html><head></head>
<body>
<div id="root">
<!-- output of renderToString -->
<div data-reactid="0">...</div>
</div>
<script>
/* output of the JSON.stringify */
var __initial_data = [{"id": ...}];
</script>
<script src="client-side-code-bundle.js"></script>
</body>
</html>
You pick up the rendering on the client using the same data as the server used:
React.render(React.createElement(BooksPage, __initial_data));
Your event listeners are bound, and you go from there.
Related
I am developing a really simple webapp that searches for a company's stocks.
Here is the JS code (uses AJAX to fetch the company's stock from the server):
document.getElementById("requestQuoteBtn").addEventListener("click", function createQuoteRequest(){
var quoteSymbol = document.getElementById("requestedSymbol").value;
var quoteRequest = createAJAX();
quoteRequest.open('GET', '/quote?sym='+quoteSymbol);
quoteRequest.send();
quoteRequest.onload = function getQuoteRequest(){
if(quoteRequest.status == 200){ // SUCCESSFUL
displayQuoteData(false, JSON.parse(quoteRequest.response)); // basically shows a hidden div with the data
}
else // NO COMPANY W/ THIS SYMBOL FOUND
displayQuoteData(true, null);
};
});
Here is the Flask code:
#app.route("/quote", methods=["GET"])
#login_required
def quote():
requestedSymbol = request.args.get("sym")
if not requestedSymbol:
return "no symbol"
quoteData = lookup(requestedSymbol) # USES AN API TO FETCH COMPANY'S STOCK
if quoteData is None:
return "NONE", 404
else:
return quoteData
The issue is that if the user accesses, for example, this URL:
www.mywebsite.com/quote?sym=AAPL
It will literally show a raw HTML with JSON's data, instead of my website with the data:
{"name":"Apple, Inc.","price":"$245.18","symbol":"AAPL"}
How can I prevent this?
If you simply want to make sure that users do not accidentally access your api endpoint when trying to access your website (aka this is about user experience and your not concerned with adding auth to your API endpoint)
The easiest way is to create separate routes for your api and your client routing
Update:
#app.route("/api/quote", methods=["GET"])
Likewise update:
quoteRequest.open('GET', '/api/quote?sym='+quoteSymbol);
Your client routing will still be:
#app.route("/quote", methods=["GET"])
If you want to make sure that nobody can access your api endpoint then you need to add some sort of authorization to your endpoint.
If you do not secure your API with some authorization then anyone can access the data you return from your server API simply by visiting the route.
Either way setting up separate routes for your API endpoints and client side routes should solve the problem of showing API data instead of your client template when visiting:
mywebsite.com/quote?sym=AAPL
Im developing a real time application using SignalR, im using MVC and Angularjs (SILO). Each cshtml page is a SPA. i have an angularjs common service which contains generic operations like delete, post etc.. Also in my common service i introduced signalR. The problem im having is that since the Serivice is only loaded when a SPA loads, so signalR only works in that SPA. What i want is my signalR to be available in my entire application.. what can i do to achieve this?
My Common Service Code
self.hub = $.connection.MyHub;
var initializeSignalR = function () {
self.hub.client.addIssue = function (issue) {
alert(JSON.stringify(issue));
};
$.connection.hub.url = "https://sitename.azurewebsites.net/signalr";
$.connection.hub.logging = true;
$.connection.hub.start().done(function () {
var tenant = sessionStorage.getItem("tenantName");
if (tenant !== "") {
self.hub.server.subscribe(tenant);
} else {
console.log("Error occured");
}
});
};
initializeSignalR();
And on my SILO i have this
viewModelHelper.hub.on('addIssue', function (issueItem) {
issues.push(issueItem);
});
viewModelHelper is my service, lets say if im in SPA Home, signalR methods in SPA Contacts are not excecuted, i understand why, Its because my service is not initialized in that SPA. How can i achieve signalR availability throughtout my app even when the SPA is not loaded.
I think you will have two options:
Load it again in every SILO by placing your SignalR service reference in shared layout. This can keep your current application structure.
Change your app to become real Single Page App(SPA) then you can load and reuse just one SignalR connection in entire app. But this will changed so much of your current system. In that way, you can not use cshtml as current, all view layer and some part of logic will be pushed to client side.
In my meteor app I have a couple of publish/subscribe setup. Now I need to have an other for all users in the system. Now I don't need this information all the time, only when the user opens the configuration overlay. What is the preferred way to load the data on a specific user action (click on a button for example).
I'm also wondering about pagination, in case the number of users is huge, is something like this possible ?
You can always use Meteor.call()
http://docs.meteor.com/#meteor_call
You declare Meteor.call on the client and the method on the server. You can return any data you want,
// client
Meteor.call('getData', function(error, data) {
// do something with data
})
// server
Meteor.methods({
getData: function() {
// return some data
}
});
For pagination, look into using the Reactive Tables package on Atmosphere / Meteorite.
I'm using this code to get client information :
$.getJSON("http://www.geoplugin.net/json.gp?jsoncallback=?",function (data) {
console.log(data.geoplugin_request);
console.log(data.geoplugin_countryName);
});
Then I would like to record this information at the first time that client visit the website (session start of the website). My current project are using backbone.js, require.js, underscore.js.
Any suggestions would be appreciated. Thanks.
Assuming that you have application.js file which act as a entry point of the backbone aplication which initializes your router an all stuff, you can set the client details in the browser using localStorage.
// Retrieve the object from storage
var retrievedVar = localStorage.getItem('countryName');
if( retrievedVar == null) {
$.getJSON("http://www.geoplugin.net/json.gp?jsoncallback=?",function (data) {
console.log(data.geoplugin_countryName);
// Put the object into storage
localStorage.seItem('countryName', JSON.stringify(data.geoplugin_countryName)
});
Hence the getJSON will only be fired once when localStorage var is not set.
Sorry for the bad phrasing.
Essentially, I want to be able to generate a link to a page, which will load a session of certain docs.
For example, Links.find() returns to Client A Links.find({clientName:"A"}). Now Client A wants to send this series of elements to his friend, and wants to do so by sending him a link which loads a client instance that can see Links.find({clientName"A"}).
Any input at all would be greatly appreciated.
Add Iron Router to your project. Then create a route that puts the relevant query into the URL, for example (in a client-loaded JavaScript file):
Router.map(function () {
this.route('client', {
path: '/client/:_clientName',
before: function () {
this.subscribe('client', this.params._clientName).wait();
}
}
}
Then a URI like http://yourapp.com/client/A would cause the client template to render (by default it uses the same name as the route name, unless you specify a different name) subscribing to the client subscription using "A" as the subscription parameter. This would be paired on the server side with:
Meteor.publish('client', function (clientName) {
// Clients is a Meteor collection
return Clients.find({clientName: clientName});
});
So that's how to process links after they've been generated. As for creating them, just work backwards: what query parameters are you passing to your subscription (that in turn get put into the find() call to MongoDB)? Identify each of them and write some code that adds them to an appropriate URI—in this case, your function would simply concatenate "http://yourapp.com/client/" with clientName, in this case "A". Obviously much-more-complicated routes/URIs and queries are possible, for example http://yourapp.com/events/2012-01-01/2012-12-31 with an Iron Router route path of /events/:_fromDate/:_toDate and so on.