SpringCloud入門(九): Zuul 上傳&回退&異常處理&跨域
- 2020 年 4 月 6 日
- 筆記
Zuul的上傳
1、構建一個上傳類
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.io.*; @RestController @RequestMapping("/file") public class FileUploadController { @PostMapping(value = "/upload", consumes = "multipart/form-data") public Object upload(@RequestParam("file") MultipartFile file) throws IOException { File temp = new File("D:/" + file.getOriginalFilename()); if(!temp.getParentFile().exists()){ temp.getParentFile().mkdir(); } try { file.transferTo(temp); //保存文件 return Boolean.TRUE; } catch (IllegalStateException e) { e.printStackTrace(); return Boolean.FALSE; } catch (IOException e) { e.printStackTrace(); return Boolean.FALSE; } } }
2、配置屬性文件
zuul默認文件上傳的時候,會走Spring DispatcherServlet,這個時候會對文件大小進行判斷,默認為10M;如果要傳大文件需要對文件大小進行設置。
zuul.ignored-services=* zuul.routes.use-routing.serviceId=ms-provider-order zuul.routes.use-routing.path=/order-service/** zuul.routes.use-routing.sensitiveHeaders= spring.servlet.multipart.max-file-size=4000MB spring.servlet.multipart.max-request-size=4000MB
通過/zuul來訪問,可以繞過DispatcherServlet轉為zuulServlet,在上傳大文件的時候不需要設置文件大小,但是需要設置hystrix和ribbon的超時時間。
zuul.ignored-services=* zuul.routes.use-routing.serviceId=ms-provider-order zuul.routes.use-routing.path=/order-service/** zuul.routes.use-routing.sensitiveHeaders= hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=600000 ribbon.ConnectTimeout=100000 ribbon.ReadTimeout=100000
3、通過PostMan測試
zuul的回退
1、自定義一個回退類
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpResponse; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; public class CustomZuulFallBack implements FallbackProvider { @Override public String getRoute() { return "*"; //*表示為所有微服務提供回退 //return "ms-provider-order" //指定某個微服務回退 } @Override public ClientHttpResponse fallbackResponse(String route, Throwable cause) { return new ClientHttpResponse() { @Override public HttpStatus getStatusCode() throws IOException { return HttpStatus.OK; } @Override public int getRawStatusCode() throws IOException { return HttpStatus.OK.value(); } @Override public String getStatusText() throws IOException { return HttpStatus.OK.getReasonPhrase(); } @Override public void close() { } @Override public InputStream getBody() throws IOException { return new ByteArrayInputStream("後端服務不可用".getBytes()); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); //和body中的內容編碼一致,否則容易亂碼 headers.setContentType(MediaType.APPLICATION_JSON_UTF8); return headers; } }; } }
zuul的回退只支援宕機回退(服務掛了)和超時回退(服務請求超時)兩種;
如果服務自己拋出的異常(404,500等),zuul的回退監控不到;原因就在於RibbonRoutingFilter的攔截器,在攔截器run中的forward方法,只爭對了HystrixRuntimeException的攔截,500等異常會直接由SpringBoot的BasicErrorController接管拋出到頁面;
protected ClientHttpResponse forward(RibbonCommandContext context) throws Exception { Map<String, Object> info = this.helper.debug(context.getMethod(), context.getUri(), context.getHeaders(), context.getParams(), context.getRequestEntity()); RibbonCommand command = this.ribbonCommandFactory.create(context); try { ClientHttpResponse response = command.execute(); this.helper.appendDebug(info, response.getRawStatusCode(), response.getHeaders()); return response; } catch (HystrixRuntimeException ex) { return handleException(info, ex); } }
zuul的全局異常處理
1、自定義一個異常處理類
import com.alibaba.fastjson.JSON; import com.netflix.zuul.context.RequestContext; import org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilter; import org.springframework.stereotype.Component; import org.springframework.util.ReflectionUtils; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; @Component public class CustomErrorFilter extends SendErrorFilter { @Override public Object run() { try { RequestContext ctx = RequestContext.getCurrentContext(); ExceptionHolder exception = findZuulException(ctx.getThrowable()); //HttpServletRequest request = ctx.getRequest(); HttpServletResponse response = ctx.getResponse(); response.setContentType("text/html; charset=UTF-8"); response.setCharacterEncoding("UTF-8"); Map<String,Object> errorMap = new HashMap<>(); errorMap.put("code","203"); errorMap.put("errMsg",exception.getThrowable().getCause().getMessage()); response.getWriter().write(JSON.toJSONString(errorMap)); } catch (Exception ex) { ReflectionUtils.rethrowRuntimeException(ex); } return null; } }
2、禁用系統自帶的異常處理類
zuul.SendErrorFilter.error.disable=true
zuul的跨域問題
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; @Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); final CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); // 允許cookies跨域 config.addAllowedOrigin("*");// #允許向該伺服器提交請求的URI,*表示全部允許,在SpringMVC中,如果設成*,會自動轉成當前請求頭中的Origin config.addAllowedHeader("*");// #允許訪問的頭資訊,*表示全部 config.setMaxAge(18000L);// 預檢請求的快取時間(秒),即在這個時間段里,對於相同的跨域請求不會再預檢了 config.addAllowedMethod("*");// 允許提交請求的方法,*表示全部允許 source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }