原文地址:http://simplapi.wordpress.com/2013/04/10/jersey-jax-rs-implements-a-cross-domain-filter/
After seeing how to create a HTTP Basic Auth Filter, we will add this time a cross domain output filter.
Cross-Domain request is available threw CORS system, it allow developper to send request to some domains which are not directly related to their HTML page, this is one of the great new features of new Xhr level 2 (already available in most of browsers).
It’s a pretty usefull feature in many case, here we will show a simple example how to embed a REST Web Services into an app (in pure HTML/JS), without any transfert threw a sub-resource provider/proxy.
Understanding the Jersey filter
We already see in previous article the filter for input request, here it’s the opposite : we want to add support on output filter.
Basically, Jersey allow to add any number of filter at input level, or, output level. We will use here the output filter.
Like previous article, we will describe both : Tomcat (v7), and Java built in server.
Java built-in server
ResourceConfig rc = new PackagesResourceConfig("");
rc.getProperties().put(
"com.sun.jersey.spi.container.ContainerResponseFilters",
"com.sun.jersey.api.container.filter.LoggingFilter;com.myprogram.CrossDomainFilter"
);
HttpServer server = HttpServerFactory.create("http://localhost:9999/", rc);
server.start();
Tomcat
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>com.sun.jersey.api.container.filter.LoggingFilter;com.myprogram.CrossDomainFilter</param-value>
</init-param>
Quite the same as previous article, just the parameter now is named "ContainerResponseFilters" instead of "ContainerRequestFilters" indicate to Jersey we want this time use the output not the input. Again the "LoggingFilter" is a basic -already done- filter provided by Jersey, allowing to get a nice debug console of input/output.
Now we put our class "com.myprogram.CrossDomainFilter" in the filter chain, we can create it.
The output filter
The output filter is a little bit different, but like input filter quite easy to understand :
/**
* Allow the system to serve xhr level 2 from all cross domain site
*
* @author Deisss (LGPLv3)
* @version 0.1
*/
public class CrossDomainFilter implements ContainerResponseFilter {
/**
* Add the cross domain data to the output if needed
*
* @param creq The container request (input)
* @param cres The container request (output)
* @return The output request with cross domain if needed
*/
@Override
public ContainerResponse filter(ContainerRequest creq, ContainerResponse cres) {
cres.getHttpHeaders().add("Access-Control-Allow-Origin", "*");
cres.getHttpHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
cres.getHttpHeaders().add("Access-Control-Allow-Credentials", "true");
cres.getHttpHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
cres.getHttpHeaders().add("Access-Control-Max-Age", "1209600");
return cres;
}
}
As you see, every parameter are in String way, in fact you can put what you want.
Here is a basic explain about thoose parameters :
- Origin : indicate which url is allowed or not. The * is the wildcard parameter, you can put for example http://localhost if you want to restrict to localhost only
- Headers : you probably don’t need to change this one, it’s indicating what headers you will use. There is no wildcard for this one
- Credentials : let it to true will be fine…
- Methods : even if you don’t use head or options, you should let them like this : the CORS system send OPTIONS request to catch cross domain policy, if you don’t set it it will be refused
- Max-Age : the max age policy to renew CORS check. Here it’s 14 days long
As you see, thoose parameters are used BY BROWSER, not your Ajax request. It means that browser will check BEFORE your request. And you can’t pass threw a wrong policy check…
Now your Jersey part should be working, let’s make a simple request to check everything is fine.
The CORS Client
I will describe here a pretty basic example using jQuery (because it’s a lot used, definitely not my favorite…). Also, you may need to setup a auth filter for that part : allowing all OPTIONS request to be throw as "HTTP OK". If you need so, use this article. In this case, you need to escape all options, so at the beginning of the filter, add like this (see from methods.equals("OPTIONS")) :
@Override
public ContainerRequest filter(ContainerRequest containerRequest) throws WebApplicationException {
//GET, POST, PUT, DELETE, ...
String method = containerRequest.getMethod();
// myresource/get/56bCA for example
String path = containerRequest.getPath(true);
if(method.equals("OPTIONS")) {
throw new WebApplicationException(Status.OK);
}
I need to do that, i don’t remember exactly why (if I remember it was just a 404 error making the system to not validate CORS policy). This will be enough to get everything running good with any HTML page allowing CORS. Of course if you are using OPTIONS in your application, this trick is not for you, you should adapt it !
Now on client side, you can see we send a simple, and regular AJAX request, yes, the browser is the key here, you don’t need to setup anything special, the browser will check everything instead of you :
/**
* Call the server using ajax cross domain request compatible
*
* @author Deisss (LGPLv3)
*
*
* @param path {String} The url to get
* @param type {String} GET, POST, PUT, DELETE, ...
* @param data {Object} Any data to supply
* @param success {Function} The success method called in case of success
* @param error {Function} The error method called in case of error
*/
function call(path, type, data, success, error){
var l = login,
p = password;
$.ajax({
url: path,
data: (data) ? JSON.stringify(data) : "",
dataType: 'json',
type: type,
contentType: 'application/json; charset=UTF-8',
crossDomain: true,
//Prepare the authorization request from it
beforeSend : function(xhr) {
var base = Base64.encode(l+ ":" + p);
xhr.setRequestHeader("Authorization", "Basic " + base);
},
//Handle default error check
statusCode: {
400 : function(){
alert('400 : bad request');
},
401 : function(){
alert('401 : unauthorized');
},
403 : function(){
alert('403 : forbidden');
},
404 : function(){
alert('404 : not found');
},
415 : function(){
alert('415 : type not allowed');
},
500 : function(){
alert('500 : internal server error');
}
},
//Callback
success: (success) ? success : null,
error: (error) ? error : null
});
};
As you see… Nothing to do ! The cross domain is in fact used by the browser directly, the browser is the one which is taking care of policy. You just need to do regular ajax request. Here I also add the HTTP Basic Auth from previous article (so you can see how to do cross domain with Basic Auth).
From this example : don’t forget to use HTTPS in all case, because the HTTP Auth is not encrypted at all, so you should take care a lot about that…
Final Words
You are now ready to interact with Web Services threw HTML page directly, allowing to build your system faster by interacting directly with many of your Web Services !