package com.cpjit.swagger4j; import com.cpjit.swagger4j.support.internal.ApiViewWriter; import com.cpjit.swagger4j.support.internal.DefaultApiViewWriter; import com.cpjit.swagger4j.support.internal.FileTypeMap; import com.cpjit.swagger4j.util.ResourceUtil; import com.cpjit.swagger4j.util.matcher.AntPathRequestMatcher; import com.cpjit.swagger4j.util.matcher.HttpMethod; import com.cpjit.swagger4j.util.matcher.RequestMatcher; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.thymeleaf.ITemplateEngine; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.WebContext; import org.thymeleaf.templatemode.TemplateMode; import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * @author yonghuan * @since 2.2.0 */ public class SwaggerMappingSupport { private final static String[] RESOURCE_PATTERNS = { "css/*" , "fonts/*" , "images/*" , "lang/*" , "lib/*" , "" , "index.html" , "swagger-ui.min.js" }; private final static String STATIC_RESCUE_PATH = "com/cpjit/swagger4j/support/internal/statics"; private final static String TEMPLATES_PATH = "templates/swagger4j"; private String contextPath; private List<RequestMatcher> requestMatchers; private String urlPrefix; private ITemplateEngine templateEngine; private ApiViewWriter apiViewWriter = new DefaultApiViewWriter(); private final static String JSON = "json"; private final ConfigResolver configResolver = new DefaultConfigResolver(); public SwaggerMappingSupport(ServletContext servletContext, String urlPatternMapping) throws ServletException { this.contextPath = servletContext.getContextPath(); urlPrefix = contextPath + urlPatternMapping; urlPrefix = urlPrefix.replaceAll("/{2,}", "/"); if (!"/".equals(urlPrefix) && urlPrefix.endsWith("/")) { urlPrefix = urlPrefix.substring(0, urlPrefix.length() - 1); } requestMatchers = Arrays.stream(RESOURCE_PATTERNS) .map(pattern -> String.join("/", this.contextPath, urlPatternMapping, pattern)) .map(pattern -> pattern.replaceAll("/{2,}", "/")) .map(pattern -> new AntPathRequestMatcher(pattern, HttpMethod.GET.name())) .collect(Collectors.toList()); ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver(SwaggerFilter.class.getClassLoader()); templateResolver.setPrefix(TEMPLATES_PATH + "/"); templateResolver.setTemplateMode(TemplateMode.HTML); TemplateEngine templateEngine = new TemplateEngine(); templateEngine.setTemplateResolver(templateResolver); this.templateEngine = templateEngine; } public boolean doMapping(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; String uri = request.getRequestURI(); if (uri.equals(this.urlPrefix)) { String accept = request.getHeader("accept"); if (accept != null && accept.contains(JSON)) { try { apiViewWriter.writeApis(request, response); } catch (Exception e) { throw new IOException(e); } } else { toIndex(request, response); } return true; } if (unsupportedRequest(request)) { return false; } String relativePath = request.getRequestURI().replaceFirst(urlPrefix, ""); // 搜索静态资源 String staticResource = String.join("/", STATIC_RESCUE_PATH, relativePath); staticResource = staticResource.replaceAll("/{2,}", "/"); InputStream is = ResourceUtil.getResourceAsStream(SwaggerFilter.class, staticResource); OutputStream out; if (is != null) { String contentType = FileTypeMap.getContentType(staticResource); response.setContentType(contentType); out = response.getOutputStream(); IOUtils.copy(is, out); IOUtils.closeQuietly(is); IOUtils.closeQuietly(out); return true; } // 搜索模板 String templateResource = String.join("", TEMPLATES_PATH, relativePath); templateResource = templateResource.replaceAll("/{2,}", "/"); is = ResourceUtil.getResourceAsStream(templateResource); if (is != null) { response.setContentType("text/html; charset=UTF-8"); response.setCharacterEncoding(StandardCharsets.UTF_8.name()); try (Writer writer = response.getWriter()) { WebContext webContext = new WebContext(request, response, request.getServletContext()); webContext.setVariable("baseUrl", resolveBaseUrl(request)); webContext.setVariable("lang", "zh-cn"); webContext.setVariable("getApisUrl", resolveBaseUrl(request)); configResolver.obtainConfig(request).forEach((k, v) -> webContext.setVariable((String) k, v)); String templateName = FilenameUtils.getName(relativePath); templateEngine.process(templateName, webContext, writer); } return true; } return false; } private void toIndex(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/html; charset=UTF-8"); response.setCharacterEncoding(StandardCharsets.UTF_8.name()); try (Writer writer = response.getWriter()) { WebContext webContext = new WebContext(request, response, request.getServletContext()); webContext.setVariable("baseUrl", resolveBaseUrl(request)); String lang = request.getParameter("lang"); if (StringUtils.isBlank(lang)) { lang = "zh-cn"; } webContext.setVariable("lang", lang); webContext.setVariable("getApisUrl", resolveBaseUrl(request)); configResolver.obtainConfig(request).forEach((k, v) -> webContext.setVariable((String) k, v)); String templateName = FilenameUtils.getName("index.html"); templateEngine.process(templateName, webContext, writer); } } private String resolveBaseUrl(HttpServletRequest request) { return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + this.urlPrefix; } private boolean unsupportedRequest(HttpServletRequest request) { return requestMatchers.stream() .filter(requestMatcher -> requestMatcher.matches(request)) .findAny() .isPresent() == false; } }