タイトル: コンテキストパスの取得
SEOタイトル: Java Servlet / JSP / Spring MVC / Thymeleaf でコンテキストパスを取得する方法まとめ
| この記事の要点 |
|
コンテキストパスとは
Java EE / Jakarta EE の Web アプリは Tomcat 等のサーブレットコンテナにデプロイすると /myapp のようなコンテキストパスが前置されます。これを動的に取得しないと、デプロイ先が変わった瞬間にリンク・フォーム・リダイレクトが全滅します。
| デプロイ形態 | URL 例 | contextPath |
|---|---|---|
Tomcat の webapps/myapp.war | http://host/myapp/login | /myapp |
| Tomcat ROOT.war | http://host/login | 空文字 "" |
| Spring Boot 組込 Tomcat 既定 | http://host/login | 空文字 |
Spring Boot で server.servlet.context-path=/api | http://host/api/login | /api |
Servlet で取得
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws IOException {
// コンテキストパス取得
String ctx = req.getContextPath(); // 例: "/myapp"
// リダイレクト先を組み立て
res.sendRedirect(ctx + "/login"); // → /myapp/login
// 関連メソッド
String servletPath = req.getServletPath(); // 例: "/hello"
String pathInfo = req.getPathInfo(); // 例: "/users/3"
String requestURI = req.getRequestURI(); // 例: "/myapp/hello/users/3"
String contextURL = req.getRequestURL().toString();
}
}
JSP で取得 (EL / JSTL)
<%-- EL で取得(推奨) --%>
<a href="${pageContext.request.contextPath}/login">ログイン</a>
<%-- フォーム --%>
<form action="${pageContext.request.contextPath}/user/save" method="post">
...
</form>
<%-- JSTL <c:url> もコンテキストパスを自動付与 --%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<a href="<c:url value='/login'/>">ログイン</a>
<%-- 変数に格納して使い回す --%>
<c:set var="ctx" value="${pageContext.request.contextPath}" />
<link rel="stylesheet" href="${ctx}/css/app.css" />
<script src="${ctx}/js/app.js"></script>
Spring MVC / Spring Boot
// 1. 設定値として注入
@Service
public class UrlBuilder {
@Value("${server.servlet.context-path:}")
private String contextPath; // 設定が無い場合は空文字
public String buildUrl(String path) {
return contextPath + path;
}
}
// 2. HttpServletRequest 経由
@GetMapping("/hello")
public String hello(HttpServletRequest req, Model model) {
model.addAttribute("ctx", req.getContextPath());
return "hello";
}
// 3. ServletUriComponentsBuilder(フル URL)
@GetMapping("/api/me")
public String me() {
String fullUrl = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/users/me")
.build()
.toUriString();
return fullUrl; // 例: http://localhost:8080/api/users/me
}
application.properties での設定:
# Spring Boot 2.x / 3.x 共通
server.servlet.context-path=/api
# ポート変更も合わせて
server.port=8080
# 旧 Spring Boot 1.x
server.context-path=/api
Thymeleaf で取得
Thymeleaf は @{/...} 構文で自動的にコンテキストパスを前置します。これが最も安全:
<!-- @{} を使えば contextPath は自動付与 -->
<a th:href="@{/login}">ログイン</a>
<form th:action="@{/user/save}" method="post"> ... </form>
<!-- 静的リソースも同じ -->
<link rel="stylesheet" th:href="@{/css/app.css}" />
<script th:src="@{/js/app.js}"></script>
<!-- パスパラメータ -->
<a th:href="@{/user/{id}(id=${user.id})}">詳細</a>
<!-- クエリパラメータ -->
<a th:href="@{/search(q=${keyword},page=1)}">検索</a>
<!-- どうしても文字列で欲しいとき -->
<span th:text="${#httpServletRequest.contextPath}">[ctx]</span>
JavaScript 側へ渡す
SPA 化していなくても、AJAX 呼び出しのために JS にコンテキストパスを渡す必要があります:
<!-- JSP -->
<script>
window.CONTEXT_PATH = '${pageContext.request.contextPath}';
</script>
<!-- Thymeleaf -->
<script th:inline="javascript">
window.CONTEXT_PATH = /*[[@{/}]]*/ '/';
</script>
<script>
// 利用側
fetch(window.CONTEXT_PATH + '/api/users')
.then(r => r.json())
.then(console.log);
</script>
web.xml での設定
従来の Servlet コンテナ (Tomcat 単体) では、コンテキストパスは war 名 or META-INF/context.xml で指定:
<!-- META-INF/context.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/myapp" reloadable="true">
<!-- DataSource 等もここに -->
</Context>
<!-- Tomcat の conf/server.xml に <Context> を書くのは非推奨 -->
<!-- war ファイル名 myapp.war を webapps に置くだけで /myapp になる -->
よくあるバグと対処
| 症状 | 原因 | 対処 |
|---|---|---|
| 本番だけリンクが 404 | <a href="/login"> ハードコード | ${pageContext.request.contextPath} を前置 |
| CSS / JS が読まれない | 同上 | @{/css/app.css} を使う |
| contextPath が空 | ROOT.war / Spring Boot 既定 | 正常。"" + "/path" = "/path" |
| Ajax だけ 404 | JS にハードコード | JSP/Thymeleaf 側で window.CONTEXT_PATH 等に渡す |
Spring Boot で server.servlet.context-path が効かない | 1.x 系設定 server.context-path のまま | Spring Boot 2.0+ は server.servlet.context-path |
FAQ
Q: request.getContextPath() と request.getServletContext().getContextPath() の違い
A: 同じ値を返します。Servlet 3.0+ なら好きな方で OK。
Q: HTML 内ハードコード /myapp/login でも動く
A: 動きますが、デプロイ先が変わった瞬間に全 URL を grep 置換する羽目に。常に動的取得が原則です。
Q: 静的 HTML から呼ぶときは?
A: 静的 HTML は JSP/Thymeleaf を介さないので、JS で document.querySelector('base').href や <meta name="ctx"> を読むパターンが多いです。