浏览器异常:TypeError 问题描述 封装好的request.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import axios from 'axios' export function request (config ) { const instance = axios.create({ baseURL: 'http://127.0.0.1:8181' , timeout: 5000 }) instance.interceptors.request.use(config => { return config }, err => { console .log(err); }) instance.interceptors.response.use(res => { return res.data }, err => { console .log(err); }) return instance(config) }
axios发送请求代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import {request} from "@/network/request" ;export default { name: "Blogs" , data() { return { currentPage: 1 , total: 0 , size: 4 , pages: 0 , blogs: [] } }, methods: { getBlogs(currentPage, size) { request({ url: '/getBlogs?currentPage=' + currentPage + '&size=' + size }).then(res => { this .total = res.data.data.total this .size = res.data.data.size this .pages = res.data.data.pages this .blogs = res.data.data.records }) }, },
解决思路
起初我以为是total这个属性读取不到,将this赋值给_this,在request里再使用 _this.total访问,还是报错;
于是去百度,看到别人报这个错是因为使用Vue.use(axios)
,改为原型全局注册后就不报错了:Vue.prototype.$http = axios
因为暂时想不出解决方法,所以我放弃使用封装的request.js,改用下面这种方法来使用axios:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' import axios from 'axios' Vue.prototype.$http = axios axios.defaults.baseURL = 'http://localhost:8181' axios.defaults.withCredentials = true Vue.config.productionTip = false new Vue({ router, store, render: h => h(App) }).$mount('#app' )
1 2 3 4 this .$http.get('http://localhost:8181/blogs' ).then(res => { console .log(res) })
请求400 问题描述 后端:
前端:
1 2 3 4 5 6 7 8 9 10 11 12 13 data() { return { ruleForm: { username: '' , password: '' , } }; }, methods: { submitForm(formName) { this .$refs[formName].validate((valid ) => { if (valid) { this .$http.post('/login' , this .ruleForm).then((res ) => {
登录页面提交表单后:
解决方法
可以看到图中的Content-Type:application/json
如果前端传入的是json数据(Content-Type:application/json ),那么后端使用@RequestBody HashMap<String, String> map
或者@RequestBody User user
进行接收。
如果前端传入的是简单类型数据(Content-Type:application/x-www-form-urlencoded ),那么后端可以使用@RequestParam("id") String id
。
spring boot一些注解的详解
跨域与拦截器导致跨域失败 拦截器和跨域的配置代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package top.nanzx.blog.config;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.web.servlet.HandlerInterceptor;import top.nanzx.blog.entity.Admin;import top.nanzx.blog.service.AdminService;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@Component public class AdminLoginInterceptor implements HandlerInterceptor { @Autowired AdminService adminService; @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Cookie[] cookies = request.getCookies(); Admin admin = adminService.getOne(new QueryWrapper<Admin>().eq("id" , 1 )); String token = admin.getToken(); for (Cookie c : cookies) { if (c.getName().equals("token" )) { return token.equals(c.getValue()); } } return false ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package top.nanzx.blog.config;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.CorsRegistry;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration public class Config implements WebMvcConfigurer { @Autowired AdminLoginInterceptor adminLoginInterceptor; @Override public void addCorsMappings (CorsRegistry registry) { registry.addMapping("/**" ) .allowedOriginPatterns("*" ) .allowedMethods("GET" , "POST" , "HEAD" , "PUT" , "DELETE" , "OPTIONS" ) .allowCredentials(true ).allowedOrigins() .maxAge(3600 ) .allowedHeaders("*" ); } @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(adminLoginInterceptor).excludePathPatterns("/getBlogs" ,"/getBlog/*" ,"/login" ,"/getAdminMess" ); } }
产生问题
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
预检请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。
在非简单请求的时候,总是报跨域访问的错误:
预检请求被拦截器拦截后,服务器端没有给浏览器返回必要的跨域指示信息,浏览器没收到指示信息就认为服务器不允许跨域请求,就会报错。
解决 拦截器截取到请求时先进行判断,如果是option请求的话,则放行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package top.nanzx.blog.config;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.web.servlet.HandlerInterceptor;import top.nanzx.blog.entity.Admin;import top.nanzx.blog.service.AdminService;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@Component public class AdminLoginInterceptor implements HandlerInterceptor { @Autowired AdminService adminService; @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if ("OPTIONS" .equals(request.getMethod().toUpperCase())) { System.err.println("OP:OK" ); return true ; } Cookie[] cookies = request.getCookies(); Admin admin = adminService.getOne(new QueryWrapper<Admin>().eq("id" , 1 )); String token = admin.getToken(); for (Cookie c : cookies) { if (c.getName().equals("token" )) { return token.equals(c.getValue()); } } return false ; } }
参考文章:跨域与拦截器导致跨域失败
response响应异常 1 java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
下载文件接口如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Override public JsonResult download (int courseId, String fileName, HttpServletResponse response) { Means means = meansDao.queryFile(fileName, courseId); File file = new File(means.getFilePath() +'\\' + means.getFileName()); if (!file.exists()) { return new JsonResult(1 , "文件不存在!" , null ); } response.setContentType("application/force-download" ); response.addHeader("Content-Disposition" , "attachment;fileName=" + new String(fileName.getBytes(StandardCharsets.UTF_8), Charset.forName("ISO8859-1" ))); byte [] buffer = new byte [1024 ]; try { FileInputStream fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis); OutputStream os = response.getOutputStream(); int i = bis.read(buffer); while (i != -1 ) { os.write(buffer, 0 , i); i = bis.read(buffer); } fis.close(); } catch (Exception e) { e.printStackTrace(); return new JsonResult(1 , "文件下载失败。" , null ); } return new JsonResult(0 , "文件下载成功。" , null ); }
出现这个错误,应该是多次response导致的,可以这么理解,http server发送response后就关闭了socket,这个时候再次发送response给http client就会出现这个问题。 而我代码中传输完文件后,流一关闭,socket也就关闭了。
解决办法:如果下载出现问题,返回 JsonResult 让前台知道问题详情,下载成功的话,直接return null 就Ok了。
修改代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Override public JsonResult download (int courseId, String fileName, HttpServletResponse response) { Means means = meansDao.queryFile(fileName, courseId); File file = new File(means.getFilePath() +'\\' + means.getFileName()); if (!file.exists()) { return new JsonResult(1 , "文件不存在!" , null ); } response.setContentType("application/force-download" ); response.addHeader("Content-Disposition" , "attachment;fileName=" + new String(fileName.getBytes(StandardCharsets.UTF_8), Charset.forName("ISO8859-1" ))); byte [] buffer = new byte [1024 ]; try { FileInputStream fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis); OutputStream os = response.getOutputStream(); int i = bis.read(buffer); while (i != -1 ) { os.write(buffer, 0 , i); i = bis.read(buffer); } fis.close(); } catch (Exception e) { e.printStackTrace(); return new JsonResult(1 , "文件下载失败。" , null ); } return null ; }
前端Json数据解析异常 1 2 3 4 Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.lang.String` out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_ARRAY token at [Source: (PushbackInputStream); line: 1, column: 101] (through reference chain: java.util.HashMap["object"])]
出错原因:前端传值类型与后端接收值的类型不一致,无法解析
这是我前端传输的数据:
1 2 3 4 5 6 7 8 9 10 11 <script> export default { data() { return { ruleForm: { courseName: '', classes: [], }, } } }
这是我后端对应的接口:
1 2 3 4 @PostMapping("/createCourse") public JsonResult createCourse (@RequestBody HashMap<String, String> map) { return teacherService.createCourse(map); }
前端数组类型,后端无法用string类型接收。
解决方法:
1 2 3 4 5 6 7 8 9 10 11 12 前端=>后端: //格式化数组--转字符串 this.tag = this.tag.join(','); 后端=>前端: //格式化字符串--转数组 for(var i in data){ if(data[i].tag!==""){ data[i].tag = data[i].tag.split(','); } }
项目代码解决:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <script> export default { name: "AddCourse", data() { return { ruleForm: { courseName: '', classes: [], }, }; }, computed: { formData() { return { courseName: this.ruleForm.courseName, classes: this.ruleForm.classes.join(','), } } }, methods: { submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { this.$http.post('/teacher/createCourse', this.formData).then((res) => { ... } </script>