• 服务网关ZuulFilter过滤器--pre/post/error的用法(校验请求信息,获取路由后的请求/响应信息,处理服务网关异常)


    微服务中Zuul服务网关一共定义了四种类型的过滤器:

    • pre:在请求被路由(转发)之前调用
    • route:在路由(请求)转发时被调用
    • error:服务网关发生异常时被调用
    • post:在路由(转发)请求后调用

     我在项目中用到了,pre/error/post三种类型,先记录一下

    pre过滤器主要是用来校验各种信息的

    import com.alibaba.fastjson.JSONObject;
    import com.dkjk.gateway.context.ResponseBean;
    import com.dkjk.gateway.domain.DockCompanyService;
    import com.dkjk.gateway.domain.UserService;
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.stereotype.Component;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    import javax.annotation.Resource;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * @author: qjc
     * @createTime: 2019/4/13 16:08
     * @Description: 接口安全验证过滤器
     */
    @Component
    @Slf4j
    public class ValidFilter extends ZuulFilter {
    
        @Override
        public String filterType() {
            return "pre";
        }
    
        @Override
        public int filterOrder() {
            return 0;
        }
    
        @Override
        public boolean shouldFilter() {
            // 进行跨域请求的时候,并且请求头中有额外参数,比如token,客户端会先发送一个OPTIONS请求来探测后续需要发起的跨域POST请求是否安全可接受
            // 所以这个请求就不需要拦截,下面是处理方式
            RequestContext requestContext = RequestContext.getCurrentContext();
            HttpServletRequest request = requestContext.getRequest();
            if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
                log.info("OPTIONS请求不做拦截操作");
                return false;
            }
            return true;
        }
    
        @Override
        public Object run() throws ZuulException {
            RequestContext requestContext = RequestContext.getCurrentContext();
            HttpServletRequest request = requestContext.getRequest();
            String userToken = request.getHeader("apikey");
            if (StringUtils.isBlank(userToken)) {
                log.warn("apikey为空");
                sendError(requestContext, 99001, "请传输参数apikey");
                return null;
            }
            return null;
        }
    
        /**
         * 发送错误消息
         *
         * @param requestContext
         * @param status
         * @param msg
         */
        private void sendError(RequestContext requestContext, int status, String msg) {
            //过滤该请求,不往下级服务转发,到此结束不进行路由
            requestContext.setSendZuulResponse(false);
            HttpServletResponse response = requestContext.getResponse();
            response.setHeader("Content-type", "application/json;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            PrintWriter pw = null;
            try {
                pw = response.getWriter();
                pw.write(JSONObject.toJSONString(new ResponseBean(status, msg, null)));
            } catch (IOException e) {
                log.error(e.getMessage());
            } finally {
                pw.close();
            }
        }
    }

    使用PrintWriter响应给客户端,有时候会报异常

     所以改为下面的方式响应给客户端

    /**
         * 发送错误消息
         *
         * @param requestContext
         * @param status
         * @param msg
         */
        private void sendError(RequestContext requestContext, int status, String msg, String userToken) {
            if (StringUtils.isNotBlank(userToken)) {
                //释放锁
                Boolean exists = redisUtil.exists(userToken, RedisKeyEnum.USER_ACCOUNT_LOCK.indexdb);
                if (exists) {
                    Long del = redisUtil.del(RedisKeyEnum.USER_ACCOUNT_LOCK.indexdb, userToken);
                    System.err.println(del);
                }
            }
            requestContext.setSendZuulResponse(false); //不对请求进行路由
            requestContext.setResponseStatusCode(status);//设置返回状态码
            requestContext.setResponseBody(JSONObject.toJSONString(new ResponseBean(status, msg, null)));//设置返回响应体
            requestContext.getResponse().setContentType("application/json;charset=UTF-8");//设置返回响应体格式,可能会乱码
        }

    这种方式在POSTMan测试时会出现Could not get any response的错误,可以用浏览器,或者代码测试网关错误信息响应,如果正常响应则没问题。

     同时也给了处理方式。

    post过滤器可以在请求转发后获取请求信息和响应入库,或者日志记录

    import com.alibaba.fastjson.JSON;
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.io.IOUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.stereotype.Component;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Enumeration;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @author: qjc
     * @createTime: 2019/5/6 11:07
     * @Description:
     */
    @Component
    @Slf4j
    public class ResponseFilter extends ZuulFilter {
    
        @Override
        public String filterType() {
            return "post";
        }
    
        @Override
        public int filterOrder() {
            return 2;
        }
    
        @Override
        public boolean shouldFilter() {
            // 进行跨域请求的时候,并且请求头中有额外参数,比如token,客户端会先发送一个OPTIONS请求来探测后续需要发起的跨域POST请求是否安全可接受
            // 所以这个请求就不需要拦截,下面是处理方式
            RequestContext requestContext = RequestContext.getCurrentContext();
            HttpServletRequest request = requestContext.getRequest();
            if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
                log.info("OPTIONS请求不做拦截操作");
                return false;
            }
            // 如果前面的拦截器不进行路由,那么后面的过滤器就没必要执行
            if (!requestContext.sendZuulResponse()) {
                return false;
            }
            return true;
        }
    
        @Override
        public Object run() throws ZuulException {
            RequestContext requestContext = RequestContext.getCurrentContext();
            InputStream stream = requestContext.getResponseDataStream();
            if (stream == null) {
                return null;
            }
    
            HttpServletRequest request = requestContext.getRequest();
    
            String requestParams = getRequestParams(requestContext, request);
            System.err.println(requestParams);
    
            try {
                String responseBoby = IOUtils.toString(stream);
                RequestContext.getCurrentContext().setResponseBody(responseBoby);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
      //获取请求参数,适用于POST请求/GET请求,以及参数拼接在URL后面的POST请求
        private String getRequestParams(RequestContext requestContext, HttpServletRequest request) {
            String requestParams = null;
            String requestMethod = request.getMethod();
            StringBuilder params = new StringBuilder();
            Enumeration<String> names = request.getParameterNames();
            if (requestMethod.equals("GET")) {
                while (names.hasMoreElements()) {
                    String name = (String) names.nextElement();
                    params.append(name);
                    params.append("=");
                    params.append(request.getParameter(name));
                    params.append("&");
                }
                requestParams = params.delete(params.length() - 1, params.length()).toString();
            } else {
                Map<String, String> res = new HashMap<>();
                Enumeration<?> temp = request.getParameterNames();
                if (null != temp) {
                    while (temp.hasMoreElements()) {
                        String en = (String) temp.nextElement();
                        String value = request.getParameter(en);
                        res.put(en, value);
                    }
                    requestParams = JSON.toJSONString(res);
                }
                if (StringUtils.isBlank(requestParams) || "{}".equals(requestParams)) {
                    BufferedReader br = null;
                    StringBuilder sb = new StringBuilder("");
                    try {
                        br = request.getReader();
                        String str;
                        while ((str = br.readLine()) != null) {
                            sb.append(str);
                        }
                        br.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (null != br) {
                            try {
                                br.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    requestParams = sb.toString();
                }
            }
            return requestParams;
        }
    }

    error过滤器是在服务网关出现异常的时候起作用的

    import com.alibaba.fastjson.JSONObject;
    import com.dkjk.gateway.context.ResponseBean;
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Component;
    import org.springframework.util.ReflectionUtils;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.PrintWriter;
    
    /**
     * @author: qjc
     * @createTime: 2019/5/30 19:11
     * @Description: 处理请求发生错误时过滤器
     */
    @Component
    @Slf4j
    public class ErrorFilter extends ZuulFilter {
        @Override
        public String filterType() {
            return "error";
        }
    
        @Override
        public int filterOrder() {
            //需要在默认的 SendErrorFilter 之前
            return 5;
        }
    
        @Override
        public boolean shouldFilter() {
            // 只有在抛出异常时才会进行拦截
            return RequestContext.getCurrentContext().containsKey("throwable");
        }
    
        @Override
        public Object run() {
            try {
                RequestContext requestContext = RequestContext.getCurrentContext();
                Object e = requestContext.get("throwable");
    
                if (e != null && e instanceof ZuulException) {
                    ZuulException zuulException = (ZuulException) e;
                    // 删除该异常信息,不然在下一个过滤器中还会被执行处理
                    requestContext.remove("throwable");
                    // 响应给客户端信息
                    HttpServletResponse response = requestContext.getResponse();
                    response.setHeader("Content-type", "application/json;charset=UTF-8");
                    response.setCharacterEncoding("UTF-8");
                    PrintWriter pw = null;
                    pw = response.getWriter();
                    pw.write(JSONObject.toJSONString(new ResponseBean(99999, "系统出现异常", null)));
                    pw.close();
                }
            } catch (Exception ex) {
                log.error("Exception filtering in custom error filter", ex);
                ReflectionUtils.rethrowRuntimeException(ex);
            }
            return null;
        }
    }
  • 相关阅读:
    RedisPlugin
    微信、支付宝授权与支付
    在手机上预览h5项目
    localStorage
    fluter中json的处理
    flutter路由
    一个类实现多个接口
    抽象类、接口
    dart中的类
    方法
  • 原文地址:https://www.cnblogs.com/java-spring/p/10951509.html
Copyright © 2020-2023  润新知