网页状态码——200、302、304、400、404、405、415、500,跨域
细节:null和空,null调用方法会出现空指针异常
Failed to convert from type [java.lang.String] to type [java.util.Date] for value ‘2021-5-28’; nested exception is java.lang.IllegalArgumentException]
报错:ERR_TOO_MANY_REDIRECTS
原因:.excludePathPatterns里面的路径反斜杠没加
访问静态资源时,如果服务器认为静态文件没有变,就会返回304状态码
再次访问时,服务器认为自己的图片没变,所以报304
存在问题,如果验证码是一张图片,那浏览器清理的缓存,结果用户请求服务器的静态图片时,就拿不到了,解决方法,骗一下服务器,每次加一个?,服务器就会再次发一下
报错信息:
spring后台的报错:
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type ‘application/x-www-form-urlencoded;charset=UTF-8’ not supported]
浏览器的报错:
报错原因:传的是字符串,接收的时候加了@RequestBody,表示用对象接收,就报415异常
解决方案:
// public ResData queryById(@RequestBody Integer id){ // TODO:415异常
public ResData queryById(Integer id){
// axios.post("/build/id",this.id)// TODO:415异常
axios.get("/build/id?id="+this.id)
在post请求中,通过用new URLSearchParams(),发送post请求,其实本质是以String类型的键值对发送给后端的
let params = new URLSearchParams();
let listParam = {"buildingIdList":this.selectedBuildingList}
params.set("buildingList",this.selectedBuildingList)
params.set("areaMin",this.areaMin)
params.set("areaMax",this.areaMax)
params.set("areaOrderASC",this.areaOrderASC)
params.set("areaOrderDESC",this.areaOrderDESC)
console.log(listParam)
console.log(params)
axios.post("/owner/query/house",params)
类型的键值对
通常看到的表单数据是这样,
本质其实就是下面这个,和get请求就差一个?,区别是一个是拼在浏览器的地址栏的路径上,另一个是放在请求头里面;
所以在上面的请求中,报错了,415异常
Spring后台的信息:
Using 'application/json', given [application/json, text/plain, */*] and supported [application/json, application/*+json, application/json, application/*+json]
Using ‘application/json’, given [application/json, text/plain, /] and supported [application/json, application/+json, application/json, application/+json]
这个错误通常出现在尝试使用 “application/json” 作为响应类型的API端点上。它是一个比较普遍的错误,通常是由于客户端在发送请求时没有正确设置请求头导致的。
这个错误信息的含义是,虽然该API端点支持 “application/json” 响应类型,但客户端发出的请求头中可能没有包含正确的 “Accept” 值。“Accept” 值指定了客户端能够接受的 MIME 类型。在这个例子中,客户端似乎已经将请求头中的 “Accept” 设置为 “text/plain” 或 “/”,但是服务器只支持 “application/json” 响应类型,因此会出现这个错误。
要解决这个问题,您需要确保客户端请求头中的 “Accept” 值设置为 “application/json”,或者可以同时支持多种响应类型。如果您正在使用 JavaScript 中的 XMLHttpRequest 或 Fetch API,则可以使用以下代码来设置请求头:
// 对于 XMLHttpRequest
xhr.setRequestHeader("Accept", "application/json");
// 对于 Fetch API
fetch(url, {
headers: {
"Accept": "application/json"
}
});
这样设置请求头之后,应该就能够成功地获得 “application/json” 响应类型了。
这种方式会报异常!!!
/**
* 查询所有空闲的房子,显示到左边的框里面 TODO:后面再全查询的基础上进行
*/
List<House> queryAllNoIntoHouse(
@Param("buildingIdList") List<Integer> buildingIdList, // 楼栋
@Param("storey") Integer storey, // 楼层
@Param("areaMin") Double areaMin, // 面积
@Param("areaMax") Double areaMax, // 面积
@Param("areaOrderASC") Boolean areaOrderASC,
@Param("areaOrderDESC") Boolean areaOrderDESC,// 排序
@Param("status") Boolean... status // 如果为false,则可以查询到非空闲房子,其他情况 都是查询空闲房子
);
SQL语句
<select id="queryAllNoIntoHouse" resultType="house">
SELECT
c_house.id,
c_owner_house.ownerId,
c_building.num,c_building.floors,c_building.unit,
c_house.storey,c_house.roomNum,c_house.area,c_house.into_date,c_house.status,c_house.building_id,
user_owner.realname AS ownerName
FROM c_house
LEFT JOIN c_building ON c_building.id = c_house.building_id
LEFT JOIN c_owner_house ON c_owner_house.houseId = c_house.id
LEFT JOIN user_owner ON user_owner.id = c_owner_house.ownerId
<where>
<choose>
<when test="buildingIdList!=null and buildingIdList.size()>0">
building_id IN
<foreach collection="buildingIdList" open="(" separator="," close=")" item="id">
#{id}
foreach>
when>
<otherwise>
otherwise>
choose>
<if test="storey!=null">
AND c_house.storey=#{storey}
if>
<if test="areaMin!=null">
AND c_house.area >= #{areaMin}
if>
<if test="areaMax!=null">
AND c_house.area <= #{areaMax}
if>
<choose>
<when test="status!=null and status.length>0 and !status[0]">
when>
<otherwise>
AND !status
otherwise>
choose>
where>
<if test="areaOrderASC">
ORDER BY c_house.area
if>
<if test="areaOrderDESC">
ORDER BY c_house.area DESC
if>
select>
前后端交互controller层,用专门前后端交互的实体类接收参数
// 2.处理搜索请求
// 默认显示所有可以选择的房间,放到左侧的框里,如果选择,双击,这条数据跳到右边的框里
@RequestMapping("/query/house")
@ResponseBody
public ResData queryHouse(
// @RequestParam(value = "buildingList", defaultValue = "List[]") List buildingList, // 楼栋
// Integer storey, // 楼层,没有用到
// Double areaMin,
// Double areaMax,
// @RequestParam(value = "areaOrderASC",defaultValue = "false") Boolean areaOrderASC,
// @RequestParam(value = "areaOrderDESC",defaultValue = "false") Boolean areaOrderDESC
@RequestBody HouseFront houseFront
){
Double areaMax = houseFront.getAreaMax();
Double areaMin = houseFront.getAreaMin();
Boolean areaOrderASC = houseFront.getAreaOrderASC();
Boolean areaOrderDESC = houseFront.getAreaOrderDESC();
Integer storey = houseFront.getStorey();
List<Building> buildingList = houseFront.getBuildingList();
List<Integer> buildingIds = buildingList.stream().map(Building::getId).collect(Collectors.toList());
System.out.println("查询条件:"
+areaMin+"/"
+areaMax+"/"
+buildingIds+"/"
+storey+"/"
);
// 大小的问题
if (!StringUtils.isBlank(areaMin) && !StringUtils.isBlank(areaMax)){
if (areaMin.compareTo(areaMax)>0){
return new ResData(1002, "最小面积不能大于最大面积", null);
}
}
// 这个排序顺序不能两个都是true
if (areaOrderASC && areaOrderDESC){
return new ResData(1003, "排序条件冲突", null);
}
List<House> list = houseService.queryAllNoIntoHouse(buildingIds, storey, areaMin, areaMax, areaOrderASC, areaOrderDESC, true);
list.forEach(System.out::println);
return new ResData(200, "ok", list);
}
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>新增业主</title>
<link rel="stylesheet" href="/bootstrap/css/bootstrap.min.css">
<script src="/js/jquery-3.5.1.js"></script>
<script src="/bootstrap/js/bootstrap.js"></script>
<script src="/js/axios.min.js"></script>
<script src="/js/vue.min-v2.5.16.js"></script>
</head>
<body>
<div id="app">
<br>
录入业主基础信息:<br>
用户名: <input type="text" v-model="username">
真实姓名:<input type="text" v-model="realname">
电话号码:<input type="text" v-model="tel"><br>
身份证号:<input type="text" v-model="identity">
性别:
<input type="radio" v-model="gender" value="男">男
<input type="radio" v-model="gender" value="女">女
<br>
备注信息:<textarea v-model="notes" rows="3" cols="22"></textarea><br>
<br>
录入选房信息:<br>
<div>
<!-- 搜索所有可以选择的房子-->
楼栋编号:
<select v-model="buildingId">
<option value="">--请选择楼栋--</option>
<option v-for="building in buildingList" :value="building.id">{{building.num}}-{{building.unit}}</option>
</select>
<button @click="addBuildingId">添加楼栋</button>
<button @click="resetSelectBuildsBtn">重选楼栋</button>
<br>
选中的楼栋为:
<select multiple style="width: 100px" @dbclick="removeSelectedBuildingsBtn">
<option v-for="building in selectedBuildingList" :value="building.id">{{building.num}}-{{building.unit}}</option>
</select>
楼层:
<select v-model="selectFloor">
<option value="">-选择楼层--</option>
<option v-for="floors in floorList" :value="floors">{{floors}}层</option>
</select>
面积:
<input type="text" v-model="areaMin" style="width: 50px" placeholder="小">---
<input type="text" v-model="areaMax" style="width: 50px" placeholder="大">
<button @click="areaASC">面积升序</button>
<button @click="areaDESC">面积降序</button>
<button @click="searchHouseBtn">搜索房子</button>
<button @click="searchHouseBtnClr">重置搜索</button><br>
</div>
<!-- TODO:可以选的房子的左侧的框-->
<!-- canSelectHouse-->
可选的房子为:
<select multiple style="width: 200px" @dblclick="selectHouseDbc">
<option v-for="house in canSelectHouse" :value="house.id">
{{house.num}}--{{house.unit}}--{{house.roomNum}}--{{house.area}}
</option>
</select>
选中的房子为:
<select multiple style="width: 200px" @dblclick="removeSelectedHouseDbc">
<option v-for="house in selectedHouse" :value="house.id">
{{house.num}}--{{house.unit}}--{{house.roomNum}}--{{house.area}}平米
</option>
</select>
<br>
<button @click="add">添加</button>
<button @click="reset">重置</button><br>
<br>
</div>
<script>
let app = new Vue({
el:"#app",
data:{
// 首先是用户的基础信息
username:"",
realname:"",
tel:"",
gender:"男",
identity:"",
buildingId:"",
houseId:"",
notes:"",
// 然后是查询的数据
// 然后是选中的楼栋的id,1栋1单元...
floorList : Array.from({ length: 12 }, (v, i) => i + 1), // 最大有12层楼,因此这里可以选择1到12
selectedBuildingList:[], // List buildingList, // 楼栋
selectFloor:"", // 楼层
areaMin:"", // 面积
areaMax:"", // 面积
// 和排序相关的
areaOrderASC:false,
areaOrderDESC:false,
// 在左侧边框里面显示可以选择的所有房间信息
canSelectHouse:[],
// 在右侧框里面显示已经选择的所有房间信息
selectedHouse:[],
buildingList:[]
},
methods:{
searchHouseBtnClr(){
// 重置搜索
this.selectedBuildingList =[]
this.buildingId = ""
this.selectFloor="" // 楼层
this.areaMin="" // 面积
this.areaMax="" // 面积
// 和排序相关的
this.areaOrderASC=false
this.areaOrderDESC=false
this.queryHouse();
},
// 如果选中了一个楼栋,就添加一个楼栋来,如果有重复的就不添加
addBuildingId(){
let selected = this.buildingList.find(b=>b.id===this.buildingId)
// 这个selected是一个 building对象
console.log(selected)
// 只有selected里面有值,并且没有重复的时候才能添加进来
if (selected && !this.selectedBuildingList.find(b=>b.id===selected.id)){
this.selectedBuildingList.push(selected)
}
},
// 如果双击选中的楼栋,则删除已经选中的楼栋
removeSelectedBuildingsBtn(event){
const optionValue = event.target.value; // 获取双击的对象的值
this.selectedBuildingList = this.selectedBuildingList.filter(building=>building.id != optionValue)
},
// 在可选的房子的框里面双击,则在选中的房子的框里面加这个房子,并且在可选的房子的框里面删除被选的房子
selectHouseDbc(event){
const optionValue = event.target.value; // 获取双击的对象的值
// TODO:注意两点,这个过滤后的还是一个集合,需要获取第一个元素,另外要用== 不要用===
const house = this.canSelectHouse.filter(house=>house.id == optionValue)[0]
console.log(house)
this.selectedHouse.push(house)
this.canSelectHouse = this.canSelectHouse.filter(house=>house.id != optionValue)
console.log(this.canSelectHouse)
console.log(this.selectedHouse)
},
removeSelectedHouseDbc(event){
const optionValue = event.target.value; // 获取双击的对象house
console.log(optionValue)
const house = this.selectedHouse.filter(house=>house.id == optionValue)[0]
this.selectedHouse = this.selectedHouse.filter(house=>house.id !=optionValue)
this.canSelectHouse.push(house)
},
//如果选中了一个房子,就添加一个进来,如果有重复的就不添加
addHouseId(){
const selectedOption = this.allhouseList.find(house=>house.id===this.houseId);
if (selectedOption && !this.houseList.find(house => house.id === selectedOption.id)) {
this.houseList.push(selectedOption);
}
},
removeSelectedOption(event) {
const optionValue = event.target.value;//获取双击的对象的值
console.log(optionValue);
this.houseList = this.houseList.filter(house => house.id != optionValue);
},
add(){
let param = {};
param.username = this.username;
param.realname = this.realname;
param.tel = this.tel;
param.gender = this.gender;
param.identity = this.identity;
param.notes = this.notes;
param.houseList = this.selectedHouse;
axios.post("/owner/add",param)
.then(response=>{
if(response.data.code==200)
{
location.href="/user/ownerPage"
}
else
{
alert(response.data.msg);
}
})
},
// 重新输入用户信息
reset(){
this.username=""
this.realname=""
this.tel=""
this.identity=""
this.notes=""
},
// 重新选择楼栋
resetSelectBuildsBtn(){
this.selectedBuildingList =[]
this.buildingId = ""
},
areaASC(){
this.areaOrderASC=true;
this.areaOrderDESC=false;
this.queryHouse();
},
areaDESC(){
this.areaOrderASC=false;
this.areaOrderDESC=true;
this.queryHouse();
},
// 查询所有可以选择的房间
queryHouse(){
let jsonObj = {}
jsonObj.buildingList=this.selectedBuildingList;
jsonObj.storey=this.selectFloor;
jsonObj.areaMin=this.areaMin;
jsonObj.areaMax=this.areaMax;
jsonObj.areaOrderASC=this.areaOrderASC;
jsonObj.areaOrderDESC=this.areaOrderDESC;
axios.post("/owner/query/house",jsonObj)
.then(response=>{
if (response.data.code==200){
this.canSelectHouse = response.data.data;
}else {
alert(response.data.msg)
}
})
},
searchHouseBtn(){
this.queryHouse();
},
},
mounted(){
// 首先要获得所有可以选择的房子
this.queryHouse();
//查询楼栋下拉列表
axios.get("/build/list/all")
.then(response=>{
this.buildingList = response.data.data;
})
}
});
</script>
</body>
</html>
专门用来前后端交互的实体类
package com.tianju.entity;
import com.alibaba.druid.filter.AutoLoad;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 专门用来和前端交互的一个对象;
* 用于选房页面的复杂查询
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HouseFront {
private List<Building> buildingList;
private Integer storey;
private Double areaMin;
private Double areaMax;
private Boolean areaOrderASC;
private Boolean areaOrderDESC;
}
同源策略:协议、域名(IP)、端口相同即为同源。浏览器的同源策略是一种约定,是浏览器最核心也是最基本的安全功能,如果浏览器少了同源策略,则浏览器的正常功能可能都会受到影响。
跨域是指:浏览器A从服务器B获取的静态资源,包括Html、Css、Js,然后在Js中通过Ajax访问C服务器的静态资源或请求。即:浏览器A从B服务器拿的资源,资源中想访问服务器C的资源。
同源策略是指: 浏览器A从 服务器B获取的静态资源,包括Html、Css、Js,为了用户安全,浏览器加了限制,其中的 Js通过 Ajax只能访问 B服务器的静态资源或请求。即:浏览器A从哪拿的资源,那资源中就只能访问哪。
同源是指:同一个请求协议(如:Http或Https)、同一个Ip、同一个端口,3个全部相同,即为同源。
要点:
package com.tinaju.bm.controller;
import com.tinaju.bm.dto.HttpResp;
import com.tinaju.bm.dto.ResultCode;
import com.tinaju.bm.entity.Book;
import com.tinaju.bm.service.IBookService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
import java.util.List;
@Api(tags = "图书的api接口类")
@RestController
@RequestMapping("/api/book")
@Slf4j
public class BookController {
@Autowired
private IBookService bookService;
@ApiOperation("findPage方法测试")
@ApiImplicitParam(name = "findByPage",value = "分页查询",required = true)
@GetMapping("/findByPage")
public HttpResp findByPage(int currentPage){
List<Book> bookList = bookService.findByPage(currentPage, 5);
return HttpResp.results(ResultCode.BOOK_SUCCESS,new Date(),bookList);
}
@CrossOrigin // TODO:直接在后台允许跨域
@ApiOperation("查询所有图书接口")
@GetMapping("/findAll")
public HttpResp findAll(){
long s1 = System.currentTimeMillis();
List<Book> list = bookService.findAll();
long s2 = System.currentTimeMillis();
log.debug("查询耗时>>>>>>>>>>>"+(s2-s1)+"毫秒");
return HttpResp.results(ResultCode.BOOK_SUCCESS,new Date(),list);
}
}
方法类 | 方法名称 | 必填 | 请求头字段 | 说明 |
---|---|---|---|---|
CorsRegistry | addMapping | 是 | 无, 非Cors属性, 属于SpringBoot配置 | 配置支持跨域的路径 |
CorsRegistration | allowedOrigins | 是 | Access-Control-Allow-Origin | 配置允许的源 |
CorsRegistration | allowedMethods | 是 | Access-Control-Allow-Methods | 配置支持跨域请求的方法, 如:GET、POST,一次性返回 |
CorsRegistration | maxAge | 否 | Access-Control-Max-Age | 配置预检请求的有效时间 |
CorsRegistration | allowCredentials | 否 | Access-Control-Allow-Credentials | 配置是否允许发送Cookie, 用于 凭证请求 |
CorsRegistration | allowedHeaders | 否 | Access-Control-Request-Headers | 配置允许的自定义请求头, 用于 预检请求 |
CorsRegistration | exposedHeaders | 否 | Access-Control-Expose-Headers | 配置响应的头信息, 在其中可以设置其他的头信息 |
implements WebMvcConfigurer
addCorsMappings(CorsRegistry registry)
package com.tinaju.bm.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 解决跨域的问题
*/
@Configuration
public class BmWebConfig implements WebMvcConfigurer {
// 可以不写实现类的原因是 default void configurePathMatch(PathMatchConfigurer configurer)
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**") // 所以级别下面的都允许跨域
.allowedOrigins("*") // 允许跨域
.allowedMethods("GET","POST","PUT","DELETE")
.allowedHeaders("*"); // 允许头部
}
}
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
port: 8080,
proxy: {
"/api": { // 1.修改端口号
// (后端服务器地址)
// target: 'http://124.70.138.34:10050', // 2.配置代理服务器
target: 'http://127.0.0.1:10050', // 2.配置代理服务器
changeOrigin: true, // 3.允许跨域请求
pathRewrite: {
// '^/api': '/' // 4.把代理路径的api删除,类似正则表达
}
}
}
}
})
网页状态码——200、302、304、400、404、405、415、500,跨域