2

I'm trying to build a webservice (Java based server and Javascript base client) I only need to send a Post request with json data and I need to get a post response with json data from server.Since client and server have different domains I think cors need to be supported. Up to now, I've implemented these: (My client implementaion is almost same with html5 rocs tutorial)

Web service client (js):

    // I call client = new WSclient() in one of my js files   
    WSclient=function(){
      makeCorsRequest();
    }

    // Create the XHR object.
    function createCORSRequest(method, url) {
      var xhr = new XMLHttpRequest();
      if ("withCredentials" in xhr) {
        // XHR for Chrome/Firefox/Opera/Safari.
        xhr.open(method, url, true);
      } else if (typeof XDomainRequest != "undefined") {
        // XDomainRequest for IE.
        xhr = new XDomainRequest();
        xhr.open(method, url);
      } else {
        // CORS not supported.
        xhr = null;
      }
      return xhr;
    }

    // Helper method to parse the title tag from the response.
    function getTitle(text) {
      return text;
    }

    // Make the actual CORS request.
    function makeCorsRequest() {
      // All HTML5 Rocks properties support CORS.
      var url = 'http://localhost:8080/myapp/myfunction';
      var xhr = createCORSRequest('POST', url);
      xhr.setRequestHeader(
          'X-Custom-Header', 'value');
      xhr.send();
    }

Web service server (java)

@Path("/myapp/")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class myFunctionClass {


    @POST
    @Path("myfunction")
    public Response recommendations(User inf){
        // From the client I also need to send json 
        // like {"name":"john","surname":"smith","name":"marry","surname":"smith"}
        // and if possible I want to put this infformation inside inf object
        List<String> infos = inf.getInformation();

         // here I call one of the my methods to get recommendations
         // I remove it for simplicity and just put type of recommendations object 
         // list<Recommendation> recommendations= runFunction(infos);

        final StringWriter sw =new StringWriter();
        final ObjectMapper mapper = new ObjectMapper();
        mapper.writeValue(sw, recommendations);
        System.out.println(sw.toString());
        sw.close(); 


        return Response.ok(sw.toString(), MediaType.APPLICATION_JSON).header("Access-Control-Allow-Origin", "*")
                .header("Access-Control-Allow-Methods", "POST").allow("OPTIONS").build();


    }
}

However, I think I need to do something more because when I run these, I got

    XMLHttpRequest cannot load      http://localhost:8080/myapp/myfunction. 
No 'Access-Control-Allow-Origin' header is present on the requested resource. 
Origin 'http://localhost:3000' is therefore not allowed access.

So what should I add to my server code to get rid of this error? Besides, how could I send json data inside my request in client? This is my first time dealing with such issues therefore, If my question a bit absurd, sorry about that.

EDIT

When I remove

 xhr.setRequestHeader(
              'X-Custom-Header', 'value');  

part from the client, It works properly. As I said before, this is my first time with web-services and javascript so actually I dont know what does this line of code. Could anyone explain me what happens if it exists or not?

EDIT2

I understood that, I need to put

xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");

in order to send a json with request.But when I added this, same errors come back. What should I add to server to achive this ?

zwlayer
  • 1,752
  • 1
  • 18
  • 41

1 Answers1

2

What's happening is that there is a preflight request (which is an OPTIONS request), made before the initial request. So you need an @OPTIONS endpoint to handle that request (i.e. set the response header). In your current code, you are trying to set it in the original requested endpoint response, where the OPTIONS request won't even reach.

A more common approach, instead of creating an @OPTIONS endpoint for each target, just use a Jersey filter as seen in this answer. The headers will get sent out for all request.

See Also:


EDIT

Example @OPTIONS

@OPTIONS
@Path("myfunction")
public Response cors() {
    return Response.ok()
        .header("Access-Control-Allow-Origin", "*")
        .header("Access-Control-Allow-Methods",
            "GET, POST, PUT, DELETE, OPTIONS, HEAD")
        // whatever other CORS headers
        .build();
}
Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • thank you for information but as I'm beginner I could not understand your answer clearly, if you have time, could you explain it with simple words ? or could you show me where should I change ? – zwlayer Aug 16 '15 at 11:38
  • Go the second filter under "Jersey 1". You will see `resourceConfig.getContainerResponseFilters().add(new CORSFilter());` – Paul Samsotha Aug 16 '15 at 11:44
  • By "target" I mean URI. If you only have one URI for the entire application, then just create an endpoint with the same URI annotated with `@OPTIONS` and return a empty OK response with the CORS headers. Otherwise if you have more than one URI that needs CORS support, better just use the filter – Paul Samsotha Aug 16 '15 at 11:49
  • This is not a help desk. It is a Q&A site. You ask one question, if you get it answered, and you have another question, then post another question. – Paul Samsotha Aug 16 '15 at 14:54
  • 1
    Your new problem is not related to CORS. You are now getting a 400 which is a different problem unrelated to CORS and it is a bad request on the client part. Please post a different question – Paul Samsotha Aug 16 '15 at 14:57
  • Aside from that, I highly suggest you use the filter, rather than the @OPTIONS method. I can see possibilities of failure still with the method. With the filter, you are sure to get the CORS response headers. – Paul Samsotha Aug 16 '15 at 15:10
  • You might want to look at the response body. I imagine there is some message that might explain it. That's why the Content-Type is text/plain – Paul Samsotha Aug 16 '15 at 15:19
  • Please try it with the filter. If you still get an error, you should post the error message on your new question. I saw your new question, and I don't think you will get the same error message with the filter. It might be related to something else. – Paul Samsotha Aug 16 '15 at 15:27
  • In your `Main` class where you configure the ResourceConfig with the server – Paul Samsotha Aug 16 '15 at 15:59
  • I thought from your previous question about mahout, that you changed to Jersey 1.x. Jersey 2.x does not have that ,ethod. Just do `rc.register(new CORSFilter())` if you are in Jersey 2.x – Paul Samsotha Aug 16 '15 at 16:07
  • No problem. You should probably delete your new question. – Paul Samsotha Aug 16 '15 at 16:18