Reputation: 13
Tl;dr I want to be able to use something like angular's http service, but all locally from my machine, so that I don't have to have a server running for it to work. My current error message is
Access to XMLHttpRequest at 'file:C:/myfilepaths/cgi-bin/movieSearch.pl?movie=Red' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
Is there an easy way to do this all locally?
The full story with code: I am currently trying to build a web app that will ask the user to enter a movie, the angular controller takes this query and passes it to a perl script which will scrape imdb to return possible movie matches to the user's entry. I've done similar things to this before and have working code: The frontend:
<div ng-app="myApp" ng-controller="Ctrl1">
<p> Input movies : </p>
<form ng-submit="loadMovie1()">
Movie 1 : <input type="text" ng-model="movie1">
<input type="submit" id="submit" value="Submit" />
</form>
<p>list={{list}}</p>
</div>
I am using Angular's http service:
$scope.loadMovie1 = function() {
if($scope.movie1){
//Take input and call the perl script to scrape imdb
var movieList = [];
$http.get("../cgi-bin/movieSearch.pl",{params: {movie: $scope.movie1}})
.then(function(response) {
movieList = response.data.record;
console.log(movieList);
//pass to next function
}, function (error){
console.log(error);
});
}
};
And finally, I use a perl web scraping library called Web::Scraper. The scraping works fine and for brevity I won't include the entire perl script. Sufficed to say that I get the data from imdb and format it into a JSON object using this piece of perl:
package rec;
sub new {
my $class = shift;
my $self = {
text => shift, #Fields returned by the query
link => shift,
};
my $self2 = {
record => $self #Every SQL Data row will be anchored by 'record' as the root node
};
bless $self2, $class;
return $self2;
}
sub TO_JSON { return { %{ shift() } }; }
package main;
my $json_str = JSON->new->utf8;
$json_str->convert_blessed(1);
my $temp;
my $e;
my $JSON;
my $master_json;
my $final_data = {};
#-----------------------------------------------
# Begin JSON Wizardry
#-----------------------------------------------
foreach my $row (\@returnMatrix)
{
$temp = encode_json(\@$row); # Pull a single row in comma separated format
$e = new rec(split(',',substr($temp, 1, -1))); # Pass the row to the rec class constructor
my $key = (keys %$e)[0]; # Get the key of that row (Will be "record" for every row)
$final_data->{$key} ||= []; # Push key onto final_data
push @{$final_data->{$key}}, $e->{$key};
}
$JSON = $json_str->encode($final_data); # Encode the full JSON object
print "$JSON\n"; #return the json object to the controller.
Now this all works great. But sadly, to my dismay, this won't work for me because I am not running a server, so angular's http service returns an error:
Access to XMLHttpRequest at 'file:C:/myfilepaths/cgi-bin/movieSearch.pl?movie=Red' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
I was hoping to not have to install or run a server all the time to make this work... I would like to run this all locally from my computer as its just a little side project. Is this possible to do in some other way? I'm open to suggestions.
Thanks very much K
Upvotes: 1
Views: 675
Reputation: 54323
The way you are trying to run your CGI script is wrong. CGI is the protocol between a web server and an application on the server. It always requires some kind of web server. You're trying to send an HTTP request to the file that contains the Perl program in your file system. That points to a fundamental misunderstanding of how web works.
My suggestion is to move away from CGI for this purpose, and run a small, stand-alone web application that brings its own web server. It will open a port and allow you to send web requests to it. It's portable, scale-able and much simpler to maintain.
If all you care about is getting this to work easily, either of the web frameworks Dancer2 or Mojolicious are perfectly suitable for your purpose. This answer will focus on Dancer2 because I am more familiar with it.
Start by installing Dancer2 from CPAN. You seem to be on Windows, so that's probably the cpanm
client that came with Strawberry Perl, or ppm
on your ActivePerl.
Create a file called app.pl (the name does not really matter) somewhere in your file system. Paste in the following code.
use strict;
use warnings;
use Dancer2;
use Web::Scraper;
set serializer => 'JSON';
get '/:name' => sub {
my ($self) = @_;
return find_movie(route_parameters->get('name'));
};
# This is your data model/source. Could also put this in
# an extra package and make it have a nice interface. That way
# you can easily wrap caching around it, or switch to a database.
sub find_movie {
my $name = shift;
# my $scraper = Web::Scraper->new;
# my $movie = $scraper->scrape();
# this would be a data structure
# return $movie;
return { foo => $name };
}
Dancer2->psgi_app;
Now open a terminal (cmd
) where you put that file, and run
$ plackup app.pl
HTTP::Server::PSGI: Accepting connections at http://0:5000/
You can now make requests to this. Use the URL http://localhost:5000/red
as an example.
You don't have to worry about the JSON stuff either, the set serializer
bit will take care of sending the correct content type back for you and convert the Perl data structure to JSON automatically.
It's now also trivial to use something like Dancer2::Plugin::Cache::CHI to cache results from your web scrape search.
If you want more information about PSGI and why CGI is not the right tool for what you're doing, here are some links:
Upvotes: 1