How to properly separate frontend and backend? - angularjs

I have a project which was built on playFramework. It has much busines logic, which involves actors, different parsers, works with three services through API, etc. Frontend is written with angularJs (single web page application). We also use coffeScript and less, and for that i added sbt plugins to compile them
I felt like it's not right to have this things altogether. Whenever i push commit to repository with changes related to css files, jenkins downloads scala dependencies, build project, etc. It's static content only for user experience.
My fronted developer gets crazy when he needs to make some changes in backend part because he doesn't know scala well enough. So he spends time in not effective way by trying to get it to work whilst i can do this for minimum time.
I think one repository should be for fronted (angular, coffee, less, images, fonts) and second one is for backend.
Today i've read few articles about that problem, got inspired, and i decided to separate them once for all :)
Firstly i installed node, npm, grunt, and http-server
Then i setup plugins for grunt: less, and coffee.
I ran 'grunt http-server' on address: localhost:8282
Launched my play application on address: localhost:9000
Then replaced old angular $resource url '/api/client' with 'localhost:9000/api/client' to check whether it works and i will see clients list on page localhost:8282. But it doesn't work. I get this in js console:
XMLHttpRequest cannot load localhost:9000/api/client. Origin localhost:8282 is not allowed by Access-Control-Allow-Origin
This is cross domain issue, so i googled and changed controller which serves /api/client
Ok(toJson(res.sortBy(_.id))).withHeaders(("Access-Control-Allow-Origin", "*"))
Headers gets applied properly, i checked this in safari. I also configured angular $httpProvider
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
But it still doesn't work.
I'm not skilled in settings up http servers but may be there is a better way? May be to set up some proxy? Or to setup nginx some how?
Any suggestions? Thanks!

Setting up CORS in play consists of two steps
Respond to preflight requests (option requests)
Setting headers for response.
Seems you miss the first step, check this article for details.

Thanks to Rebotnix
You are right, backend didn't response with needed headers.
Scala code in article is outdated a little bit (There is no PlainResult in play 2.3.6), but it explains the point of using CORS.
package controllers.filters
import play.api.mvc.{Filter, RequestHeader, Result}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object CorsFilter extends Filter {
override def apply(nextFilter: (RequestHeader) => Future[Result])(rh: RequestHeader): Future[Result] = {
nextFilter(rh).map { result =>
result.withHeaders(
"Access-Control-Allow-Origin" -> "*",
"Access-Control-Allow-Methods" -> "POST, GET, OPTIONS, PUT, DELETE"
)
}
}
}
And global settings:
import controllers.filters.CorsFilter
import play.api.mvc.WithFilters
object Global extends WithFilters(CorsFilter)
And preflight action (the same as article's one)
def preflight(url: String) = Action {
Ok.withHeaders(
"Access-Control-Allow-Origin" -> "*",
"Access-Control-Allow-Headers" -> "Origin, X-Requested-With, Content-Type, Accept, Referer, User-Agent",
"Access-Control-Allow-Methods" -> "POST, GET, PUT, DELETE, OPTIONS",
"Allow" -> "*"
)
}
and set up route for preflight:
OPTIONS /*anyUrl controllers.Application.preflight(anyUrl:String)
Now everything works!

Related

React app using API with another origin (CORS)

I have a react app, which uses a java ee backend rest server, running on another domain. I have enabled CORS:
Access-Control-Allow-Origin : http://localhost:3000
Access-Control-Allow-Headers : origin, content-type, accept, authorization
Access-Control-Allow-Credentials : true
Access-Control-Allow-Methods : GET, POST, PUT, DELETE, OPTIONS, HEAD
Access-Control-Max-Age : 1209600
I am using react with fetch like this:
export function get(path, headers) {
return fetch(apiUrl + path, {
"metod" : "GET",
"headers" : headers,
"credentials" : "include"
})
}
My react app is running on http://localhost:3000.
When I am logging in, the server returns the Set-Cookie, but the cookie is not included in any further request to the server, unless I try to log in again. Then it is included for that specific login request.
Any suggestions?
I just want to share how I make my local development painless by this post if you are using create-react-app by just adding your main API url proxy to your package.js for example "proxy": "http://localhost:8080/API"
No need to setup CORS on your backend.
Install this.
https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?hl=es
Once installed, click on his BrowserIcon and toggle on. It is all. You will not receive more error.
EDIT. Solution for Production
If you want config it from your server (or simply not adding a browser extension, try this:)
If you are using node.js do the following: node.js server file: response.writeHead(200, { 'Content-Type': contentType, 'Access-Control-Allow-Origin': '*' })
fetch('http://ajax.googleapis.com/ajax/services/feed/load?v=‌​1.0&num=8&q=http://r‌​ss.cnn.com/rss/editi‌​on_entertainment.rss‌​?output=rss', { method: 'get', mode: 'no-cors', }).then(() => { console.log('Works!'); });
Other solution:If you are using PHP too you can add: <?php header('Access-Control-Allow-Origin: *'); ?> into your PHP File. As I see, it is not the case, so... In your server (eg: Apache) add this directive: Header set Access-Control-Allow-Origin * in Settings (as the first option).
So, I solved the problem by using another stackoverflow thread and robertklep's comment. As stated here: "When working on localhost, the cookie domain must be omitted entirely.". I implemented robertkleps idea, but did not set the domain. It resulted in a Set-Cookie like this: Set-Cookie:kek=7fukucsuji1n1ddcntc0ri4vi; Version=1; Path=/; Max-Age=100000. This works fine.
To add more on existing answers.
With react you can use "proxy" in your package.json to avoid CORS.
Basically if you need to reach localhost:8100 (your java backend) and your react app run on localhost:3000
You can set:
In your package.json
"proxy": "http://localhost:8100"
And then when you want to make a get to /hello which would be an endpoint of your java API you can do:
import axios from 'axios';
axios.get('/hello')
.then(resp => {
console.log(resp.data);
});
And it will be redirected to http://localhost:3000/hello so you will avoid CORS.

ExpressJS not sending Access-Control-Allow-Origin header

I am working on a project where I need an angular web app to be able to access a node/express based backend, and I am attempting to use Cors. However, the express server seemingly won't send the Access-Control-Allow-Origin header, and I have no clue why. I've tried creating a middleware as such:
this.app.use((request, response, next) => {
response.header("Access-Control-Allow-Origin", "*");
next();
}
I've also tried doing something similar individually for each request, using setHeader(), header(), set() and append(), but none of these work either. I'm currently using the cors npm package, but that also isn't working with the following code:
this.app.use(cors({
origin: "http://localhost/4200"
}));
this.app.options("*", cors({
origin: "http://localhost/4200"
})
);
I've checked that none of these are adding the relevant header to the response via postman. Express is adding the "error" header I use to give a human-readable description of the error, but not "Access-Control-Allow-Origin".
I'm writing this in TypeScript.
Did you restart your express server after making the change?
Did you try also adding the Access-Control-Allow-Headers header?
Did you check your middleware is invoked on request?
Did you check your middleware is registered before listen is called?
If both is guaranteed it must have a reason depending on a other part of your Application. So you should provide more information about booting and structure of files and directories.
Turns out the problem wasn't with the code, but with the compiler setup. I deleted and recreated the tsconfig.json file, and it works now.

On Ionic 2, XMLHttpRequest cannot load .. with Laravel api

This is my scenario:
XMLHttpRequest cannot load http://phi.dev/api/login. Redirect from 'http://phi.dev/api/login' to 'http://localhost:8100/' has been blocked by CORS policy: Request requires preflight, which is disallowed to follow cross-origin redirect.
I have a Aungular2/Ionic 2 App on local and a Laravel Web API for authenticating user.
if I call this Web API from my Angular2 Module, I get an exception as given above.
Note: In Chrome Network, I could my angular service is being called 2 times. First with Request Method: OPTIONS and second time with Request Method: Get, which returns Http 302.
Does anyone know how to resolve this issue.
Thanks in advance.
Because the request is external and because you are serving the application locally you will have CORS issues.
To avoid such issues locally (when using ionic serve), you have to setup a local proxy in the ionic configuration files.
Check your Ionic 2 project directory and locate the file ionic.config.json: it usually is in the root directory of the project (and need to be there, along with package.json and so on).
Open the file, and add this (do not forget to be SURE that the line BEFORE that one ends with a comma (,), since it's a json file):
"proxies": [
{
"path": "/server",
"proxyUrl": "http://phi.dev"
}
]
Now, in the section where are you are performing the HTTP request, replace the http://phi.dev with /server. I will give you an example here.
I do recommend you, however, to be aware that such edit will make your compiled app to NOT work, so you likely want to put a debug flag for testing and compiled environments, like this:
class MyServiceOrComponent {
private debug: boolean = true; // Set this flag to FALSE when you need to actually compile the app for any real device.
private server: string = this.debug ? "/server" : "http://phi.dev";
constructor(private http: HTTP) {
const login = "/api/login"; // don't to it exactly like that, make a static list or something like this to store paths.
let request = this.http.get(this.server + login).then((res) => {
console.log(res);
});
}
}
What happens, explained briefly, is that if you perform the HTTP request as "localhost" it will throw you an exception (because of the CORS policy). Such will happen only when you are running the application in your testing environment (localhost). To avoid such, you provide a proxy, so that the request will be valid.

Getting CORS error in angularJS front end

I am using a flask server with an angular front end. Up until recently, I was running the project on my local and had no issues.
I now moved my project to a remote server and have been getting the following error. I am not sure what i'm doing wrong:
My error:
XMLHttpRequest cannot load http://ec2-..../api/loginStatus/. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:8100' is therefore not allowed
access. The response had HTTP status code 503.
The snippets of my flask server side code (where I am adding my headers to the response is given below):
#app.after_request
def after_request(response):
response.headers.add('Access-Control-Allow-Origin','http://localhost:8100')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE')
response.headers.add('Access-Control-Allow-Credentials', 'true')
return response
I use both restangular and $http methods in my front end angularjs.
I have added the following lines in the .config of my main module:
.config(['RestangularProvider', '$stateProvider', '$urlRouterProvider','$httpProvider',
function(RestangularProvider, $stateProvider, $urlRouterProvider,$httpProvider) {
//$httpProvider.defaults.useXDomain = true;
$httpProvider.defaults.headers.common['Access-Control-Allow-Origin'] = "http://localhost:8100";
$httpProvider.defaults.withCredentials = true;
$httpProvider.defaults.headers.common = 'Content-Type: application/json';
delete $httpProvider.defaults.headers.common['X-Requested-With'];
Would someone be able to help me out here? I've referred to a lot of material and I am not sure what i'm doing wrong.
PS: I am getting 200 status messages on my server. I am therefore assuming that the error is in my front end and not my server side. Please correct me if I am wrong
Regards,
Sunil
EDIT
Hi everyone, I have solved the issue. I would like to thank #Mathijs Segers and #Hassan Mehmood for their inputs.
It turns out that there was a nginx configuration issue which led to the server becoming unavailable.
Firstly, there was an issue with the symbolic link that was being created for the flask backend (I am running my server side through a git repo on /home/username and then creating a symbolic link at /var/www/sitename.com
I was also throttling the number of requests that can be sent in a second (users could send only 1 every 2 seconds) resulting in the 503 error.
The original code I put up worked fine after I fixed it.
Eyooo, it is actually on your server side. You need to provide correct headers.
So you've tried this, I have no experience with flask but this I don't like;
response.headers.add('Access-Control-Allow-Origin','http://localhost:8100')
for testing purposes I suggest you just change the http:// part, to *
so
response.headers.add('Access-Control-Allow-Origin','*')
If that doesn't work verify that the header is actually being set, you could use a different program which doesn't care for CORS like postman or directly calling it in the browser if it doesn't depend on Accept headers.
here is some more readings about what it all is about.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
EDIT:
Ok silly of me: The response had HTTP status code 503.
This part in the error actually states what kind of response your server is giving, so currently there is an error on your server side. This happens when it is f/e down or what not.
So it seems that you're not doing anything strange, but your server side seems broken.
XMLHttpRequest cannot load http://ec2-..../api/loginStatus/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8100' is therefore not allowed access. The response had HTTP status code 503.
So this error here, I suggest looking at your headers, and maybe disable some. You currently allow only 2 request headers that might cause some issues as well?
Flask-CORS
A Flask extension for handling Cross Origin Resource Sharing (CORS), making cross-origin AJAX possible.
Installation
Install the extension with using pip, or easy_install.
$ pip install -U flask-cors
Simple Usage
In the simplest case, initialize the Flask-Cors extension with default arguments in order to allow CORS for all domains on all routes. Read More.
from flask import Flask
from flask_cors import CORS, cross_origin
app = Flask(__name__)
CORS(app)
#app.route("/")
def helloWorld():
return "Hello, cross-origin-world!"
Reference: https://flask-cors.readthedocs.io/en/latest/

Yeoman with grunt-connect-proxy returns a ECONNREFUSED error

I'm trying to add grunt-connect-proxy to a project that uses the latest yeoman + angular generator.
But, when hitting the road that should be forwarded (e.g. 127.0.0.1:9000/api/v1), the server returns this error:
An error has occurred: {"code":"ECONNREFUSED","errno":"ECONNREFUSED","syscall":"connect"}
I used a modified Gruntfile: same as here as recommended.
How I'm a supposed to debug this? Has anyone had a similar issue lately with grunt-connect-proxy?
I'm also interested in similar solutions for proxing api calls for an angular webapp, if you have any that actually work.
Thank you for helping me out! Much appreciated!
Gruntfile is fine, the error is caused by your backend, i.e. localhost:3000 (probably Rails).
If it is Rails, it's a CORS problem. You need to allow rails to accept connection from grunt server. Add 'rack-cors' to your gemfile and configure it like this in your development.rb config file:
config.middleware.use Rack::Cors do
allow do
origins 'localhost:9000'
resource '*', :headers => :any, :methods => [:get, :post, :options, :delete]
end
end
Check this link for detailed setup, it helped me to get it working.
Working with Angular.js and Rails
I was running into this issue while trying to develop locally using AngularJS with a Laravel backend.
I was able to get the grunt-conenct-proxy example working using the before mentioned Rails example: Working with Angular.js and Rails, however when trying to connect locally to my laravel backend I would always get the ECONNREFUSED error.
The only real difference I could see was that the rails server was spinning up on 0.0.0.0. where as my apache server was running on localhost/127.0.0.1. After changing my config for my apache sever to run at 0.0.0.0 using:
php artisan serve --host 0.0.0.0
I was the able to connect to my backed without issue. If anyone could comment and explain how/why this works I would be very grateful.
Well, I finally got it to work somehow.
But frankly, I don't get it. It was definitely related to localhost not being the same as 127.0.0.1. How did it ended that way, I don't have a clue...
I'll share what I learned while doing this, as I found no real answer for debugging this staff out there on the web. Here is what I learned:
grunt has a --verbose option that may help you see what's going on
If grunt-connect-proxy is configured correctly you must see something like this at startup:
Running "configureProxies" task
Proxy created for: /api/v1 to localhost:8000
In my case, the grunt server was running on 127.0.0.1:9000, the second server on localhost:8000. I proxied requests made on api/v1 so grunt server --verbose would output something like this on every proxied request:
Proxied request: /api/v1/test -> http://localhost:8000/api/v1/test
{
"host": "127.0.0.1:9000",
"connection": "keep-alive",
"accept": "...",
"user-agent": "...",
"accept-encoding": "...",
"accept-language": "...",
"cookie": "..."
}
the whole point of using a proxy is to avoid any unnecessary CORS configuration on your second server
This is because once deployed your frontend and backend code will coexist on the same server (= the grunt server is only needed at dev time). I wanted to stress that point, because #Ulugbek answer may suggest that you need CORS to make grunt-connect-proxy work.
the ECONNREFUSED error happens when your second server is not responding
In my case, I discovered that http://127.0.0.1:8000/api/v1/test was not responding even if http://localhost:8000/api/v1/test was. So, I messed around with the /etc/hosts a bit and solved the problem by making sure both endpoints were responding accordingly.
The same error may happen to you if the second server is not listening to the proxied endpoint.

Resources