How to configure asp.net core to return react front end and work with react-router? - reactjs

I would like my backend to serve up my front end (which is written in react). I added the following cade to program.cs:
var frontEndBuildDirectory = "Path to front end build directory";
app.UseFileServer(new FileServerOptions()
{
FileProvider = new PhysicalFileProvider(frontEndBuildDirectory ),
RequestPath = ""
});
My understanding is that this code does two things:
If a request's url starts with the host name of my backend (localhost:xyzw), everything following the host name will be a virtual path into frontEndBuildDirectory.
When a request is made into frontEndBuildDirectory (the directory itself, not a file within the directory), then the response is the index.html file within frontEndBuildDirectory.
The problem is that my react code uses the react-router library. If I make a request to the backend (localhost:xyzw), the front end is served. Then when I click on something that changes the route (using react router), the url changes to localhost:xyzw/route1 and the everything works. However, if I try to search up localhost:xyzw/route1, I get a page not found error.
My understanding is that this happens because there is no file named route1 in frontEndBuildDirectory.
What is the proper way to make requests like localhost:xyzw/route1 not give a page not found error?

Related

How to load dynamic url as static with Zeit.co for nextjs app?

I have a nextjs app that where I have this on my server.js:
server.get('/recipes/:urlId', (req, res) => {
const actualPage = '/recipe'
const queryParams = { urlId: req.params.urlId }
app.render(req, res, actualPage, queryParams)
})
So basically whenever I try to reach /recipes/{something} it renders the page with queryParams.
This is used later to call an api that will reach for the actual recipe information before displaying it back to the user.
If the user navigate from within the application, it works fine and I can pass the parameters properly and everything works ok.
But if I get the url and paste it directly on the url, I get a 404 instead of the recipe.
Running locally it works fine, but when I deploy it to Zeit I get this issue.
Is there something I need to configure there? Does it use the server.js I have set on my app locally or it uses something else?
The problem is the url makes the server to search for the folder recipe and to search for the folder which you have passed the urlId.
There are two ways you can do this.
1) So you have to tell the server that do not look for the folder just redirect the url for any url to index.html page
Or
2) You can use the hash strategy to make the url have # location path
Check the other solution here Router not working.
Or You can add the htaccess file for the server side changes may not be possible

Managing routes in reactjs app in production

How is routing handled in a built react app?
Specifically, in a development environment, we can simply hit <host>:<port>/<some-path> and the corresponding component is loaded, but once the app is built, we get a bunch of static files and single index.html file, which are then served by some server.
Now, upon hitting the url <server-host>:<server-port>, the app works as intended, but upon entering the path, say <server-host>:<server-port>/<component-path>, a 404 error is returned.
If there is, say a button, upon clicking which, the same /<component-path> is to be redirected, the app works, but then again upon refreshing that page, 404 error occurs.
How can this be solved? What is the correct way to serve such apps having many components at different routes?
approach1:(recommended)
In server config you should point all urls ( http://ipaddress:port/<* any url pattern>) to index.html of react-app . this is known as fallback-mechanism.
And when any request comes,index.html of React app will take care of that automatically because it is single page application.
approach2:
Use HashRouter in React app. So You will not have to configure anything.
Depending on which server you are deploying to, you should redirect all errors to the index.html look for the configuration maybe htaccess or for example if it an AWS S3 bucket you just specify the error page to the same index.html file that is served. Then you handle actual error in your code using a routing library like maybe react-router-dom to take care of actual error. Your failure is because in normal circumstances in a static host when you provide a URL like <server-port>/<component-path> the assumption the server makes is that there is a folder with name component-path in your root directory which has an index file from where to load and display but in the case of React single page application, everything is managed by the index.html. So every request has to pass via the index.html

Sending all routes or paths to angular

In my node js app.js I want that whatever the url is it goes to my angular/html page i.e. begin.html, app.js resides in server folder and begin.html is in client folder of my project. It's like :-
-Project
----Server
---------app.js
----Client
---------begin.html
What should I type in app.js so that all the urls go to begin.html where i am using Angular routing?? I think its something like..
var begin=require('../Client/begin.html);
app.use('*',begin);
If you are going to just have all routes go back to that HTML page, then you could just use a web server like Nginx serve that directory statically.
It looks like you are using Express, but that is just a guess. If you want to make HTTP requests from your Angular side to your Node.js side then you will probably want the default response to return your HTML, but still be able to allow requests through. I would look at using the static method from express to expose a static directory while still allowing you to build other routes (i.e. api routes).
It might look something like this:
// Already created express app above
/*
This will default to using index.html from
your Client directory and serve any other resources
in that directory.
*/
app.use(express.static('../Client'));
// Any other routes
app.listen(3000); // Or whatever your port is
Or you could also implement it using the 404 error handler style and return your default file:
/*
This comes after all of your other
route and middleware declarations and
before the .listen() call
*/
app.use(function(req, res, next) {
res.sendFile(path.join(__dirname + '/../begin.html'));
});

How to redirect crawlers requests to pre-rendered pages when using Amazon S3?

Problem
I have a static SPA site built with Angular and hosted on Amazon S3. I'm trying to make my pre-rendered pages accessible by crawlers, but I can't redirect the crawlers requests since Amazon S3 does not offer a URL Rewrite option and the Redirect rules are limited.
What I have
I've added the following meta-tag to the <head> of my index.html page:
<meta name="fragment" content="!">
Also, my SPA is using pretty URLs (without the hash # sign) with HTML5 push state.
With this setup, when a crawler finds my http://mywebsite.com/about link, it will make a GET request to http://mywebsite.com/about?_escaped_fragment_=. This is a pattern defined by Google and followed by others crawlers.
What I need is to answer this request with a pre-rendered version of the about.html file. I've already done this pre-rendering with Phantom.js, but I can't serve the correct file to crawlers because Amazon S3 do not have a rewrite rule.
In a nginx server, the solution would be to add a rewrite rule like:
location / {
if ($args ~ "_escaped_fragment_=") {
rewrite ^/(.*)$ /snapshots/$1.html break;
}
}
But in Amazon S3, I'm limited by their redirect rules based on KeyPrefixes and HttpErrorCodes. The ?_escaped_fragment_= is not a KeyPrefix, since it appears at the end of the URL, and it gives no HTTP error since Angular will ignore it.
What I've tried
I've started trying using dynamic templates with ngRoute, but later I've realized that I can't solve this with any Angular solution since I'm targeting crawlers that can't execute JavaScript.
With Amazon S3, I have to stick with their redirect rules.
I've managed to get it working with an ugly workaround. If I create a new rule for each page, I'm done:
<RoutingRules>
<!-- each page needs it own rule -->
<RoutingRule>
<Condition>
<KeyPrefixEquals>about?_escaped_fragment_=</KeyPrefixEquals>
</Condition>
<Redirect>
<HostName>mywebsite.com</HostName>
<ReplaceKeyPrefixWith>snapshots/about.html</ReplaceKeyPrefixWith>
</Redirect>
</RoutingRule>
</RoutingRules>
As you can see in this solution, each page will need its own rule. Since Amazon limits to only 50 redirect rules, this is not a viable solution.
Another solution would be to forget about pretty URLs and use hashbangs. With this, my link would be http://mywebsite.com/#!about and crawlers would request this with http://mywebsite.com/?_escaped_fragment_=about. Since the URL will start with ?_escaped_fragment_=, it can be captured with the KeyPrefix and just one redirect rule would be enough. However, I don't want to use ugly URLs.
So, how can I have a static SPA in Amazon S3 and be SEO-friendly?
Short Answer
Amazon S3 (and Amazon CloudFront) does not offer rewrite rules and have only limited redirect options. However, you don't need to redirect or rewrite your URL requests. Just pre-render all HTML files and upload them following your website paths.
Since a user browsing the webpage has JavaScript enabled, Angular will be triggered and will take control over the page which results into a re-rendering of the template. With this, all Angular functionalities will be available for this user.
Regarding the crawler, the pre-rendered page will be enough.
Example
If you have a website named www.myblog.com and a link to another page with the URL www.myblog.com/posts/my-first-post. Probably, your Angular app has the following structure: an index.html file that is in your root directory and is responsible for everything. The page my-first-post is a partial HTML file located in /partials/my-first-post.html.
The solution in this case is to use a pre-rendering tool at deploy time. You can use PhantomJS for this, but you can't use a middleware tool like Prerender because you have a static site hosted in Amazon S3.
You need to use this pre-render tool to create two files: index.html and my-first-post. Note that my-first-post will be an HTML file without the .html extension, but you will need to set its Content-Type to text/html when you upload to Amazon S3.
You will place the index.html file in your root directory and my-first-post inside a folder named posts to match your URL path /posts/my-first-post.
With this approach, the crawler will be able to retrieve your HTML file and the user will be happy to use all Angular functionalities.
Note: this solution requires that all files be referenced using the root path. Relative paths will not work if you visit the link www.myblog.com/posts/my-first-post.
By root path, I mean:
<script src="/js/myfile.js"></script>
The wrong way, using relative paths, would be:
<script src="js/myfile.js"></script>
EDIT:
Below follows a small JavaScript code that I've used to prerender pages using PhantomJS. After installing PhantomJS and testing the script with a single page, add to your build process a script to prerender all pages before deploying your site.
var fs = require('fs');
var webPage = require('webpage');
var page = webPage.create();
// since this tool will run before your production deploy,
// your target URL will be your dev/staging environment (localhost, in this example)
var path = 'pages/my-page';
var url = 'http://localhost/' + path;
page.open(url, function (status) {
if (status != 'success')
throw 'Error trying to prerender ' + url;
var content = page.content;
fs.write(path, content, 'w');
console.log("The file was saved.");
phantom.exit();
});
Note: it looks like Node.js, but it isn't. It must be executed with Phantom executable and not Node.

Running an Backbone app as an independent JS application - Routes not working

currently, I run backbone as the front-end of my rails 3.2 application. I need to migrate it into an independent JS application, as part of my putting it as part of Trigger.io.
It now exists as its own index.html file, referencing the assets folder.
When I open the file, it loads the Backbone modules, but the page remains empty. And when I run fetch() commands, it
So, I got a couple of qns:
1) How do I trigger the routes such that it goes to a designated page by default?
I know it gets triggered in Backbone.History.Start, but I am not sure what to do before that.
2) The url is "file://localhost/Users/mingyeow/Desktop/index.html"
How do I set the root url manually to use localhost:3000/my web address?
// define router
var Router = Backbone.Router.extend({
routes : {
'index' : 'indexAction',
'*default' : '_defaultAction'
},
indexAction : function() {
// this will be executed when user navigate to #index
},
_defaultAction : function() {
// this will be executed when user navigate anywhere else (#XXX)
}
});
// on document ready
$(function() {
// initialize router
new Router();
// and start history
Backbone.history.start();
});
You can navigate this way.
Or by clicking the link : Index route
You can use python server. To start it type in the Terminal:
$ python -m SimpleHTTPServer
And check http://localhost:8000
1) To trigger a route change you just need to navigate to a page via a href or JavaScript like window.location. Read up on Backbone Routes but essentially you need to write a function for every 'page'. Each function should take care of rendering the page.
2) This should be very simple. You need a local web server. What I started doing recently is just having a simple Node server. Node is very easy to install and its worth experimenting with. Download a static web server such as this one I made. To use it just put your backbone application in a directory named 'public' and run server.js in node.
If you don't want to do this you can run a simple LAMP/WAMP/MAMP installation and set the root of the Apache web server.

Resources