HTTP是一种用于客户端和服务器之间通信的协议。根据请求的复杂程度,HTTP请求可以被分为简单请求和复杂请求。
简单请求
简单请求是指仅使用HTTP头信息中的信息(如GET,POST等)进行请求,并且没有使用特殊的HTTP头字段的请求。简单请求的触发条件如下:
- 请求方法只能是GET、POST或HEAD。
- 请求头中的内容只能是Accept、Accept-Language、Content-Language、Content-Type(且值只能是 application/x-www-form-urlencoded、multipart/form-data 或 text/plain)和 Origin。
- 请求中没有使用ReadableStream流。
- 请求没有使用客户端提供的证书。
简单请求不会触发预检请求,可以直接发送到服务器端进行处理。
对于简单请求,例如使用 GET 或 POST 方法且没有自定义请求头,浏览器会自动添加 Origin 头部,发送跨域请求时,服务端可以通过 Access-Control-Allow-Origin 头部来授权。在这种情况下,跨域请求会被浏览器允许,而不需要进行预检请求。
复杂请求
复杂请求是指满足下列任一条件的请求:
- 使用了复杂的请求方法(如PUT,DELETE等)。
- 请求头中使用了自定义的HTTP头字段。
- 请求中使用了ReadableStream流。
- 请求需要进行身份验证(需要提供客户端证书等)。
复杂请求的触发条件不满足简单请求的条件,因此需要发送预检请求。预检请求是一种HTTP OPTIONS请求,用于通知服务器客户端希望发送的实际请求的参数和特性。服务器会返回一个带有Access-Control-Allow-Origin头字段的响应,用于告知客户端是否允许发送实际请求。如果允许,客户端将发送实际请求。
预检请求会携带一些自定义的头部信息,例如 Access-Control-Request-Method、Access-Control-Request-Headers 等,以告知服务器实际请求所需要的权限。服务器在接收到预检请求后,可以根据预检请求中的信息,返回相应的响应头部信息,表示是否允许跨域请求。
需要注意的是,预检请求的响应会被浏览器缓存,下次发送相同的复杂请求时,会直接使用缓存中的响应,而不会再发送预检请求。
实际工作中遇到一个问题,现象是get请求不跨域,post请求的预检请求出现了跨域,为什么?
- 之所以出现跨域,是因为预检请求会携带一些自定义的头部信息,例如
Access-Control-Request-Method、Access-Control-Request-Headers,而服务器未设置access-control-allow-headers。当服务器设置了access-control-allow-headers就可以解决该问题 - 值得思考的是,开发时并没有设置自定义Headers以及其它配置,为什么post请求成了复杂请求? 因为在请求时,数据格式是JSON格式,这样浏览器会根据数据类型给
Content-Type一个默认值application/json,而application/json会导致请求变成复杂请求。 - 注意点:浏览器根据数据设置
Content-Type默认值的问题,不同的浏览器处理的默认值不同,所以当请求时,最好明确设置Content-Type,从而避免一些问题。