CORS in rails-api and angularJs - ruby-on-rails

I've been trying out all kinds of solutions from the countless other questions on this topic, without any luck...
I'm trying to setup a rails-api project, with a front-end in AngularJs. They will be in different domains. I can make GET requests without any problem. But when I try to do a PUT, I get on Chrome console:
XMLHttpRequest cannot load http://0.0.0.0:3000/thingies/1.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:9000' is therefore not allowed access
Here's what my failed request looks like:
Remote Address:0.0.0.0:3000
Request URL:http://0.0.0.0:3000/thingies/1
Request Method:OPTIONS
Status Code:404 Not Found
Request Headersview source
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8,ja;q=0.6,ko;q=0.4,pt;q=0.2,ro;q=0.2,zh-CN;q=0.2
Access-Control-Request-Headers:accept, content-type
Access-Control-Request-Method:PUT
Cache-Control:no-cache
Connection:keep-alive
Host:0.0.0.0:3000
Origin:http://localhost:9000
Pragma:no-cache
Referer:http://localhost:9000/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36
Response Headersview source
Connection:Keep-Alive
Content-Length:17164
Content-Type:text/html; charset=utf-8
Date:Tue, 26 Aug 2014 06:48:06 GMT
Server:WEBrick/1.3.1 (Ruby/2.1.1/2014-02-24)
X-Request-Id:xxxxxx-xxxxxxx-xxxxxx-xxxxxxxxxx
X-Runtime:0.027031
Here's my app.js in AngularJs:
angular
.module('myApp', [
'ngCookies',
'ngResource',
'ngRoute',
'ngSanitize',
'restangular',
])
.config(function ($routeProvider,RestangularProvider, $httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common["X-Requested-With"];
$httpProvider.defaults.headers.common["Accept"] = "application/json";
$httpProvider.defaults.headers.common["Content-Type"] = "application/json";
$routeProvider
.when('/', {
templateUrl: 'views/home.html',
controller: 'HomeCtrl'
.otherwise({
redirectTo: '/'
});
RestangularProvider.setBaseUrl('http://0.0.0.0:3000');
});
This is in my Rails project:
application.rb:
class Application < Rails::Application
config.action_dispatch.default_headers.merge!({
'Access-Control-Allow-Origin' => '*',
'Access-Control-Request-Method' => '*'
})
end
My routes.rb, application_controller.rb and thingy_controller are the default scaffolded files. (I've tried modifying them according to other solutions for CORS, without any luck.)
I've used POSTMAN (Chrome Extension) as well, to see if I could use PUT. The requests went through without problems.
I'd really appreciate if someone could point me in the right direction with this!

Try add { 'Content-Type': 'application/x-www-form-urlencoded' } in your $http request like that
$http({
method : 'POST',
url : 'backend.json',
data : $.param($scope.yourFormData), // pass in data as string, important!
headers: { 'Content-Type': 'application/x-www-form-urlencoded' } // set the appropriate headers
})

The Problem is that CORS is blocking your requests. You have to provide the Service and the Application on the same host with the same port. To avoid this problem you could use a Proxy to "map" the url of the service to the url of your application.
Cheers
Nighthawk

Related

Unable to modify HttpHeader during HttpClient POST in Angular 5

Using Angular 5 (http://localhost) and making HttpClient POST request to a REST service hosted on another server (http://192.168.200.75:5555).
Getting following error:-
Failed to load http://192.168.200.75:5555/rest/pub/ws/provider/requestToCSAF/: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
Service class looks like
import {Injectable} from '#angular/core';
import {HttpClient, HttpHeaders} from '#angular/common/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import {Observable} from 'rxjs/Observable';
import {RequestJSON} from './requestJSON';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json', 'Access-
Control-Allow-Origin': '*' })
};
#Injectable()
export class MyService {
private serviceUrl
= 'http://192.168.200.75:5555/rest/pub/ws/provider/requestToCSAF/';
constructor(private httpClient: HttpClient) {
}
postRequest(json: RequestJSON): Observable<RequestJSON> {
const _options = {headers: new HttpHeaders({'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*'}) };
return this.httpClient.post(this.serviceUrl, json, httpOptions)
.catch((error: any) => Observable.throw(error.json().error || 'Server error'));
}
}
But request header looks as follow and throw No 'Access-Control-Allow-Origin' header is present on the requested resource error message:-
OPTIONS rest/pub/ws/provider/requestToCSAF/ HTTP/1.1
Host: 192.168.200.75:5555
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Access-Control-Request-Headers: access-control-allow-origin,content-type
Accept: */*
DNT: 1
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Referred: https://angular.io/tutorial/toh-pt6
On the REST service side, you should also add the header "Access-Control-Allow-Origin" with the value of the client server(http://localhost), so that the service will allow calls from the client.
you can find more details about cross-site requests here:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Server-Side_Access_Control

API Endpoint only allows GET when it should take POST

I am building a React.js front-end portion of a web application using an already existing back-end that is built with ASP.NET MVC Framework. The method in the API's controller is named Post (which should default it to a post method) and even has the [HttpPost] decorator. I have recently enabled CORS, but I am not sure if that matters.
The API also has Swagger (it's like postman if you haven't used it before) enabled, and when I call the method as POST from the swagger, it will work and return the correct data.
However, when I try to call the method from my UI component, the API endpoint will give a 405 Method Not Allowed status code.
It says that the method only allows GET, which is baffling since it is supposed to only allow POST and it works that way on swagger or if I manually travel to the URL using a browser. Has anyone had a problem like this using React components or working with ASP.NET MVC/Web API 2?
I am completely stuck...
EDIT:
I am adding the API's method for the post endpoint.
[Route("{id}/merchant/SearchMerchants")]
[HttpPost]
[AuthorizeUserPermissionsToken(Cookie)]
public Response Post(Request rq)
{ // I put a breakpoint here to debug
// some code
}
I had to redact a lot of the code but here is the post method.
But I don't think it has to do with the API endpoint.
This is because when I debug, it will not even reach the breakpoint or call the method at all, when I am using the UI. However, it will reach the breakpoint when I travel to the endpoint on the browser or use swagger.
EDIT 2:
EDIT 3: (removed EDIT 2 and moved the original headers to bottom to reduce confusion)
Pre-flight OPTIONS request headers
Accept:*/*
Accept-Encoding:gzip, deflate
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:POST
Cache-Control:no-cache
Connection:keep-alive
Host: *redacted domain*
Origin:http://localhost:49702
Pragma:no-cache
Referer:http://localhost:49702/FindMerchants
User-Agent:Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Mobile Safari/537.36
response headers to OPTIONS request:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept
Access-Control-Allow-Origin:http://localhost:49702
Allow:OPTIONS, TRACE, GET, HEAD, POST
Content-Length:0
Date:Mon, 07 Aug 2017 18:41:42 GMT
Public:OPTIONS, TRACE, GET, HEAD, POST
Server:Microsoft-IIS/10.0
X-Content-Type-Options:nosniff
X-Frame-Options:SAMEORIGIN
then the real POST request headers after pre-flight:
Accept:*/*
Accept-Encoding:gzip, deflate
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Connection:keep-alive
Content-Length:154
Content-Type:application/json
Cookie: *redacted a bunch of cookies*
Host:a *redacted domain*
Origin:http://localhost:49702
Pragma:no-cache
Referer:http://localhost:49702/FindMerchants
User-Agent:Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Mobile Safari/537.36
The real response headers are like so:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept
Access-Control-Allow-Origin:http://localhost:49702
Allow:GET
Cache-Control:no-cache
Content-Length:73
Content-Type:application/json; charset=utf-8
Date:Mon, 07 Aug 2017 17:58:28 GMT
Expires:-1
Pragma:no-cache
Server:Microsoft-IIS/10.0
X-Content-Type-Options:nosniff
X-Frame-Options:SAMEORIGIN
EDIT 4:
Request URL:*redacted domain*/380/merchant/SearchMerchant
Request Method:POST
Status Code:405 Method Not Allowed
Remote Address:127.0.0.1:80
Referrer Policy:no-referrer-when-downgrade
EDIT #9999 (jk):
My react component simply calls a custom data service that I wrote, which has a post method like this.
post(relativeUrl, data, apiVersion) {
var url = this.getUrl(relativeUrl, apiVersion);
var res = $.ajax({
type: 'POST',
url: url,
contentType: 'application/json',
xhrFields: {
withCredentials: true
},
crossDomain: true,
data: JSON.stringify(data)
});
return res;
}
I simply pass relativeUrl to fetch a list related to a user account.
Before I make a call that fails (which shows 405), I use this same method to log in and update the global redux state for the app. After updating the global state, I render a different component and use this method again with another relativeUrl to fetch the list. Then I get the error. I also make sure that the login call is finished and returns and updates the state correctly before moving on.
To follow my comment,
in web.config file:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Methods" value="POST, GET, OPTIONS, HEAD" />
</customHeaders>
</httpProtocol>

Loading font fron CloudFront giving No 'Access-Control-Allow-Origin' header is present on the requested resource

First, I know there are tons of similar issues on StackOverflow and elsewhere. I tried them for days with no luck. So sorry if this sounds duplicate. Correct me if it is actually.
I have Heroku Rails app loading font from CloudFront. Everything else seems to load fine except that font. I get
Access to Font at
'https://domainname.herokuapp.com/assets/LigatureSymbols-2.11-722bc1af51c5e458d52834d798e4c0e8.ttf'
from origin 'null' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'null' is therefore not allowed access.
It says origin null, and access-control-allow-origin is not present. However this is the request and response data:
Request URL:https://domainname-herokuapp-com.global.ssl.fastly.net/assets/LigatureSymbols-2.11-722bc1af51c5e458d52834d798e4c0e8.ttf
Request Method:GET
Status Code:302 Found
Remote Address:151.101.0.249:443
Referrer Policy:no-referrer-when-downgrade
Response Headers
view source
Accept-Ranges:bytes
Access-Control-Allow-Origin:*
Age:52
Connection:keep-alive
Content-Length:0
Content-Type:text/html; charset=utf-8
Date:Sun, 16 Apr 2017 10:40:37 GMT
Location:https://domainname.herokuapp.com/assets/LigatureSymbols-2.11-722bc1af51c5e458d52834d798e4c0e8.ttf
Server:gunicorn/19.7.1
Via:1.1 varnish
Via:1.1 vegur, 1.1 vegur
X-Cache:HIT
X-Cache-Hits:1
X-Content-Type-Options:nosniff
X-Frame-Options:SAMEORIGIN
X-Served-By:cache-hhn1527-HHN
X-Timer:S1492339237.333330,VS0,VE0
Request Headers
view source
Accept:*/*
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Host:domainname-herokuapp-com.global.ssl.fastly.net
**Origin:https://staging.domainname.com**
Referer:https://domainname.herokuapp.com/assets/application-d2b73eea67437c851b6e71803bad2a6c.css
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Name
?store=true&search_type=posts
css?family=Lato:400,300,700
MgNNr5y1C_tIEuLEmicLmwLUuEpTyoUstqEm5AMlJo4.woff2
LigatureSymbols-2.11-722bc1af51c5e458d52834d798e4c0e8.ttf
MDadn8DQ_3oT6kvnUq_2r_esZW2xOQ-xsNqO47m55DA.woff2
icon-close-89c814d2fa53dff86b00936a7a8c836e.png
icon-bell-91b2c7d5db27d9fc9e8325dda55e73c0.png
icon-comment-35728eedd10ff36bc89c7a977ed67764.png
Origin is defined, and it should be allowing all origins. What am I doing wrong?

expires_in max-age cache control doesn't work

I can't get the max-age cache control to work properly,
I have used expires_in that resulted in a "Cache-Control:max-age=86400, public, must-revalidate" header.
But yet, the browser still sends the request to the server, at least it is defined as "304 not modified", meaning that the ETag/If-None-Match headers works properly.
I have tested it with webrick on my localhost and on heroku,
with chrome 45 and Safari.
And no, my development tools are not opened, and the "disable cache" is not checked.
I also have tried to remove the ", must_revalidate: true" of the expires_in method call.
What am I missing?
Here is the output from the network in chrome:
General:
Remote Address:127.0.0.1:3000
Request URL:http://localtest.me:3000/api/books
Request Method:GET
Status Code:304 Not Modified
Response Headers:
Access-Control-Allow-Origin:*
Access-Control-Request-Method:*
Cache-Control:max-age=86400, public, must-revalidate
Connection:Keep-Alive
Date:Tue, 08 Sep 2015 13:28:01 GMT
Etag:W/"1f1b2d0b822830bc74e7c47a116205be"
Server:WEBrick/1.3.1 (Ruby/2.2.1/2015-02-26)
X-Content-Type-Options:nosniff
X-Frame-Options:SAMEORIGIN
X-Request-Id:c70d4715-dcff-4558-85af-9d21556d406a
X-Runtime:0.553353
X-Xss-Protection:1; mode=block
Request Headers:
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8,fr;q=0.6,he;q=0.4
Cache-Control:max-age=0
Connection:keep-alive
Host:localtest.me:3000
If-None-Match:W/"1f1b2d0b822830bc74e7c47a116205be"
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36
And here is the ruby code:
before_action :cache_control, only: [:index, :show]
def cache_control
expires_in 1.day, public: true, must_revalidate: true
end
Well, the headers are fine, it is just a meter of how browsers treats the reload button or shortcut keys.
I found the answer here: https://stackoverflow.com/a/16510707/789658
Here are the findings in chrome:
Pressing Enter on the address bar respects the "max-age" and doesn't send a request to the server.
The reload button or Cmd+R sends a request to the server with If-Modified-Since and will return 304 if resource wasn't modified since...
Cmd+Shift+R sends a request to the server without even If-Modified-Since

Response Permanent Redirect in MVC not issuing 301 or changing URL

I am converting an old ASP.NET web forms site to ASP.NET MVC 5. I would like to issue permanent redirects for the old page URLs.
Here is what I have done -
RouteConfig.cs:
routes.MapRoute("About_old",
"About/About.aspx",
new { controller = "Home", action = "About_old" });
HomeController.cs:
public ActionResult About_old()
{
return new RedirectResult("/About", true);
// I've also tried
// return RedirectToActionPermanent("About");
// return RedirectPermanent("/About");
}
All attempts load the correct /About view, however the URL does not change, and I do not see a 301 code in the response. In other words, the URL is "localhost/About/About.aspx" and I expect it to be "localhost/About"
Complete Request/Repsonse from Chrome:
Request URL:http://localhost:55774/About/About.aspx
Request Method:GET
Status Code:200 OK
Request Headers
GET /About/About.aspx HTTP/1.1
Host: localhost:55774
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Response Headers
Cache-Control:private
Content-Encoding:gzip
Content-Length:2284
Content-Type:text/html; charset=utf-8
Date:Sat, 01 Mar 2014 18:10:41 GMT
Server:Microsoft-IIS/8.0
Vary:Accept-Encoding
X-AspNet-Version:4.0.30319
X-AspNetMvc-Version:5.1
X-Powered-By:ASP.NET
Nowhere do I see a 301 and the URL does not change. I have to think this has to do with how I am mapping the route of the old aspx page in RouteConfig.cs as all action methods have the same results. NOTE I have put a solution using global.asax below, however I would prefer it to work as I am attempting above, so I have not accepted my answer.
Am I doing something wrong or just missing something? How do I get the 301 to issue and URL to change?
Here is my solution (Global.asax)
protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e)
{
string currentUrl = HttpContext.Current.Request.Path.ToLower();
if (currentUrl.EndsWith("/about/about.aspx"))
{
Response.Status = "301 Moved Permanently";
Response.AddHeader("Location", "/About");
Response.End();
}
}
From answer here: Global 301 redirection from domain to www.domain

Resources