vue-router之钩子函数

[TOC]

钩子函数

路由的切换过程,本质上是执行一系列路由钩子函数,钩子函数总体上分为两大类:

  • 全局的钩子函数
  • 组件的钩子函数

全局的钩子函数定义在全局的路由对象中,组件的钩子函数则定义在组件的route选项中。

全局钩子函数

全局钩子函数有2个:

  • beforeEach:在路由切换开始时调用
    项目中用到的路由钩子函数来进行登陆判断
router.beforeEach((to, from, next) => {
  //如果跳转到登陆界面
  if (to.path == '/login') {
    sessionStorage.removeItem('user');
  }
  let user = JSON.parse(sessionStorage.getItem('user'));
  if (!user && to.path != '/login') {
    next({ path: '/login' })
  } else {
    next()
  }
})
  • afterEach:在每次路由切换成功进入激活阶段时被调用

    组件的钩子函数

    组件的钩子函数一共6个:

  • data:可以设置组件的data

  • activate:激活组件
  • deactivate:禁用组件
  • canActivate:组件是否可以被激活
  • canDeactivate:组件是否可以被禁用
  • canReuse:组件是否可以被重用

    切换对象

    每个切换钩子函数都会接受一个 transition 对象作为参数。这个切换对象包含以下函数和方法:

  • transition.to
    表示将要切换到的路径的路由对象。

  • transition.from
    代表当前路径的路由对象。
  • transition.next()
    调用此函数处理切换过程的下一步。
  • transition.abort([reason])
    调用此函数来终止或者拒绝此次切换。
  • transition.redirect(path)
    取消当前切换并重定向到另一个路由。

钩子函数的执行顺序

  1. 可重用阶段
    检查当前的视图结构中是否存在可以重用的组件 ,canReuse
  2. 验证阶段
    检查当前的组件是否能够停用以及新组件是否可以被激活, canDeactivatecanActivate
  3. 激活阶段
    一旦所有的验证钩子函数都被调用而且没有终止切换,切换就可以认定是合法的。路由器则开始禁用当前组件并启用新组件。
    此阶段对应钩子函数的调用顺序和验证阶段相同,其目的是在组件切换真正执行之前提供一个进行清理和准备的机会。界面的更新会等到所有受影响组件的deactivateactivate钩子函数执行之后才进行。
  4. 执行data

ES6新知识get

[TOC]

ECMAScript6 作为新的Javascript标准,与ECMAScript5有着很大区别,项目实践中积累新知识。

const,var,le区别

  • const定义的变量不可以修改,而且必须初始化。常用来定义常量。类似于java中常定义的Constants常量。
  • var定义的变量可以修改,如果不初始化会输出undefined,不会报错。
  • let是块级作用域,函数内部使用let定义后,对函数外部无影响。
let c = 3;
console.log('函数外let定义c:' + c);//输出c=3
function change(){
let c = 6;
console.log('函数内let定义c:' + c);//输出c=6
} 
change();
console.log('函数调用后let定义c不受函数内部定义影响:' + c);//输出c=3

箭头函数

ES6出来之后,箭头函数频繁出现啊,经常看的我云里雾里的,狠下决心好好弄清楚这个到底是什么鬼,认真看看其实也就是匿名函数的简化表达而已。比如,

var str = a =>a + a;

相当于:

var str = function(a) {
    return a+a;
}

虽然理解了,还是很不习惯啊。。。

shiro之roles实现or关系的角色过滤

roles参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,每个参数通过才算通过,相当于hasAllRoles()方法。shiro的角色过滤是and的关系

而最近根据项目需求,要求订阅号管理模块,多个角色都可以进行管理,角色的过滤非and关系而是or的关系,applicationContext.xml中roles[“管理员”,”订阅号”]不能实现或者关系的角色过滤,需要自定义继承AuthorizationFilter的Filter。

CustomRolesAuthorizationFilter

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;

public class CustomRolesAuthorizationFilter extends AuthorizationFilter
{

 @Override
 protected boolean isAccessAllowed(ServletRequest request,
   ServletResponse response, Object mappedValue) throws Exception
 {
  Subject subject = getSubject(request, response);  
        String[] rolesArray = (String[]) mappedValue;  

        if (rolesArray == null || rolesArray.length == 0) 
        {  
            return true;  
        }  
        for(int i=0;i<rolesArray.length;i++)
        {
            if(subject.hasRole(rolesArray[i]))
            {  
                return true;  
            }  
        }  
        return false;  
 }
}

applicationContext.xml

<!--自定义的filter-->
<bean id="roleOrFilter" class="com.city.login.CustomRolesAuthorizationFilter"> 
 </bean>

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean" depends-on="roleOrFilter">
    <!-- securityManager -->
    <property name="securityManager" ref="securityManager" />
    <!-- 登录路径 -->
    <property name="loginUrl" value="/login.jsp" />
    <!-- 登录成功后跳转路径 -->
    <property name="successUrl" value="/index.jsp" />
    <!-- 授权失败跳转路径 -->
    <property name="unauthorizedUrl" value="/login.jsp" />
    <property name="filters">
        <util:map>
            <entry key="authc" value-ref="formAuthenticationFilter" />
            <entry key="sysUser" value-ref="sysUserFilter" />
            <entry key="kickout" value-ref="kickoutSessionControlFilter" />
            <entry key="roleOrFilter" value-ref="roleOrFilter"/>
        </util:map>
    </property>

    <!-- 过滤链定义 -->
    <property name="filterChainDefinitions">
        <value>
            /login.jsp = anon
            /logout = logout 
            <!-- authc表示需要认证的服务 -->
            /rest/credit/** = authc 
            /rest/Uploadinfo/** = authc
            /userManager.jsp = kickout,roles["管理员"]
            /subscribe.jsp = kickout,roleOrFilter["订阅号","管理员"]
            /lost.jsp = kickout,authc,roles["失物招领"]
            /*.jsp = kickout,authc
        </value>
    </property>
</bean>

这样,roleOrFilter就可以实现or的关系,即订阅号角色和管理员角色都可以访问subscribe.jsp页面。

java实现文件下载

根据项目需求实现word文档下载功能,这是java的后台实现,前台调用采用ajax方式,参数是文件名

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("laws")
public class LawsRest {
    private static Logger logger = LoggerFactory.getLogger(LawsRest.class);

    @RequestMapping(value = "/downloadFile/{value}",method=RequestMethod.GET)
    @ResponseBody
    public void downloadFile(@PathVariable("value") String value, HttpServletRequest request,
            HttpServletResponse response) throws IOException {

        String directory = request.getSession().getServletContext().getRealPath("/");
        String fileName = value+".doc";
        String filePath = directory + "laws\\" + fileName;

        //设置向浏览器端传送的文件格式
        //判断文件是否存在
        File file=new File(filePath);
        if(!file.exists()){
            fileName = value+".docx";
            filePath = directory + "laws\\" + fileName;
        }
        logger.info(value);
        logger.info(filePath);
        response.setContentType("application/msword");  
        response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        FileInputStream in = new FileInputStream(filePath);
        // 创建输出流
        OutputStream out = response.getOutputStream();
        // 创建缓冲区
        byte buffer[] = new byte[1024];
        int len = 0;
        // 循环将输入流中的内容读取到缓冲区当中
        while ((len = in.read(buffer)) > 0) {
            // 输出缓冲区的内容到浏览器,实现文件下载
            out.write(buffer, 0, len);
        }
        out.flush();

        in.close();
        out.close();
    }
}

vue-cli搭建vue项目遇到的问题详解

[TOC]

安装使用

vue初探,即使照着官方教程一步步来,依然是错误百出,记得最好是将node.js更新到最新版本,不然各种报错不知道怎么解决。
果不其然,再经过将环境整个更新之后在根据以下官网上的步骤一步步来就木有问题了。

# 全局安装 vue-cli
$ npm install --global vue-cli

国内的npm网速不稳定,推荐使用淘宝镜像,点击查看详情

# 如果安装失败,可以先clean一下,清理缓存,再重新安装
$ npm cache clean 
$ npm install -g cnpm --registry=https://registry.npm.taobao.org  
$ cnpm -v //查看是否安装成功

# 创建一个基于webpack 模板的新项目,其中webpack是模板名称
$ vue init webpack first
# 安装依赖,走你
$ cd first
$ cnpm install
$ cnpm run dev

浏览器成功显示以下页面。
vue-cli:官方发布的一个vue脚手架帮助我们快速搭建vue项目,可以自动启用热加载功能。
webpack:是模板名称,可以到 vue.js 的 GitHub 上查看更多的模板https://github.com/vuejs-templates

enter description here

vue-cli

Vue.js官方的脚手架生成工具。
vue-cli提供了5中脚手架:

  • webpack: 基于webpack和vue-loader的目录结构,而且支持热部署、代码检查、测试及css抽取。生成的项目结构如下图项目文件结构所示。
  • webpack-simple
  • browerify
  • browerify-simple
  • simple

    项目分析

    项目文件结构

enter description here
main.js:默认的主入口文件。

项目路径说明

enter description here

使用官方推荐的引入方法:

<script src="https://unpkg.com/vue/dist/vue.js"></script>

打包上线

自己的项目文件都需要放到 src 文件夹下,项目开发完成之后,可以输入 npm run build 来进行打包工作

npm run build

打包完成后,会生成dist文件夹,如果已经修改了文件路径,可以直接打开本地文件查看项目上线时,只需要将 dist 文件夹放到服务器就行了。

app挂载

new Vue({
    el: '#app',
    components: {
        app: App
    },
    router,
}).$mount('#app')

等价于

new Vue({
    el: '#app',    //这里绑定的是index.html中的id为app的div元素
    router,
    render: h => h(App)
})

vue-resource与axios

vue更新到2.0之后,作者就宣告不再对vue-resource更新,而是推荐的axios

axios:基于 Promise 的 HTTP 请求客户端,可同时在浏览器和 node.js 中使用。详情查看 axios文档
类似于jquery中的ajax功能,是一个小小的ajax组件。

structs2之Action与ActionContext

[TOC]

Action

structs2的核心功能就是action,主要是编写一个个action来处理逻辑。
action类通常都要实现com.opensymphony.xwork2.Action接口,并实现该接口中的execute()方法。

Struts2并不是要求所有编写的action类都要实现Action接口,也可以直接编写一个普通的Java类作为action,只要实现一个返回类型为String的无参的public方法即可:public String xxx()

在实际开发中,action类很少直接实现Action接口,通常都是从com.opensymphony.xwork2.ActionSupport类继承,ActionSupport实现了Action接口和其他一些可选的接口,提供了输入验证,错误信息存取,以及国际化的支持,选择从ActionSupport继承,可以简化action的定义。

项目中常用的action基类,封装了对HttpServletRequest、HttpServletResponse、HttpSession的获取,所有的action都从baseAction继承。

public class BaseAction extends ActionSupport{
    protected final transient Log log = LogFactory.getLog(getClass());

      /**
     * 获取请求
     * Convenience method to get the request
     * @return current request
     */
    protected HttpServletRequest getRequest() {
        return ServletActionContext.getRequest();
    }

    /**
     * 获取回应
     * Convenience method to get the response
     * @return current response
     */
    protected HttpServletResponse getResponse() {
        return ServletActionContext.getResponse();
    }

    /**
     * 获取session
     * @return
     */
    protected HttpSession getSession() {
        return getRequest().getSession();
    }
}

@ParentPackage(value = "json-default")
@Controller
@Scope("prototype")
@Action(value = "/loginAction", results = { @Result(name = "success", type = "redirect",location = "/index.jsp"),
@Result(name = "input", type = "redirect", location = "/login.jsp"),
@Result(name = "error", type = "redirect", location = "/login.jsp") }, exceptionMappings = {
        @ExceptionMapping(exception = "java.lang.Exception", result = "error") })
public class LoginAction extends BaseAction {
    ...
    public String login() throws Exception {
        ...
        HttpServletRequest request= getRequest(); 
        ...
        return SUCCESS;
    }

     public String logout() throws Exception {
        ...
        HttpServletRequest request= getRequest(); 
        ...
        return SUCCESS;
    }
}

开发好action之后,好需要对action进行配置,以告诉Struts2框架,针对某个URL的请求应该交由哪个action进行处理。@Action可以写在类名上,也可以写在方法名上。
继承baseAction类的action类中则可以直接getRequest()、getResponse()、getSession()直接获取到Http请求或者响应。

Action原型

Prototype 原型 每次请求都会创建一个新的Action对象,俗称“多例”。一个注册的例子,如果没加上这个注解,注册完成后,下一个请求再注册一次,Action里会保留上一次注册的信息..
Singleton 原型: 当第一次请求时,创建servlet对象之后每次都使用该对象即可。spring 默认scope 是单例模式

@Scope("prototype")

添加@Scope注解,如上LoginAction类所示。

ActionContext

在Web应用程序开发中,除了将请求参数自动设置到Action的字段中,我们往往也需要在Action里直接获取请求(Request)或会话 (Session)的一些信息, 甚至需要直接对JavaServlet Http的请求(HttpServletRequest),响应(HttpServletResponse)操作。

ActionContext context = ActionContext.getContext();
Map params = context.getParameters();
String username = (String) params.get("username");
Map session = context.getSession();

ActionContext(com.opensymphony.xwork.ActionContext)是Action执行时的上下文,上下文可以看作是一个容器(其实我们这里的容器就是一个Map而已),它存放放的是Action在执行时需要用到的对象。

ServletActionContext

ServletActionContext(com.opensymphony.webwork. ServletActionContext),这个类直接继承了我们上面介绍的ActionContext,它提供了直接与Java Servlet相关对象访问的功能,它可以取得的对象有:

  1. javax.servlet.http.HttpServletRequest:HTTPservlet请求对象
  2. javax.servlet.http.HttpServletResponse;:HTTPservlet相应对象
  3. javax.servlet.ServletContext:Servlet 上下文信息
  4. javax.servlet.ServletConfig:Servlet配置对象
  5. javax.servlet.jsp.PageContext:Http页面上下文

    从ServletActionContext里取得Servlet的相关对象:

HttpServletRequest request = ServletActionContext. getRequest();
HttpSession session = ServletActionContext.getRequest().getSession();

如上baseAction类所示。

线程安全

ActionContext是线程安全的,而ServletActionContext继承自ActionContext,所以ServletActionContext也线程安全,线程安全要求每个线程都独立进行,所以req的创建也要求独立进行,所以ServletActionContext.getRequest()这句话不要放在构造函数中,也不要直接放在类中,而应该放在每个具体的方法体中(eg:login()、queryAll()、insert()等),这样才能保证每次产生对象时独立的建立了一个request。

如上LoginAction类所示,HttpServletRequest request= getRequest();放在login(),logout()方法体里面,不能放在之外。

servlet和Structs2

刚好抽空总结一下这些知识点,项目开发中就会更加清晰,说道这里就难免想到servlet,毕竟Structs2是在servlet上发展起来的。看到一个很不错的分析关于servlet和Structs2,分享一下。

servlet分析

客户端—>web容器–>web.xml–>servlet来处理 —–>model–>数据库

structs2分析

客户端—–>web容器—>web.xml–>struts2过滤器—>struts.xml—>Action—>model—>数据库

list<Object>的排序

list集合的排序
当集合中存放的是Object复杂对象时,集成Comparable接口来实现排序。
例如:

@Entity
@Table(name = "guide")
public class Guide implements java.io.Serializable,Comparable<Guide> {

    private Integer id;
    private Integer number;//排序序号
    ...(此处省略set,get方法)

    @Override
    public int compareTo(Guide o) {
        int i = this.getNumber() - o.getNumber();// 先按照number排序
        if (i == 0) {
            return this.getId() - o.getId();// 如果number相等了再用id进行排序
        }
        return i;
    }

}

Collections.sort(list);直接排序

@RequestMapping(value = "/queryAllDepart",method=RequestMethod.GET)
@ResponseBody
public JSONArray queryAllDepart(){

    List<Department> list = departServiceImpl.queryAll();
    Collections.sort(list);
    JSONArray lostJSONList = JSONArray.fromObject(list, Common.getJsonConfig());
    return lostJSONList;
}

shiro实现SSL登陆

考虑项目安全需求,将http请求转为https,项目中已集成的shiro框架已实现SSL登陆,来看下具体实现吧。

tomcat的server.xml文件配置。将http的8080端口调转到https的8443端口

<Connector acceptCount="500" connectionTimeout="20000" enableLookups="false" maxThreads="400" port="8080" protocol="HTTP/1.1" URIEncoding="UTF-8" redirectPort="8443"/>

<Connector SSLEnabled="true" acceptCount="500" clientAuth="false" keystoreFile="D:\localhost.keystore" keystorePass="aerors123" maxThreads="400" port="8443" protocol="HTTP/1.1" scheme="https" secure="true" sslProtocol="TLS"/>

applicationContext.xml中添加shiro相关配置

<bean id="sslFilter" class="org.apache.shiro.web.filter.authz.SslFilter">
        <property name="port" value="8443" />
    </bean>

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <!-- securityManager -->
    <property name="securityManager" ref="securityManager" />
    <!-- 登录路径 -->
    <property name="loginUrl" value="/login.jsp" />
    <!-- 登录成功后跳转路径 -->
    <property name="successUrl" value="/index.jsp" />
    <!-- 授权失败跳转路径 -->
    <property name="unauthorizedUrl" value="/login.jsp" />
    <property name="filters">
        <util:map>
            <entry key="authc" value-ref="formAuthenticationFilter" />
            <entry key="sysUser" value-ref="sysUserFilter" />
            <entry key="kickout" value-ref="kickoutSessionControlFilter" />
            <entry key="ssl" value-ref="sslFilter" />
        </util:map>
    </property>
    <!-- 过滤链定义 -->
    <property name="filterChainDefinitions">
        <value>
            /login.jsp = ssl,anon
            /logout = logout
            /*.js = anon
            /*.css = anon
            /error.jsp= anon
            /mapdaohang.jsp= anon
            /unauthor.jsp= anon
            <!-- authc表示需要认证的链接 -->
            /lost.jsp = kickout,authc,roles["lost"]
            /*.jsp = kickout,authc
        </value>
    </property>
</bean>

在filters中添加<entry key="ssl" value-ref="sslFilter" />即可在filterChainDefinitions中直接引用.

推荐个详细教程:第十四章 SSL——《跟我学Shiro》

URL路径含中文,requset.getParameter()乱码

项目最近碰到了一个以前从来没有注意到的问题,就是URL路径包含中文的问题,虽说不建议路径名中包含中文,但有的时候也不可避免的出现这种问题,找到一个管用的方法,修改tomcat的server.xml文件,添加 URIEncoding="UTF-8"属性。如下所示:

<Connector acceptCount="500" connectionTimeout="20000" enableLookups="false" maxThreads="400" port="8080" protocol="HTTP/1.1" URIEncoding="UTF-8" redirectPort="8443"/>

Java与JSON对象相互转换

json字符串——>json对象 JSONObject.fromObject()

JSONObject jsonObj = JSONObject.fromObject(value);

rest中@RequestBody接收的就是一个Json对象的字符串,所以需要先转换为json对象

json对象——>JavaBean对象 JSONObject.toBean()

PublicGarden publicgarden = (PublicGarden) JSONObject.toBean(jsonObj,PublicGarden.class);
|