跨域问题简单的说就是前台请求一个后台链接,发送请求的前台与后台的地址不在同一个域下,就会产生跨域问题。这里所指的域包括协议、IP地址、端口等。
后端代码:
package cn.qs.controller;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.commons.collections.MapUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/test")
@RestController
public class TestController {
@GetMapping("/get")
public Map get(@RequestParam Map condition) {
if (MapUtils.isEmpty(condition)) {
condition = new LinkedHashMap<>();
condition.put("param", null);
}
return condition;
}
}
前端代码:
结果:虽然后端正常响应,但是JS报错,这就是跨域安全问题,如下:
js报错如下:
Failed to load http://localhost:8088/test/get.html: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8020' is therefore not allowed access.
(1)浏览器限制
发生ajax跨域的问题的时候后端是正常执行的,从后台打印的日志可以看出,而且后台也会正常返回数据。浏览器为了安全进行了限制,说白了就是浏览器多管闲事。
(2)跨域:
当协议、域名、端口不一致浏览器就会认为是跨域问题。
(3)XHR(XMLHttpRequest)请求,也就是ajax请求
如果不是ajax请求,不存在跨域问题(这个我们应该可以理解,浏览器直接访问以及a标签跳转等方式都不会产生跨域问题)。
针对上面三个原因可以对跨域问题进行解决。思路如下:
(1)浏览器端:浏览器允许跨域请求,这个不太现实,我们不可能改每个客户端
(2)XHR请求使用JSONP(JSON with Padding)方式进行方式。它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。
(3)针对跨域问题解决:
被调用方:也就是服务器端接口,服务器允许跨域。但是如果某些情况服务器端不是我们写的就不可行了。
调用发:也就是JS客户端,隐藏跨域。通常是通过代理的形式隐藏跨域请求,使请求都类似于同一域下发出a标签。
比如chrom启动的时候设置参数关闭安全检查,如下:
chrome --disable-web-security --user-data-dir=g:/test
设置之后可以正常进行访问,这也进一步证明了跨域问题与后台无关。
JSONP(JSON with Padding) 是一种变通的方式解决跨域问题。JSONP是一种非官方的协议,双方进行约定一个请求的参数。该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
JSONP发出的请求类型是script,不是XHR请求,所以可以绕过浏览器的检查。JSONP返回的是application/javascript,普通的xhr请求返回的是application/json。
JSONP的原理:通过向界面动态的添加script标签来进行发送请求。script标签会加上callback参数以及_,_是为了防止请求被缓存。
比如我们发送一个请求地址是http://localhost:8088/test/get.html?name=zhangsan&callback=handleCallback&_=123。后端看到有约定的参数callback,就认为是JSONP请求,如果XHR正常请求的响应是{success: true},那么后端会将回传的JSON数据作为参数,callback的值作为方法名,如: handleCallback({success: true}), 并将响应头的Content-Type设为application/javascript,浏览器看到是JS响应,则会执行对应的handleCallback(data)方法。
(1)服务器端代码需要改动
(2)只支持get方法,由于JSONP原理是通过script标签实现的,所以只能发送get请求
(3)不是XHR异步请求。所以不能使用XHR的一些特性,比如异步等。
后端:增加一个advice
package cn.qlq.aspect;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice;
@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super("callback");
}
}
前端:采用JSON包装的JSONP请求
结果:
(1)请求是script
请求头:
(2)查看响应数据头和数据:
数据如下:
/**/jQuery18309128178844464243_1575299406254({"name":"zhangsan","callback":"jQuery18309128178844464243_1575299406254","_":"1575299406287"});
补充:JSONP也可以自己定义返回的方法名称,默认是JSON生成的随机字符串
查看请求数据:参数加_是为了防止浏览器缓存JS请求
查看响应数据:
结果:
这里所说的被调用方一般也就是指的是服务端。
客户端发送请求到http服务器,通常是nginx/Apache;http服务器判断是静态请求还是动态请求,静态请求就直接响应,动态请求就转发到应用服务器(Tomcat\weblogic\jetty等)。
当然也有省去中间静态服务器的应用,就变为客户端直接请求应用服务器。
被调用方通过请求头告诉浏览器本应用允许跨域调用。可以从tomcat应用服务器响应请求头,也可以从中间服务器向请求头添加请求头。
(1)浏览器先执行还是先判断请求是XHR请求?
查看下面的简单请求与非简单请求的解释。
(2)浏览器如何判断?
分析普通请求和跨域请求的区别:
普通请求的请求头如下:
XHR的请求如下:
可以看出XHR请求的请求头会多出一个Origin参数(也就是域),浏览器就是根据这个参数进行判断的,浏览器会拿响应头中允许的。如果不允许就产生跨域问题,会报错。
补充:关于XHR请求头中携带X-Requested-With与Origin
我自己测试,如果用jquery的ajax访问自己站内请求是会携带X-Requested-With参数、不带Origin参数,如果访问跨域请求不会携带X-Requested-With参数,会携带Origin参数。
if ( !options.crossDomain && !headers["X-Requested-With"] ) {
headers["X-Requested-With"] = "XMLHttpRequest";
}
1.被调用方过滤器中实现支持跨域
package cn.qs.filter;
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.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
/**
* 允许跨域请求
*/
@WebFilter(filterName = "corsFilter", urlPatterns = "/*")
public class CorsFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response2 = (HttpServletResponse) response;
response2.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8020");
response2.setHeader("Access-Control-Allow-Methods", "GET");
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
上面Access-Control-Allow-Origin是允许跨域请求的域, Access-Control-Allow-Methods 是允许的方法。
我们再次查看XHR请求头和响应头:
如果允许所有的域和方法可以用:
response2.setHeader("Access-Control-Allow-Origin", "*"); response2.setHeader("Access-Control-Allow-Methods", "*");
再次查看请求头和响应头:
这种跨域是不支持携带cookie发送请求的。
2.简单请求和非简单请求
简单请求是先执行后判断,非简单请求是先发一个预检命令,成功之后才会发送请求。
(1)简单请求:请求的方法为GET\POST\HEAD方法中的一种;请求的header里面无自定义头,并且Content-Type为:text/plain、multipart/form-data、application/x-www-form-urlencoded中的一种。
只有同时满足以上两个条件时,才是简单请求,否则为非简单请求
(2)非简单请求:put、delete方法的ajax请求;发送json格式的ajax请求;带自定义头的ajax请求。最常见的是发送json格式的ajax请求。非简单会发送两次请求:一个options的预检请求、预检请求根据响应头判断正确之后发送数据请求。
发送一个非简单请求:
后端:
package cn.qs.controller;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.commons.collections.MapUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/test")
@RestController
public class TestController {
@GetMapping("/get")
public Map get(@RequestParam Map condition) {
if (MapUtils.isEmpty(condition)) {
condition = new LinkedHashMap<>();
condition.put("param", null);
}
return condition;
}
@PostMapping("/getJSON")
public String getJSON(@RequestBody String param) {
System.out.println(param);
return param;
}
}
前端
function test() {
$.ajax({
url: "http://localhost:8088/test/getJSON.html",
type: "POST",
data: JSON.stringify({name : "张三"}),
contentType: "application/json;charset=utf-8",
success: function(res) {
console.log(res);
}
});
}
test();
结果:(发送预检请求的时候报错)
控制台报错: (发送预检的响应头未设置需要的响应头)
Failed to load http://localhost:8088/test/getJSON.html: Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
修改filter
package cn.qs.filter;
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.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
/**
* 允许跨域请求
*/
@WebFilter(filterName = "corsFilter", urlPatterns = "/*")
public class CorsFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response2 = (HttpServletResponse) response;
// 允许请求的域(协议://IP:port)
response2.setHeader("Access-Control-Allow-Origin", "*");
// 允许请求的方法
response2.setHeader("Access-Control-Allow-Methods", "*");
// 正确的响应预检请求
response2.setHeader("Access-Control-Allow-Headers", "Content-Type");
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
再次前端发送请求:(响应头增加Access-Control-Allow-Headers预检请求会正常响应,预检成功之后会发送正常的数据请求,所以看到是发出两个请求)
补充:预检命令可以缓存,过滤器向响应头增加如下响应头:(浏览器会缓存1个小时的预检请求)
// 缓存预检命令的时长,单位是s response2.setHeader("Access-Control-Max-Age", "3600");
1小时内发送非简单请求只会预检请求1次。我们可以用chrom的disable cache 禁掉缓存测试:
3.带cookie的跨域请求
同域下发送ajax请求默认会携带cookie;不同域发送cookie需要进行设置,前后台都需要设置。
(1)首先明白跨域请求需要后台进行设置:请求头的值 Access-Control-Allow-Origin 不能是*,必须是具体的域。需要根据请求头的Origin获取到请求的域之后写到响应头中。
(2)响应头也需要增加允许携带cookie的字段 。
// 允许cookie response2.setHeader("Access-Control-Allow-Credentials", "true");
(3)客户端发送ajax请求的时候需要withCredentials: true 允许携带cookie。A发ajax请求给B, 带着的是B的cookie, 还是受限于同源策略, ajax的Request URL是B, cookie就是B的
先在C:\Windows\System32\drivers\etc\hosts下面增加虚拟域名:
127.0.0.1 a.com 127.0.0.1 b.com
上面a.com 用于访问静态页面,b.com 用于接收后端请求。
后端过滤器修改
package cn.qs.filter;
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.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
/**
* 允许跨域请求
*/
@WebFilter(filterName = "corsFilter", urlPatterns = "/*")
public class CorsFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// 允许访问的源
String headerOrigin = request.getHeader("Origin");
if (StringUtils.isNotBlank(headerOrigin)) {
response.setHeader("Access-Control-Allow-Origin", headerOrigin);
}
// 允许访问的方法
response.setHeader("Access-Control-Allow-Methods", "*");
// 正确的响应预检请求
response.setHeader("Access-Control-Allow-Headers", "Content-Type");
// 允许预检命令缓存的时间
response.setHeader("Access-Control-Max-Age", "3600");
// 允许cookie
response.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
后端Controller:
@GetMapping("/getCookie") public String getCookie(@CookieValue(value = "cookie1", required = false) String cookie, HttpServletRequest request) { System.out.println("cookie: " + cookie); System.out.println("Origin: " + request.getHeader("Origin")); return cookie; } @GetMapping("/setCookie") public String setCookie(HttpServletRequest request, HttpServletResponse response) { Cookie cookie2 = new Cookie("cookie1", "value1"); cookie2.setPath("/"); response.addCookie(cookie2); String cookie = "cookie1=value1"; return cookie; }
前端JS:
function test() {
$.ajax({
type : "get",
async: false,
url : "http://b.com:8088/test/getCookie.html",
xhrFields: {
withCredentials: true
},
success: function(res) {
console.log("res: " + res);
},
error:function(){
alert('fail');
}
});
}
test();
测试:
(1)如果直接执行前端不会传cookie,因为没有cookie。如下:(由于我们访问的服务是b.com域名,我们的cookie需要是b.com域名下的cookie)
首先我们访问后台 http://b.com:8088/test/setCookie.html 获取cookie,当然可以通过document.cookie进行设置
(2)接下来再访问后台:
请求头如下:
响应头如下:
(3)后台控制台日志
cookie: value1
Origin: http://a.com:8020
4.带自定义头的跨域请求
过滤器修改,根据自定义请求头在响应头中增加允许的请求头:
package cn.qs.filter;
import java.io.IOException;
import java.util.Enumeration;
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.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
/**
* 允许跨域请求
*/
@WebFilter(filterName = "corsFilter", urlPatterns = "/*")
public class CorsFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// 允许访问的源
String headerOrigin = request.getHeader("Origin");
if (StringUtils.isNotBlank(headerOrigin)) {
response.setHeader("Access-Control-Allow-Origin", headerOrigin);
}
// 允许访问的方法
response.setHeader("Access-Control-Allow-Methods", "*");
// 正确的响应预检请求
// response.setHeader("Access-Control-Allow-Headers", "Content-Type");
// 允许自定义的请求头(根据自定义请求头)
String headers = request.getHeader("Access-Control-Request-Headers");
if (StringUtils.isNotBlank(headers)) {
response.addHeader("Access-Control-Allow-Headers", headers);
}
// 允许预检命令缓存的时间
response.setHeader("Access-Control-Max-Age", "3600");
// 允许cookie
response.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
Controller:
@GetMapping("/getHeader")
public JSONResultUtil getHeader(@RequestHeader("x-header1") String header1,
@RequestHeader("x-header2") String header2) {
System.out.println(header1 + " " + header2);
return new JSONResultUtil(true, header1 + " " + header2);
}
前端:
结果:
我们禁调缓存会发送两条请求:
(1)预检请求
(2)第二条请求
5. 被调用方解决-nginx解决方案(替代上面的filter的作用)
这里用被调用方nginx解决是通过nginx代理之后增加所需的响应头。
我们还是基于上面的配置的本地域名。 下面 a.com 用于访问静态页面, b.com 用于接收后端请求。
127.0.0.1 a.com 127.0.0.1 b.com
(1)打开nginx/conf/nginx.conf,在最后的 } 前面增加如下:
include vhost/*.conf;
表示引入 当前目录/vhost/ 下面所有后缀为conf的文件。
接下来在当前conf目录创建vhost目录,并在下面创建b.com.conf文件,内容如下:
server {
listen 80;
server_name b.com;
location /{
proxy_pass http://localhost:8088/;
add_header Access-Control-Allow-Methods true;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Max-Age 3600;
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Headers
$http_access_control_request_headers;
if ($request_method = OPTIONS) {
return 200;
}
}
}
注意
(0)前面的是设置监听域名是b.com、80端口,转发到 http://localhost:8088/
(1)nginx中请求头都是小写,-要用_代替。
(2)$http_origin可以取到请求头的origin。
(3)最后判断如果是预检请求,会直接返回200状态吗。
关于nginx的使用:
nginx检查语法:
E:\nginx\nginx-1.12.2>nginx.exe -t
nginx: the configuration file E:\nginx\nginx-1.12.2/conf/nginx.conf syntax is ok
nginx: configuration file E:\nginx\nginx-1.12.2/conf/nginx.conf test is successful
nginx重新加载配置文件:
nginx.exe -s reload
重启和停止
nginx.exe -s reopen
nginx.exe -s stop
注释掉filter之后修改前台:异步访问 b.com, 会被请求转发到: http://localhost:8088/
function test() {
$.ajax({
url: "http://b.com/test/getCookie.html",
type: "get",
headers: {
"x-header1": "header1",
"x-header3": "header3"
},
beforeSend: function(xhr) {
xhr.setRequestHeader("x-header2","header2");
},
xhrFields: {
withCredentials: true
},
success: function(res) {
console.log(res);
}
});
}
test();
(1)预检命令
(2)第二次正式请求
6. Spring注解跨域:@CrossOrigin
加在类上表示所有方法允许跨域,加在方法表示方法中允许跨域。
package cn.qs.controller;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.commons.collections.MapUtils;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import cn.qs.utils.JSONResultUtil;
@RequestMapping("/test")
@RestController
@CrossOrigin
public class TestController {
@GetMapping("/get")
public Map get(@RequestParam Map condition) {
if (MapUtils.isEmpty(condition)) {
condition = new LinkedHashMap<>();
condition.put("param", null);
}
return condition;
}
@GetMapping("/getCookie")
public String getCookie(@CookieValue(value = "cookie1") String cookie) {
return cookie;
}
@PostMapping("/getJSON")
public String getJSON(@RequestBody String param) {
System.out.println(param);
return param;
}
@GetMapping("/getHeader")
public JSONResultUtil getHeader(@RequestHeader("x-header1") String header1,
@RequestHeader("x-header2") String header2) {
System.out.println(header1 + " " + header2);
return new JSONResultUtil(true, header1 + " " + header2);
}
}
被调用方解决跨域是通过nginx代理,将被调用方的请求代理出去,隐藏掉跨域请求。
(1)在nginx/conf/vhost下面新建a.com.conf,内容如下:
server {
listen 80;
server_name a.com;
location /{
proxy_pass http://localhost:8020/;
}
location /server{
proxy_pass http://b.com:8088/;
}
}
解释: 监听 a.com 的80端口。 默认是/会转发到本地的8020端口,也就是前台页面所用的端口;如果是/server/ 开始的会转发到后端服务所用的路径。
(2)Controller修改
@GetMapping("/getCookie")
public String getCookie(@CookieValue(value = "cookie1", required = false) String cookie,
HttpServletRequest request) {
System.out.println("cookie1: " + cookie);
System.out.println("=====================");
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String header = (String) headerNames.nextElement();
String value = request.getHeader(header);
System.out.println(header + "\t" + value);
}
return cookie;
}
(3)前端修改:(统一访问 /server 由nginx转发到后端服务)
function test() {
$.ajax({
url: "/server/test/getCookie.html",
type: "get",
headers: {
"x-header1": "header1",
"x-header3": "header3"
},
beforeSend: function(xhr) {
xhr.setRequestHeader("x-header2","header2");
},
xhrFields: {
withCredentials: true
},
success: function(res) {
console.log(res);
}
});
}
test();
(4)首先设置cookie:(cookie是设置为a.com的cookie,nginx访问转发请求的时候也会携带到b.com)
查看cookie:
(5)刷新页面测试:
前端查看:可以看到前端请求发送至 a.com
请求头:
响应头:
后端控制台:(可以看到携带了x-requested-with参数,仍然是ajax请求,但是相当于同域请求。主机也是b.com(由nginx转发过来的请求))
cookie1: a.com.cookie
=====================
host b.com:8088
connection close
pragma no-cache
cache-control no-cache
accept */*
x-header3 header3
x-requested-with XMLHttpRequest
x-header2 header2
user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
x-header1 header1
referer http://a.com/%E6%99%AE%E9%80%9A%E7%9A%84%E6%B5%8B%E8%AF%95/index.html?__hbt=1575599926569
accept-encoding gzip, deflate
accept-language zh-CN,zh;q=0.9
cookie cookie1=a.com.cookie
补充:调用方采用nodejs的express模块和http-proxy-middleware进行代理
(1)安装express模块和http-proxy-middleware模块:需要以管理员身份运行cmd
cnpm install --save-dev http-proxy-middleware
cnpm install --save-dev express
(2)编写nodejs代理脚本:
const express = require('express');
const proxy = require('http-proxy-middleware');
const app = express();
app.use(
'/server',
proxy({
target: 'http://b.com:8088',
changeOrigin: true,
pathRewrite: {'/server' : ''}
}));
app.use(
'/',
proxy({
target: 'http://a.com:8020'
}));
app.listen(80);
注意:上面的顺序需要先代理/server,再代理/。否则会先匹配/。
(3)测试方法同上面nginx代理测试。
补充:nginx采用多进程结构
因为 Nginx 最核心的一个目的是要保持高可用性、高可靠性,而当 Nginx 如果使用的是多线程结构的时候,因为线程之间是共享同一个地址空间的,所以当某一个第三方模块引发了一个地址空间导致的段错误时、在地址越界出现时,会导致整个 Nginx 进程全部挂掉。而当采用多进程模型时,往往不会出现这样的问题。
master 进程被设计用来的目的是做 worker 进程的管理的,也就是所有的 worker 进程是处理真正的请求的,而 master 进程负责监控每个 worker 进程是不是在正常的工作、需不需要做重新载入配置文件、需不需要做热部署。
我们启动一个nginx服务会启动两个进程,比如linux下面查看nginx相关进程:
[root@lawyer-test conf.d]# ps -ef | grep nginx root 19997 1 0 4月07 ? 00:00:00 nginx: master process ./nginx nginx 23141 19997 0 9月20 ? 00:00:01 nginx: worker process root 32666 20866 0 14:44 pts/7 00:00:00 grep --color=auto nginx
可以看到两个进程。一个 nginx master 进程是由 root 用户起的,进程 PID 是 19997。还有一个worker进程,是由19997进程起来的,进程ID是23141。
再次重新加载配置文件,查看进程如下:
[root@lawyer-test conf.d]# nginx -s reload
[root@lawyer-test conf.d]# ps -ef | grep nginx
nginx 11357 19997 0 14:47 ? 00:00:00 nginx: worker process
root 12572 20866 0 14:48 pts/7 00:00:00 grep --color=auto nginx
root 19997 1 0 4月07 ? 00:00:00 nginx: master process ./nginx
可以看到 worker 工作进程的进程ID发生改变。
补充: nginx -s 参数
-s 代表的是向主进程发送信号。其中信号有 4 个,stop, quit, reopen, reload。比如 nginx -s reload 命令就是重新加载配置文件。
补充: nginx 设置允许上传的最大文件
在nginx.conf配置文件中的http块中配置client_max_body_size参数
client_max_body_size 500M;
所谓的跨域请求是指XHR请求发送的时候 协议、域名、端口不完全一致的情况。只要有一个不同就是跨域。
如果用jquery的ajax访问自己站内请求是会携带X-Requested-With参数、不带Origin参数;如果访问跨域请求不会携带X-Requested-With参数,会携带Origin参数。
后端获取请求头的时候不区分大小写,比如说前端发送的请求头是 x-header1:header1。后端可以用 request.getHeader("X-HEADER1"); 接收。