Vue.js开发技巧Vue.jsVue.js专区

07、vue前后端整合

2018-04-21  本文已影响314人  spilledyear

vue-monitor作为前端应用,vue-admin作为后台提供标准接口,这是标准的前后端分离解决方案。本文主要讲解两个应用整合的过程。按理来说,也不存在什么整合的过程,这本就是两个独立的应用,只需要将两个应用分别启动就可以正常访问了,事实也确实如此,但再次之前需要先解决一个问题:跨域问题。

启动vue-monitor

进入vue-monitor跟目录,执行以下命令:

npm run dev

这样前端应用就启动了,这时候前端时完全依赖于node的。如果想不依赖于node也可以,可以执行

 npm run build 

这样就可以将前端应用打包成静态资源,然后将这些静态资源部署到nginx服务器,同样可以正常访问,这里用的时第一种方法。

启动vue-admin

其实就是启动一个spring-boot应用,启动方法很多种,这里是在IDEA中直接 运行的

image.png

至此,两个应用已经完全启动了。

前端调用后端接口

在我们的前端应用中,有一个登录界面,效果如下

注意,随着应用的开发,可能之后这些代码会被删除或者覆盖,这里只是 为了说明前端调用后端应用的一个示例。

image.png

当点击的登录按钮的时候,调用后台的登录接口,后台对应 的接口信息如下

image.png

我们期望的结果是:点击登录的时候,可以访问到后台这个接口。但是当我们点击登录按钮的时候,发现界面没有任何响应,后台也没有任何日志输出,这已经可以说明接口没有调用成功了。打开浏览器调试工具,发现有如下错误:

image.png

具体内容如下:

Failed to load http://localhost:8081/api/system/login: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8082' is therefore not allowed access. The response had HTTP status code 403. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

这是一个跨域问题,我们前端应用的端口是 8002,后台应用的端口是8001,存在跨域问题。怎么解决?有很多方法:

这里用了CORS 解决跨域问题。第二种方法没有用过,之后补上吧。

CORS解决跨域

使用cors解决跨域问题,需要在后台将 前端域名 设置成可以访问。
先在后台添加一个 过滤器:

package com.hand.sxy.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 不加 @Configuration 注解不生效
 *
 * @author spilledyear
 * @date 2018/4/21 18:42
 */
@Configuration
@WebFilter(urlPatterns = "/*")
public class CorsFilter implements Filter {
    private Logger logger = LoggerFactory.getLogger(CorsFilter.class);

    @Override
    public void init(FilterConfig arg0) throws ServletException {
    }


    @Override
    public void doFilter(ServletRequest request, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        logger.debug("跨域拦截");

        HttpServletResponse response = (HttpServletResponse) res;

        // 指定允许其他域名访问
        response.setHeader("Access-Control-Allow-Origin", "*");
        // 响应类型
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE");
        // 响应头设置
        response.setHeader("Access-Control-Allow-Headers", "token,Content-Type,Access-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Max-Age,authorization");
        response.setHeader("Access-Control-Max-Age", "3600");

        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }
}

这里有一个需要注意的地方,需要添加 @Configuration注解,要不然这个filter 是不生效了。添加过滤器后,重新启动后台应用,再次点击登录按钮,发现还是报错了,报错信息如下:

image.png
Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'http://localhost:8082' is therefore not allowed access.

大概意思是说, 后台中的响应头不能设置 Access-Control-Allow-Origin 的值为 通配符 *。这时候对后台应用该稍作修改

// 指定允许其他域名访问,因为前端应用域名是 http://localhost:8082
response.setHeader("Access-Control-Allow-Origin", "http://localhost:8082");

再次重新启动该后台应用,在前端再次访问,发现还是失败了,报错信息如下:

Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. Origin 'http://localhost:8082' is therefore not allowed access

提示当 request's credentials mode 的值是  'include' 的时候, Access-Control-Allow-Credentials 在响应头中必输设置成 true。

这让我想到一个问题,那就是在前端应用中 request 请求头设置问题,内容如下:

let request = {
  credentials: 'include',
  method: type,
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  },
  mode: "cors",
  cache: "force-cache"
}

果然设置了 credentials: 'include'。
可是这个参数我并不太清楚是干嘛的,解决方法有两个:
1、在后台添加一行代码,设置 Access-Control-Allow-Credentials 的值为 true

response.setHeader("Access-Control-Allow-Credentials", "true");

2、在前端的请求头中,去除 credentials: 'include' 这个设置

let request = {
  method: type,
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  },
  mode: "cors",
  cache: "force-cache"
}

至此,跨域问题得到解决。再次点击登录按钮,查看后台日志:

image.png

从上图中的日志中可以看出,成功的调用了 登录接口。

接收参数

跨域问题是成功解决了,但是参数还没有传过来。为了将用户名和密码传到后台测试,对后台代码稍作修改。

//对查询方法稍作修改
<select id="query" resultMap="BaseResultMap" parameterType="com.hand.sxy.account.dto.User">
    SELECT * FROM USER WHERE USERNAME = #{username} AND PASSWORD = #{password}
</select>

然后其它地方也稍作修改,具体的请看源码,controller 中的代码如下

package com.hand.sxy.system.controller;

import com.hand.sxy.account.dto.User;
import com.hand.sxy.system.dto.Result;
import com.hand.sxy.system.service.ILoginService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * @author spilledyear
 * @date 2018/4/21 12:58
 */
@RestController
public class LoginController {
    private Logger logger = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private ILoginService loginService;

    @RequestMapping(value = "/api/system/login", method = RequestMethod.POST)
    public Result login(HttpServletRequest request, User user) {

        List<User> userList = loginService.login(user);
        Result result = new Result(userList);

        if (userList == null || userList.isEmpty()) {
            logger.info("登录失败,用户名或密码错误");
            result.setSuccess(false);
            result.setMessage("用户名或密码错误");
        }
        logger.info("登录成功");
        return result;
    }

}

修改之后,重启后台系统,在前端界面输入用户名和密码,点击登录按钮,用浏览器调试工具发现前端调用了请求,参数也传了,但是在后台打断点发现参数没有穿过来。
解决方法:方法加上 @RequestBody 注解

package com.hand.sxy.system.controller;

import com.hand.sxy.account.dto.User;
import com.hand.sxy.system.dto.Result;
import com.hand.sxy.system.service.ILoginService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * @author spilledyear
 * @date 2018/4/21 12:58
 */
@RestController
public class LoginController {
    private Logger logger = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private ILoginService loginService;

    @RequestMapping(value = "/api/system/login", method = RequestMethod.POST)
    public Result login(HttpServletRequest request, @RequestBody User user) {

        List<User> userList = loginService.login(user);
        Result result = new Result(userList);

        if (userList == null || userList.isEmpty()) {
            logger.info("登录失败,用户名或密码错误");
            result.setSuccess(false);
            result.setMessage("用户名或密码错误");
        }
        logger.info("登录成功");
        return result;
    }

}

@RequestBody用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上,然后再把HttpMessageConverter返回的对象数据绑定到 controller中方法的参数上。

@RequestBody注解是否必须要,根据request header Content-Type的值来判断
GET、POST方式提时

PUT方式提交时

说明:request的body部分的数据编码格式由header部分的Content-Type指定;

@ResponseBody用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。 在Controller中返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用。

上一篇下一篇

猜你喜欢

热点阅读