服務網關ZuulFilter過濾器--pre/post/error的用法(校驗請求信息,獲取路由後的請求/響應信息,處理服務網關異常)

微服務中Zuul服務網關一共定義了四種類型的過濾器:java

  • pre:在請求被路由(轉發)以前調用
  • route:在路由(請求)轉發時被調用
  • error:服務網關發生異常時被調用
  • post:在路由(轉發)請求後調用

 我在項目中用到了,pre/error/post三種類型,先記錄一下web

pre過濾器主要是用來校驗各類信息的spring

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();
        }
    }
}

post過濾器能夠在請求轉發後獲取請求信息和響應入庫,或者日誌記錄apache

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過濾器是在服務網關出現異常的時候起做用的json

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 -1;
    }

    @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;
    }
}
相關文章
相關標籤/搜索