I'm deep in the rabbit hole with this. I'm creating a simple app that uses SOLR 4 as a NoSQL datastore and AngularJS v1.2.2 for the interface. I've loaded a bunch of documents from the command line and AngularJS makes it very easy to search/view these. I want to permit document editing but can't get the POST working. Chrome console shows 400 errors and the Network tab shows it's failing on OPTIONS method.
Network Headers:
Request URL:http://localhost:8983/solr/mrm_assay/update
Request Method:OPTIONS
Status Code:400 Bad Request
Request Headers
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8,es;q=0.6
Access-Control-Request-Headers:accept, content-type
Access-Control-Request-Method:POST
Cache-Control:no-cache
Connection:keep-alive
Host:localhost:8983
Origin:http://localhost:63342
Pragma:no-cache
Referer:http://localhost:63342/angular-solr2/app/index.html
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36
Response Headers
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:origin, content-type, cache-control, accept, options
Access-Control-Allow-Methods:GET,POST,DELETE,PUT,HEAD,OPTIONS
Access-Control-Allow-Origin:http://localhost:63342
Access-Control-Max-Age:1800
Content-Type:application/xml; charset=UTF-8
Transfer-Encoding:chunked
Quick overview of architecture:
Both SOLR and AngularJS apps are running on my Mac.
SOLR is using the default Jetty instance and the Angular app runs within the server from WebStorm's IDE. CORS is enabled and includes GET,POST,DELETE,PUT,HEAD,OPTIONS.
Updates work when:
- Using SOLR's dashboard
- Using command line (example)
$ curl http://localhost:8983/solr/mrm_assay/update -H 'Content-type:application/json' -d '[ {
"keep_in_assay" {"set" : "N",
"detected_endog": "N",
"problems_w_is": "removed b/c requires addition post-Oasis",
"onc_std_set": "set1",
"fraction_id": "7",
"onclistgene": "UBL3",
"geneid": "UBL3_IS6",
"taxonid": "9606",
"peptide": "SSNVPADMINLR",
"degen_human": "1",
"gene_degeneracy": "1",
"percnt_id": "66.7",
"uuid": "6d20eb03-d3ee-4eb2-bc16-27cfaabab989"
} ]'
My Angular controller code looks like this:
assayControllers.controller('AssayUpdateController', ['$scope', '$http',
function($scope, $http){
$scope.processForm = function(){
console.log($scope.assay);
$http({
method:'POST',
url:'http://localhost:8983/solr/mrm_assay/update',
data : $scope.assay,
headers : {'Content-Type': 'application/json' }
})
.success(function(data, status, headers){
console.log(data.message);
})
.error(function(data, status, headers, config){
console.log(status);
});
};
}
]);
Data is successfully sent from the form as I can see it on the console (although the JSON object isn't packaged in an array, which SOLR seems to expect...I also tried to push JSON-formatted data to an array and POST that but no luck)
I appreciate your help - even if it's just to direct my troubleshooting!
The answer on how to update an individual document to SOLR v4.7 using AngularJS v1.2.2 is multi-part. This has only been tested on my localhost!
I. Configure CORS on Jetty server that ships with SOLR
solr-4.7.0/example/solr-webapp/webapp/WEB-INF/web.xml
Notes: CrossOriginFilter has a parameter, chainPreflight, that is set to true by default. This needs to be set to false. This was the key to CORS forwarding POST instead of OPTIONS to SOLR.. Also, order matters!
<filter>
<filter-name>cross-origin</filter-name>
<filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class>
<init-param>
<param-name>allowedOrigins</param-name>
<param-value>http://localhost*</param-value>
</init-param>
<init-param>
<param-name>allowedMethods</param-name>
<param-value>GET,POST,DELETE,PUT,HEAD,OPTIONS</param-value>
</init-param>
<init-param>
<param-name>allowedHeaders</param-name>
<param-value>origin, content-type, cache-control, accept, options, authorization, x-requested-with</param-value>
</init-param>
<init-param>
<param-name>supportsCredentials</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>chainPreflight</param-name>
<param-value>false</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>cross-origin</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
II. SOLR expects payload in array format, so you need to push Angular object into one. Basically, $scope.data becomes [$scope.data]
assayControllers.controller('AssayUpdateController', ['$scope', '$http', '$location',
function($scope, $http, $location){
$scope.processForm = function(){
$http.post("http://localhost:8983/solr/mrm_assay/update?commit=true", [$scope.assay])
.success(function(data, status, headers){
console.log(data.message);
$location.path("/assays")
})
.error(function(data, status, headers, config){
console.log(status);
});
};
}
]);
III. SOLR documents contain version field that can cause "conflict" errors on POST. This is due to a cache issue I wasn't able to track down (curl showed current value but browsers had old one; even on force refresh). Anyway, this is of more use to SOLR than me so the best bet is to not return it to the client in the first place. I don't think field exclusion is an option so I whitelisted all the fields I wanted to return in solrconfig.xml
<!-- A request handler that returns indented JSON by default -->
<requestHandler name="/query" class="solr.SearchHandler">
<lst name="defaults">
<str name="echoParams">explicit</str>
<str name="wt">json</str>
<str name="indent">true</str>
<str name="df">text</str>
<str name="fl">uuid,keep_in_assay,detected_endog,problems_w_is,onc_std_set,fraction_id,detected_by_orbi_experiments,onclistgene,geneid,taxonid,peptide,\
degen_human,degen_mouse,gene_degeneracy,department,protein_ac,pepid,protid,percnt_id,peptide_ordered</str>
Now the app works like a charm!
If you are failing on OPTIONS request then it is CORS that's causing problems. Most likely because the jetty that comes with Solr does not support (or is not configured) with one. The latest version of Jetty has one, but it still needs to be configured.
So, you can disable CORS on Solr requests or enable it in Jetty. Upgrading Jetty if the version shipped does not support it.
However, you also have a second problem. Solr should not be exposed directly to the internet but is supposed to run behind a middleware client or - worst case - a heavily restricting proxy. Otherwise, anybody can connect directly to your instance and delete the data or do other damage. Solr is not even security tested for direct internet access. So keep that in mind before you get too far down 'Solr-Angular' direct bridge.