Post document to SOLR 4 from AngularJS

2019-02-22 11:55发布

问题:


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!

回答1:

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!



回答2:

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.