1

Solved

I have been working on this program since 07:00. This has been driving me crazy. I have written multiple full stack web apps and have not had this problem.

Basically I have a form in html that allows a user to input data. Whenever the submit button is clicked, it sends that data to a collection in mongoDB. I have tested this in postman and it works. However, when I run the program through a webbrowser, I get this error:

XMLHttpRequest cannot load http://localhost:8080/articles/addArticle. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 404.

I tried installing CORS and configuring the endpoint

I tried going into the javascript file and changing the

This is my JavaScript file

alert('JS is linked to page!');

function Article(id = 1, title = "", authors="", content = "", genre = "", date = 1497484623) {
    console.log("JavaScript file loaded successfully");
    var self = this;
    self.Id = id;
    self.Title = title;
    self.Authors = authors;
    self.Content = content;
    self.Genre = genre;
    self.Date = date;
    self.Save = function() {
        var settings = {
            url: 'http://localhost:8080/articles/addArticle',
            method: 'POST',
            dataType: "json",
              beforeSend: function (xhr) {
                xhr.setRequestHeader("Accept", "application/json");
            }
        };
        var myData = {
            "Id" : self.Id,
            "Title": self.Title,
            "Authors": self.Authors,
            "Content": self.Content,
            "Genre": self.Genre,
            "Date": self.Date
        };
        settings.data = myData;

        $.ajax(settings).done(function(article) {
         var myArticle = new Article(article.Id, article.Title, article.Authors,
                article.Content, article.Genre, article.Date);
        });
    };
}



function addArticle(Article) {
    alert('addArticle Activated');
    var settings = {
        url: 'http://localhost:8080/articles/addArticle',
        method: 'POST',
        dataType: "json",
          beforeSend: function (xhr) {
                xhr.setRequestHeader("Accept", "application/json");
            }
    };
    var myData = {      
        "Title": Article.Title,     
        "Authors" : Article.Authors,
        "Content": Article.Content,
        "Genre" : Article.Genre,
        "Date": Article.Date 
    };
    settings.data = myData;

    $.ajax(settings).done(function(Article) {
        var myArticle = new Article(article.Id, article.Title, article.Authors, article.Content,
            article.Genre, article.Date);
        console.log("Article Created");
    });
}

$(document).ready(function() {


    $(document).on("submit", "#add-article", function(e) {
        e.preventDefault();
            alert('submit Activated');
        var title, authors, genre, content;
        title = $("#Title").val();
        director = $("#Authors").val();
        rating = $("#Genre").val();
        notes = $("#Content").val();
        var myArticle = new Article(0, title, authors, genre, content, 1497484623);
        alert(myArticle.Title);
        addArticle(myArticle);
        $("#add-article")[0].reset();
        $("#title").focus();

    });

});

/*function CreateSuccessRow(Article) {
    var successDataRow = `<tr id="Article-${Article.Id}"><td>${Article.Title}</td>
                <td>${Article.Authors}</td>
                <td>${Article.Genre}</td>

Due to popular demand, this is the server-side code:

package com.stereoscopics.app.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.stereoscopics.app.models.Article;
import com.stereoscopics.app.repo.ArticleRepo;

@RestController
@RequestMapping("/articles")
public class ArticleController {

private ArticleRepo articleRepo;

@Autowired
public ArticleController(ArticleRepo articleRepo) {
    this.articleRepo = articleRepo;
}

@RequestMapping(value = "/findall", method = RequestMethod.GET)
@ResponseBody
public List<Article> findall() {
    return articleRepo.findAll();
}

@RequestMapping(value = "/addArticle", method = RequestMethod.POST)
@ResponseBody
public Article addArticle(@RequestBody Article newArticle) {
    articleRepo.save(newArticle);
    return newArticle;
}

}

I have no idea how to fix this. Please help.

UPDATE

This still isn't working. I've updated the code as per some suggestions and I'm either doing it wrong or it's incorrect. The changes are shown below:

  @RestController
  @RequestMapping("/articles")
  public class ArticleController {

private ArticleRepo articleRepo;

@Autowired
public ArticleController(ArticleRepo articleRepo) {
    this.articleRepo = articleRepo;
}

public void doPost(HttpServletRequest request, HttpServletResponse response) 
throws ServletException, IOException {
    response.addHeader("Access-Control-Allow-Origin", "*");
}

@RequestMapping(value = "/findall", method = RequestMethod.GET)
@ResponseBody
public List<Article> findall() {
    return articleRepo.findAll();
}

@RequestMapping(value = "/addArticle", method = RequestMethod.POST)
@ResponseBody
public Article addArticle(@RequestBody Article newArticle, final 
HttpServletResponse response) {
    response.addHeader("Access-Control-Allow-Origin", "*");
    articleRepo.save(newArticle);
    return newArticle;
  }
}

THIS IS NOT SOLVED YET

DaveCat
  • 773
  • 1
  • 10
  • 28
  • `I tried installing CORS and configuring the endpoint` - can you show the **server side** code for this, because this is a question about the **server side** as CORS is implemented **server side** – Jaromanda X Jul 04 '17 at 02:04
  • also, `xhr.setRequestHeader("Accept", "application/json");` probably means you will get a preflight `OPTIONS` request, which needs to be handled by the **server side** code as well (though, depending on what you mean by "installing CORS" this may already be handled) - without **server side** code, your question is almost impossible to answer – Jaromanda X Jul 04 '17 at 02:05
  • and finally, `The response had HTTP status code 404` - clearly there's other issues on the server side, as you are requesting a resource that doesn't exists – Jaromanda X Jul 04 '17 at 02:06
  • ..What? Unless I did this wrong, Installing CORS is not a server side operation. It's a plugin on chrome that you configure through a basic interface to allow Access-Control-Headers. I don't know what server side code you're referring to, but I'll put my controller into the original question to give clarity. – DaveCat Jul 04 '17 at 02:22
  • 1
    oh, sorry, CORS is usually a server side configuration for servers to allow its resources to be consumed cross domain. Installing a chrome add-on is only ever a hack – Jaromanda X Jul 04 '17 at 02:23
  • My mistake. I added the server-side code. Any input would be very helpful because I've been banging my head against the wall for like 15 hours with this thing. I've built multiple full stack web apps and never have had this error before. I suspect it's in the Javascript file but I very well may be wrong at this point. – DaveCat Jul 04 '17 at 02:25
  • I obviously want others to use my site. If I didn't make it clear in the original question and with the multiple comments indicating that I had not had the problem before, I have not actually had this problem before and was unsure how to resolve it. – DaveCat Jul 04 '17 at 02:30
  • https://stackoverflow.com/questions/10636611/how-does-access-control-allow-origin-header-work – david25272 Jul 04 '17 at 03:10
  • If you have not had this problem before than it means that you have not served ajax content from a different port before. The browser considers `localhost` and `localhost:8080` to be two completely different servers and thus block all ajax requests. CORS was added as a way to allow requests from one server to another. The traditional solution is to serve BOTH pages from the same server (that means same domain, same port). The modern solution is for the `localhost:8080` server to add CORS header to its reply – slebetman Jul 04 '17 at 05:11
  • Do note that browsers will block ALL CORS requests from files because files don't have domain names. So you need to server your HTML from `localhost` and have `localhost:8080` add CORS header to its replies or server both your HTML file and the ajax data from `localhost:8080` – slebetman Jul 04 '17 at 05:12
  • @slebetman This is the fix. The browser considers localhost and localhost:8080 to be two completely different servers and thus block all ajax requests – DaveCat Jul 04 '17 at 14:52
  • @slebetman Maybe this is a stupid question but how do I simply serve Ajax from the same port and same server? I've changed the requestMapping to ":8080/articles" and it's not doing anything. I don't want to serve them from different servers or ports. – DaveCat Jul 07 '17 at 13:19
  • How are you serving the html file? The one that includes the javascript. I mean what is serving `localhost`? – slebetman Jul 07 '17 at 15:47

4 Answers4

0

Basically what it says is that your service at http://localhost:8080/articles/addArticle. should add a header 'Access-Control-Allow-Origin' that the browser can read.

I ran to similar scenario when I was making a REST API, but instead of installing cors (which its version conflicts with our production server build), I just added that header when sending out responses to the request client.

response.AddHeader("Access-Control-Allow-Origin", "*");

If you are using asp.net, then you can either put that code in the Global.asax under:

protected void Application_BeginRequest(Object sender, EventArgs e) { 
    var context = HttpContext.Current;
    var response = context.Response; 
    response.AddHeader("Access-Control-Allow-Origin", "*"); 
}

If you are using java/servlet/jsp then it's quite similar as you already have access to HttpServletResponse response so in your code body, just do:

public void doPost(HttpServletRequest request,
                     HttpServletResponse response)
      throws ServletException, IOException {
    response.addHeader("Access-Control-Allow-Origin", "*");

    //other codes and stuff here...
} 

from your code on the other hand which uses spring, you could do something like:

@RequestMapping(value = "/addArticle", method = RequestMethod.POST)
@ResponseBody
public Article addArticle(@RequestBody Article newArticle, final HttpServletResponse response) {
    response.addHeader("Access-Control-Allow-Origin", "*");
    articleRepo.save(newArticle);
    return newArticle;
}
Qiqo
  • 64
  • 5
  • Where would I be adding this? – DaveCat Jul 04 '17 at 02:37
  • if you are using asp.net, then you can either put that code in the Global.asax under `protected void Application_BeginRequest(Object sender, EventArgs e) { var response = context.Response; response.AddHeader("Access-Control-Allow-Origin", "*"); }` or if you dont have access to the Global asax, or are using other app server, you simply add that header during creation of your response – Qiqo Jul 04 '17 at 02:41
  • I have updated my answer to show the code on the Global.asax – Qiqo Jul 04 '17 at 02:47
  • I'm using java. Do you know where I'd put this? – DaveCat Jul 04 '17 at 02:48
  • if you are using servlet then its quite similar as you already have access to `HttpServletResponse response` so in your body, just do: `response.addHeader("Access-Control-Allow-Origin", "*");` before you return,dispatch or forward to view – Qiqo Jul 04 '17 at 02:52
  • Maybe this is me being particularly dense but could you show me an example with the code I have? I don't believe I'm following. – DaveCat Jul 04 '17 at 03:04
  • No worries, I have updated my answer to reflect the java version and something that aligns with your code. – Qiqo Jul 04 '17 at 03:28
  • This is still not working. Maybe I am confused. I updated my original code to reflect my the changes you suggested. – DaveCat Jul 04 '17 at 13:46
0

You can not solve cors as javascript end, I mean to say the problem is with your server code.

Cors is a security feature implemented by browser, let me tell you how it works.

when you query server, at first it hits with option method, at this time server sends allowed origins list, it can be one or multiple. now if your local domain or any domain is not present in that list then browser does not make the actual request.

To fix this you will have to configure cors filter on your server you can do it like

package com.package.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


 @Component
 public class SimpleCORSFilter extends GenericFilterBean {

/**
 * The Logger for this class.
 */
private final Logger logger = LoggerFactory.getLogger(this.getClass());

@Override
public void doFilter(ServletRequest req, ServletResponse resp,
                     FilterChain chain) throws IOException, ServletException {
    logger.info("> doFilter");

    HttpServletResponse response = (HttpServletResponse) resp;
    response.setHeader("Access-Control-Allow-Origin", "*");
    response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
    //response.setHeader("Access-Control-Allow-Credentials", "true");
    chain.doFilter(req, resp);

    logger.info("< doFilter");
}

now if it's still not working then you will have to debug it line by line at server what's code, and at browser end you can test if option method has list of allowed origins or not.

MehulJoshi
  • 879
  • 8
  • 19
  • I'll give this a try. Do I just create the class within the filter package or is there something else I'm supposed to do? I've done this and still get the exact same error. – DaveCat Jul 04 '17 at 13:56
0

I accidentally marked this as solved, when it clearly is not.

By including the port number in the Javascript url, the server threw an error because it mistook the request as originating from somewhere it wasn't supposed to come from. When I remove the port number, it doesn't know which port to hit. It should be hitting 8080 by default, but I'm getting another error.

To date, nobody has solved this problem. I've built apps without having this come up before and everyone I've asked at work seems to think I should just use spring forms. I'd ideally like to actually solve the problem instead of just finding work arounds.

DaveCat
  • 773
  • 1
  • 10
  • 28
-1

Deal your CORS problem that you need to set the crossDomain first.

$.ajax({
  crossDomain:true
})

Next, you gonna to set the xhrFields in withCredentials.

$.ajax({
  crossDomain:true,
  xhrFields: {
    withCredentials: true
  }
})

In you Code , it's look like following this setting config.

let settings = {
    url: 'http://localhost:8080/articles/addArticle',
    method: 'POST',
    crossDomain:true,
    xhrFields: {
      withCredentials: true
    }
    dataType: "json",
      beforeSend: function (xhr) {
        xhr.setRequestHeader("Accept", "application/json");
    }
};
Rach Chen
  • 1,322
  • 2
  • 10
  • 26