Hallo zusammen,
von einer nicht Domino Web Site möchte ich Daten via REST api aus unserer Domino Welt abrufen. Da die Daten benutzerspezifisch sind (Leser-Felder), benötige ich einen authentifizierten Zugriff. Mit viel hin und her, habe ich das auch einigermaßen hinbekommen.
Auszug aus der html Datei in der externen Domain:
$( document ).ready( function() {
var userName = "xHubert Humpert/Hubi" ;
var password = "paderborn" ;
queryData = function( serviceUrl, requestParams ) {
var modul = "doGet" ;
try {
// check params
if( isEmpty( serviceUrl ) ) {
throw new Error( "missing or empty param serviceUrl" ) ;
}
if( isEmpty( requestParams ) ) {
throw new Error( "missing or empty param requestParams" ) ;
}
$.ajax({
type: "POST",
url: serviceUrl ,
data: JSON.stringify( requestParams ) ,
contentType: "application/json" ,
crossDomain : true ,
dataType: "json",
beforeSend: function( xhr ) {
xhr.setRequestHeader('Authorization', 'Basic ' + btoa(unescape( encodeURIComponent( userName + ':' + password ) ) ) ) ;
},
success: function( result ) {
console.log( "success" ) ;
console.log( result ) ;
},
failure: function( result ) {
console.log( "failure" ) ;
console.log( result ) ;
}
});
} catch (e) {
console.log( e ) ;
}
}
isEmpty = function( param ) {
var result = false ;
try {
if( param === undefined || param === null || param === "" ) {
result = true ;
}
} catch ( e ) {
console.log( e ) ;
}
return result ;
}
$( "#sendRequest" ).on( "click", function() {
var serviceUrl = "http://dev01/RESTAuth.nsf/api.xsp/Test" ;
var requestParams = {
action : "getTestData"
}
queryData( serviceUrl, requestParams ) ;
}) ;
}) ;
Wie man erkennen mag, sende ich die Daten zur Basic Authentification in meinem ajax request mit.
Die xpage, die den Aufruf entgegen nimmt und dann an die service bean weiterleitet:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xe="http://www.ibm.com/xsp/coreex"
rendered="false">
<xe:restService
id="restService6"
pathInfo="Test">
<xe:this.service>
<xe:customRestService
contentType="application/json"
serviceBean="de.egvpb.termin.Test">
</xe:customRestService>
</xe:this.service>
</xe:restService>
</xp:view>
Hierzu sei gesagt, dass für Anonymous in der ACL no access mit read public documents gesetzt ist. Für die xpage api.xsp ist public access erlaubt.
Und schließlich noch die Service Bean:
package de.egvpb.termin;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList ;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import com.ibm.commons.util.StringUtil;
import com.ibm.domino.services.ServiceException;
import com.ibm.domino.services.rest.RestServiceEngine;
import com.ibm.xsp.extlib.component.rest.CustomService;
import com.ibm.xsp.extlib.component.rest.CustomServiceBean;
import com.ibm.xsp.model.domino.DominoUtils;
public class Test extends CustomServiceBean {
public void renderService(CustomService service, RestServiceEngine engine) throws ServiceException {
try {
HttpServletRequest request = engine.getHttpRequest() ;
HttpServletResponse response = engine.getHttpResponse();
response.setHeader("Access-Control-Allow-Origin", "*") ;
response.setHeader("Access-Control-Allow-Methods", "OPTIONS, POST" ) ;
response.setHeader("Access-Control-Allow-Headers", "Content-Type, authorization" ) ;
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", -1);
response.setContentType( "application/json" ) ;
String method = request.getMethod() ;
System.out.println( "Test.renderService, method = " + method );
if (method.equals("OPTIONS")) {
// wird bei Authentifizierung aufgerufen
response.getWriter().write( "options durchlaufen" );
response.getWriter().close();
} else if (method.equals("POST")) {
this.doPost(request, response);
}
} catch ( Exception e ) {
e.printStackTrace() ;
}
}
private void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
String result = null ;
String postedData = this.convertStreamToString( request.getInputStream() ) ;
JSONObject jsonIn = (JSONObject) new JSONParser().parse( postedData ) ;
String action = (String) jsonIn.get("action") ;
if( action.equals("getTestData") ) {
result = this.getTestData( request ) ;
} else {
String errorMessage = "Test.doPost, undefined param action = " + action ;
result = this.getParamErrorMessageString(errorMessage) ;
}
response.getWriter().write( result );
response.getWriter().close();
} catch (Exception e) {
e.printStackTrace() ;
}
}
@SuppressWarnings("unchecked")
private String getTestData( HttpServletRequest request ) {
JSONObject result = new JSONObject() ;
try {
result.put( "isError", false ) ;
result.put( "message", "result from rest service, user = " + DominoUtils.getCurrentSession().getEffectiveUserName() ) ;
} catch (Exception e) {
result.put( "isError", true ) ;
result.put( "errorMessage", "Error in REST service" ) ;
e.printStackTrace() ;
}
return result.toJSONString() ;
}
/**
* Helper functions to read URL parameters
*/
@SuppressWarnings("unused")
private static Boolean getParameterAsBoolean(HttpServletRequest request, String parameter, boolean defaultValue) {
Boolean result = null ;
try {
result = Boolean.valueOf(getParameter(request, parameter, String.valueOf(defaultValue)));
} catch (Exception e) {
e.printStackTrace() ;
}
return result ;
}
private static String getParameter(HttpServletRequest request, String parameter, String defaultValue) {
String result = "" ;
try {
String pm = request.getParameter(parameter);
if (StringUtil.isEmpty(pm)) {
return defaultValue;
}
result = pm;
} catch (Exception e) {
e.printStackTrace() ;
}
return result ;
}
@SuppressWarnings("unused")
private static Integer getParameterAsInt(HttpServletRequest request, String parameter, int defaultValue) {
Integer result = 0 ;
try {
String pm = request.getParameter(parameter);
if (StringUtil.isEmpty(pm)) {
return defaultValue;
}
result = Integer.valueOf(pm);
} catch (Exception e) {
e.printStackTrace() ;
}
return result ;
}
private String convertStreamToString(InputStream stream) throws IOException {
String result = "" ;
try {
InputStreamReader inputStreamReader = new InputStreamReader( stream, "UTF8" ) ; // Param UTF8 für korrekte Umlaute!!!
BufferedReader reader = new BufferedReader( inputStreamReader );
StringBuffer buffer = new StringBuffer();
String line = "";
while( (line = reader.readLine()) != null ){
buffer.append(line);
}
reader.close();
result = buffer.toString() ;
} catch (Exception e) {
e.printStackTrace() ;
}
return result ;
}
/**
* Fehler protokollieren und als JSON String zurückgeben
* @param errorMessage
* @return
*/
@SuppressWarnings("unchecked")
private String getParamErrorMessageString( String errorMessage ) {
String result = "" ;
try {
JSONObject jResult = new JSONObject() ;
jResult.put( "isError", true ) ;
jResult.put( "errorMessage", errorMessage ) ;
result = jResult.toJSONString() ;
} catch (Exception e) {
e.printStackTrace() ;
}
return result ;
}
}
Falls die Credentials korrekt sind, wird mit der success function des ajax requests der Name des authentifizierten Benutzers ausgegeben. Falls nicht, erhalte ich auf der Konsole des Browsers die Meldung
Access to XMLHttpRequest at 'http://dev01/RESTAuth.nsf/api.xsp/Test' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
jquery-3.3.1.min.js:2 POST http://dev01/RESTAuth.nsf/api.xsp/Test net::ERR_FAILED
Meine Fragen:
- kann / sollte man das so machen?
- wie kann man die CORS-Fehlermeldung durch eine sprechendere Variante ersetzen bzw. vorab eine Prüfung realisieren?
Ich bitte um kritische Würdigung.
Gruß
Hubert