本篇主要讲跨域问题
目前我正在使用spring boot跑后端,node.js跑react前端。后端运行的地址是http://localhost:8080
,而前端运行的地址是http://localhost:3000
。
现在前端想给后端发送一个axios请求,带有参数(比如用户名和密码),后端会返回一些数据。
Existing code
Backends
To make things simple, I just wrote a controller, using @ResponseBody
and @RequestParam
to recieve parameters in requests.
@Controller
public class IndexController {
@RequestMapping("/")
@ResponseBody
public String hello() {
return "Hello Spring Boot";
}
@RequestMapping(value="/testusername", method=RequestMethod.POST)
@ResponseBody
public String testdata(@RequestParam("username") String username, @RequestParam("password") String password){
return TestDataHelper.getTestJsonUserInfo(username);
}
}
The method TestDataHelper.getTestJsonUserInfo(username);
would return a simple json String as follow:
{
name: "Alice",
email: "alice@hue.au",
pasl: 2323
}
Frontends
In the frontend, axios is used to send requests. In this example, qs is required to make sure backends side can recieve the params.
qs is installed along with axios, simply import it by import qs from 'qs'
.
function doLogin() {
var username = document.getElementById('lgusername').value;
var password = document.getElementById('lgpassword').value;
const axios = require('axios');
axios({
method: 'post',
url: 'http://localhost:8080/testusername',
data: qs.stringify({username, password}) // use qs to stringify data
}).then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
Problem
Starting both projects, and clicking the button which will run doLogin()
function, the backend cannot recieve the request:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
This is because the browser is blocking it and it usually only allows a request in the same origin for security reasons. Even only port numbers are different, they still be treated as different origins.
Solution
Here, a simple solution on the backend side (spring boot) will be discussed, which implements a filter to allow cross domain requests.
Another solution applying Cross-Origin Resource Sharing (CORS) which I believe is better but more difficult can be found here: https://web.dev/cross-origin-resource-sharing/
For the filter solution, we can simply create a filter package and add a filter class with a @Component
annotation:
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
@Component
public class AccessFilter implements Filter {
@Override
public void init(FilterConfig arg0) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// Response to preflight request doesn't pass access control check:
// No 'Access-Control-Allow-Origin' header is present on the requested resource.
response.setHeader("Access-Control-Allow-Origin", "http://localhost:3000"); // the url that cross domain requests are from
response.setHeader("Access-Control-Allow-Credentials","true");
response.setHeader("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With,X-App-Id, X-Token");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
chain.doFilter(req, res);
}
@Override
public void destroy() {
}
}
Top comments (0)