/* * Copyright (c) 2002-2012 Alibaba Group Holding Limited. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.citrus.util; import static com.alibaba.citrus.util.FileUtil.*; import static com.alibaba.citrus.util.StringUtil.*; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import javax.servlet.http.HttpServletRequest; /** * 有关servlet的小工具。 * * @author Michael Zhou */ public class ServletUtil { /** * 判断servlet是否为前缀映射。 * <p> * Servlet mapping有两种匹配方式:前缀匹配和后缀匹配。 * </p> * <ul> * <li>对于前缀匹配,例如:/turbine/aaa/bbb,servlet path为/turbine,path info为/aaa/bbb</li> * <li>对于后缀匹配,例如:/aaa/bbb.html,servlet path为/aaa/bbb.html,path info为null</li> * </ul> */ public static boolean isPrefixServletMapping(HttpServletRequest request) { String pathInfo = trimToNull(request.getPathInfo()); if (pathInfo != null) { return true; } else { // 特殊情况:前缀映射/turbine,requestURI=/turbine // 此时,pathInfo也是null,但其实是前缀匹配。 // 这种情况可以通过查看servletPath是否有后缀来部分地识别。 String servletPath = trimToEmpty(request.getServletPath()); int index = servletPath.lastIndexOf("/"); if (servletPath.indexOf(".", index + 1) >= 0) { return false; } else { return true; } } } /** * 取得request所请求的资源路径。 * <p> * 资源路径为<code>getServletPath() + getPathInfo()</code>。 * </p> * <p> * 注意,<code>ResourcePath</code>以<code>"/"</code>开始,如果无内容,则返回空字符串 * <code>""</code>。 * </p> */ public static String getResourcePath(HttpServletRequest request) { String pathInfo = normalizeAbsolutePath(request.getPathInfo(), false); String servletPath = normalizeAbsolutePath(request.getServletPath(), pathInfo.length() != 0); return servletPath + pathInfo; } /** * 取得request请求的基准URL。 * <p> * 基准URL等同于<code>SERVER/contextPath</code>。 * </p> * <p> * 基准URL总是<strong>不</strong>以<code>"/"</code>结尾。 * </p> * <p> * 以下等式总是成立:<code>fullURL = baseURL + resourcePath</code>。 * </p> */ public static String getBaseURL(HttpServletRequest request) { String fullURL = request.getRequestURL().toString(); String fullPath; try { fullPath = new URL(fullURL).getPath(); } catch (MalformedURLException e) { throw new IllegalArgumentException("Invalid URL: " + fullURL, e); } // 基本URL StringBuilder buf = new StringBuilder(fullURL); buf.setLength(fullURL.length() - fullPath.length()); // 加上contextPath buf.append(normalizeAbsolutePath(request.getContextPath(), true)); return buf.toString(); } /** * 取得request所请求的资源路径。 * <ul> * <li>对于前缀匹配的servlet,等同于<code>getPathInfo()</code>。例如映射 * <code>/turbine/*</code>:<code>/turbine/xx/yy</code>的resource path为 * <code>/xx/yy</code>。</li> * <li>对于后缀匹配的servlet,等同于 <code>getServletPath()</code>。例如映射 * <code>*.do</code>:<code>/xx/yy.do</code>的resource path为 * <code>/xx/yy.do</code>。</li> * </ul> * <p> * 注意,<code>ResourcePath</code>以<code>"/"</code>开始,如果无内容,则返回空字符串 * <code>""</code>。 * </p> * <p> * 本方法适用于servlet-mapping对应的URL。 * </p> */ public static String getServletResourcePath(HttpServletRequest request) { String resourcePath; if (isPrefixServletMapping(request)) { resourcePath = request.getPathInfo(); } else { resourcePath = request.getServletPath(); } resourcePath = normalizeAbsolutePath(resourcePath, false); return resourcePath; } /** * 取得request请求的基准URL。 * <ul> * <li>对于前缀匹配的servlet,等同于<code>SERVER/contextPath/servletPath</code>。例如映射 * <code>/turbine/*</code>:<code>http://localhost/myapp/turbine/xx/yy</code> * 的baseURL为 <code>http://localhost/myapp/turbine</code>。</li> * <li>对于后缀匹配的servlet,等同于<code>SERVER/contextPath</code>。例如映射 * <code>*.do</code>:<code>http://localhost/myapp/xx/yy.do</code>的baseURL为 * <code>http://localhost/myapp</code>。</li> * </ul> * <p> * 基准URL总是<strong>不</strong>以<code>"/"</code>结尾。 * </p> * <p> * 以下等式总是成立:<code>fullURL = servletBaseURL + servletResourcePath</code>。 * </p> * <p> * 本方法适用于servlet-mapping对应的URL。 * </p> */ public static String getServletBaseURL(HttpServletRequest request) { String fullURL = request.getRequestURL().toString(); String fullPath; try { fullPath = new URL(fullURL).getPath(); } catch (MalformedURLException e) { throw new IllegalArgumentException("Invalid URL: " + fullURL, e); } // 基本URL StringBuilder buf = new StringBuilder(fullURL); buf.setLength(fullURL.length() - fullPath.length()); // 加上contextPath buf.append(normalizeAbsolutePath(request.getContextPath(), true)); // 对于前缀匹配,加上servletPath if (isPrefixServletMapping(request)) { buf.append(normalizeAbsolutePath(request.getServletPath(), true)); } return buf.toString(); } /** 规格化URI。 */ public static String normalizeURI(String uri) { return URI.create(trimToEmpty(uri)).normalize().toString(); } /** * 判断path是否为fullpath的前缀,匹配到“/”边界。 * <ul> * <li><code>startsWithPath("/index", "/index.htm") == false</code>。</li> * <li><code>startsWithPath("/path", "/path/index") == true</code>。</li> * </ul> */ public static boolean startsWithPath(String path, String fullpath) { if (fullpath != null && path != null) { if (path.endsWith("/")) { return fullpath.startsWith(path); } else if (path.length() == fullpath.length()) { return fullpath.equals(path); } else if (path.length() < fullpath.length()) { return fullpath.startsWith(path + "/"); } } return false; } }