web-dev-qa-db-ja.com

Webアプリでコンテキストを処理する賢い方法はありますか?

Javaでは、WebアプリはWARにバンドルされています。デフォルトでは、多くのサーブレットコンテナは、アプリケーションのコンテキスト名としてWAR名を使用します。

したがって、myapp.warは http://example.com/myapp にデプロイされます。

問題は、Webアプリケーションがその「ルート」を「ルート」、または単に「/」と見なすのに対し、HTMLはアプリケーションのルートを「/ myapp」と見なすことです。

サーブレットAPIとJSPには、これを管理するための機能があります。たとえば、サーブレットで次のように実行した場合:response.sendRedirect( "/ mypage.jsp")、コンテナはコンテキストを付加してURLを作成します: http://example.com/myapp/mypage .jsp "。

ただし、たとえば、HTMLのIMGタグを使用してこれを行うことはできません。 <img src = "/ myimage.gif" />を実行すると、「/ myapp/myimage.gif」が本当に必要だったため、おそらく404が返されます。

多くのフレームワークには、同様にコンテキストを認識するJSPタグがあり、JSP内で正しいURLを作成する方法はいくつかあります(特にエレガントではありません)。

絶対的なURLではなく、「App Relative」のURLをいつ使用するかをコーディング担当者が急いで開始することは、骨の折れる問題です。

最後に、オンザフライでURLを作成する必要のあるJavaScriptコードと、CSS内に埋め込まれたURL(背景画像など)の問題があります。

他の人がこの問題を緩和して回避するためにどのような手法を使用しているかに興味があります。多くの場合、サーバールートまたは使用しているコンテキストにパントしてハードコーディングします。私はすでにその答えを知っています。それは私が探しているものではありません。

職業はなんですか?

39
Will Hartung

JSTLを使用してURLを作成できます。

例えば、 <c:url value="/images/header.jpg" />は、コンテキストルートのプレフィックスになります。

CSSでは、これは通常私にとって問題ではありません。

私はこのようなウェブルート構造を持っています:

/ css
/images

CSSファイルでは、相対URL(../images/header.jpg)を使用するだけでよく、コンテキストルートを意識する必要はありません。

JavaScriptについては、次のように、ページヘッダーにいくつかの一般的なJavaScriptが含まれています。

<script type="text/javascript">
var CONTEXT_ROOT = '<%= request.getContextPath() %>';
</script>

次に、すべてのスクリプトでコンテキストルートを使用できます(または、パスを構築する関数を定義できます-もう少し柔軟かもしれません)。

明らかに、これはすべて、JSPとJSTLの使用に依存しますが、私はFaceletsでJSFを使用しており、関連する手法は似ています-唯一の本当の違いは、コンテキストルートを別の方法で取得することです。

24
user7094

HTMLページの場合は、HTML <base>タグを設定するだけです。すべての相対リンク(つまり、スキームまたは/で始まっていない)は、それに対する相対リンクになります。 HttpServletRequestですぐに取得するための明確な方法はないため、ここではJSTLの支援はほとんど必要ありません。

<%@taglib prefix="c" uri="http://Java.Sun.com/jsp/jstl/core" %>
<%@taglib prefix="fn" uri="http://Java.Sun.com/jsp/jstl/functions" %>
<c:set var="req" value="${pageContext.request}" />
<c:set var="url">${req.requestURL}</c:set>
<c:set var="uri">${req.requestURI}</c:set>

<!DOCTYPE html>
<html lang="en">
  <head>
    <base href="${fn:substring(url, 0, fn:length(url) - fn:length(uri))}${req.contextPath}/" />
    <link rel="stylesheet" href="css/default.css">
    <script src="js/default.js"></script>
  </head>
  <body>
    <img src="img/logo.png" />
    <a href="other.jsp">link</a>
  </body>
</html>

ただし、これには注意が必要です。アンカー(#identifier URL)は、ベースパスに対しても相対的になります。それらのいずれかがある場合は、代わりにリクエストURL(URI)を基準にしたいとします。したがって、次のように変更します

<a href="#identifier">jump</a>

<a href="${uri}#identifier">jump</a>

JSでは、相対URLを絶対URLに変換する場合はいつでも、DOMから<base>要素にアクセスできます。

var base = document.getElementsByTagName("base")[0].href;

またはjQueryを行う場合

var base = $("base").attr("href");

CSSでは、画像のURLはスタイルシート自体のURLを基準にしています。したがって、スタイルシート自体に関連するフォルダに画像をドロップするだけです。例えば。

/css/style.css
/css/images/foo.png

次のようにそれらを参照します

background-image: url('images/foo.png');

CSSフォルダーと同じレベルのフォルダーに画像をドロップしたい場合

/css/style.css
/images/foo.png

次に、../を使用して共通の親フォルダーに移動します

background-image: url('../images/foo.png');

以下も参照してください。

8
BalusC

tardateに同意します。私もフィルターに注意を払い、プロジェクト rlRewriteFilter に直面して解決策を見つけました。次のような簡単な設定:

<rule>
    <from>^.+/resources/(.*)$</from>
    <to>/resources/$1</to>
</rule>

*/resourcesパスのすべてのリクエストを/ resourcesパス(コンテキストパスプレフィックスを含む)に転送するのに役立ちます。そのため、images[〜#〜] css [〜#〜]resourcesフォルダーの下のファイルと、背景画像やその他の場合のために、スタイルで相対URLを引き続き使用します。

4
Demwis

サーブレットAPIとJSPには、これを管理するための機能があります。たとえば、サーブレットで次のように実行した場合:response.sendRedirect( "/ mypage.jsp")、コンテナはコンテキストを付加してURLを作成します: http://example.com/myapp/mypage .jsp "。

ああ、多分、そうでないかもしれません-それはあなたのコンテナとサーブレットの仕様に依存します!

サーブレット2.3:新機能の公開

そして最後に、専門家グループによる長い議論の後、サーブレットAPI 2.3は、非ルートコンテキスト内で実行されているサーブレットのres.sendRedirect( "/ index.html")呼び出しで何が起こるかを明確にしました。問題は、サーブレットAPI 2.2では、「/ index.html」のような不完全なパスがサーブレットコンテナによって完全なパスに変換される必要があることですが、コンテキストパスの処理方法がわかりません。呼び出しを行うサーブレットがパス「/ contextpath」のコンテキストにある場合、リダイレクトURIはコンテナルート( http:// server:port/index.html )またはコンテキストルート( http:// server:port/contextpath/index.html )?移植性を最大限にするには、動作を定義することが不可欠です。長い議論の末、専門家たちはコンテナのルートを基準にして翻訳することにしました。コンテキスト相対が必要な場合は、getContextPath()からの出力をURIの前に追加できます。

そのため、2.3では、パスはコンテキストパスを含むように自動的に変換されません。

3
tardate

私は使いました ヘルパークラス imgタグなどを生成します。このヘルパークラスは 接頭辞パス アプリケーションのcontextPath。 (これは機能しますが、私はあまり好きではありません。他にもっと良い代替案がある場合は、教えてください。)

CSSファイルなどのパスには、 Antビルドスクリプト 実稼働環境ではsite.cssに、開発環境ではsite.development.cssにsite.production.cssを使用しています。

または、Antスクリプトを使用することもあります。 @ token @を置き換えます さまざまな環境に適したデータを持つトークン。この場合、@ contextPAth @トークンは正しいコンテキストパスに置き換えられます。

1
kosoant

私はこれらの技法のほとんどを使用しました(XSLTアーキテクチャーを保存します)。

問題の核心(とコンセンサス)は、複数のディレクトリが存在する可能性のあるサイトを持つことです。

ディレクトリの深さ(より良い用語がないため)が一定である場合、CSSなどの相対URLに依存できます。

レイアウトは完全にフラットである必要はなく、一貫している必要があります。

たとえば、/ css、/ js、/ common、/ admin、/ userなどの階層を作成しました。適切なページとリソースを適切なディレクトリに配置します。このような構造は、コンテナベースの認証で非常にうまく機能します。

また、*。cssと* .jsをJSPサーブレットにマップし、動的に作成して、オンザフライで構築できるようにしました。

私は見逃していたかもしれない何かがあったことをただ望んでいた。

1
Will Hartung

Vilmantasはここで正しい言葉を述べました:相対URL。

IMGで行う必要があるのは使用することだけです

<img src="myimage.gif"/>

の代わりに

<img src="/myimage.gif"/>

そして、それはアプリのコンテキストに相対的になります(ブラウザーが移動先のURLを解釈しているため)

1

Request.getContextPath()を使用して、特定のコンテキストにハードコードされていない絶対URLを作成できます。以前の答えが示したように、JavaScriptの場合、JSPの上部(またはできればテンプレート)に変数を設定し、その変数の前にコンテキストを付けます。

他の問題を引き起こす可能性のあるCSSファイルを動的に生成する必要がない限り、CSSイメージの置換には機能しません。しかし、CSSファイルと画像の関係がわかっているので、相対URLを使用する必要はありません。

何らかの理由で、IE相対URLの処理に問題があり、JavaScript変数をコンテキストに設定した式の使用にフォールバックする必要がありました。IE画像の置き換えを独自のファイルに入れ、IEマクロを使用して正しいマクロを取得しました。透明なPNGを処理するためにすでにそれを行わなければならなかったので、大したことではありませんでしたとにかくきれいではありませんが、動作します。

1
Brian Deterling

1つのオプションは、可能な限り「フラットな」アプリケーション構造と相対URLを使用することです。

「フラット」とは、アプリケーションルートの下にサブディレクトリがないことを意味します。「images /」などの静的コンテンツ用のディレクトリはほとんどない可能性があります。すべてのJSP、アクションURL、サーブレットはルートの直下にあります。

これはあなたの問題を完全に解決するわけではありませんが、それを大幅に簡素化します。

特別な場合を除いて、この方法で絶対URLを使用しないことをお勧めします。ずっと。絶対URLは、another webappがWebアプリケーション内の何かを指している場合に適しています。内部的に-1つのリソースが同じコンテキストの2番目のリソースを指している場合、リソースはそれがどこにあるかを知っている必要があるため、2番目のリソースへの相対パスを表現できるはずです。

もちろん、モジュール式コンポーネントを作成しますが、それらにはそれらを含むリソースがわかりません。例えば:

_/myapp/user/email.jsp:
Email: <a href="../sendmail.jsp">${user.email}</a>

/myapp/browse/profile.jsp:
<jsp:include page="../user/email.jsp" />

/myapp/home.jsp:
<jsp:include page="../user/email.jsp" />
_

では、_email.jsp_はどのように_sendmail.jsp_の相対パスを知るのでしょうか。明らかに、リンクは_/myapp/browse/profile.jsp_で壊れるか、_/myapp/home.jsp_で壊れます。答えは、すべてのURLを同じフラットファイルパススペースに保持することです。つまり、すべてのURLの_/myapp/_の後にスラッシュを含めないでください。

これは、URLとコンテンツを生成する実際のファイルとの間に何らかのマッピングがある限り、簡単に実行できます。 (たとえば、Springでは、DispatcherServletを使用して、URLをJSPファイルまたはビューにマップします。)

特別な場合があります。例えばJavaScriptでブラウザー側アプリケーションを作成している場合は、フラットなファイルパススペースを維持することが難しくなります。その場合、または他の特別な場合、または個人的な好みがある場合は、<%= request.getContextPath() %>を使用して絶対パスを作成することはそれほど重要ではありません。

1
Travis Wilson

ゼロからサイトを作成するとき、私は@Willを支持します-相対参照に固執できるように、一貫性があり予測可能なURL構造を目指します。

しかし、元々サイトルート "/"(単純なJSPサイトではかなり一般的)の直下で動作するように構築されたサイトを正式な Java EE パッケージング(コンテキストルートルートの下のパスになります)。

それは多くのコード変更を意味します。

コードの変更を回避または延期したいが、正しいコンテキストルート参照を確実にしたい場合、私がテストした手法はサーブレットフィルターを使用することです。フィルターは何も変更せずに既存のプロジェクトにドロップでき(web.xmlを除く)、アウトバウンドHTMLのURL参照を正しいパスに再マップし、リダイレクトが正しく参照されるようにします。

ここで利用可能なサンプルサイトと使用可能なコード: EnforceContextRootFilter-1.0-src.Zip NB:実際のマッピングルールはサーブレットクラスに正規表現として実装され、かなり一般的なキャッチオールを提供しますが、必要になる場合があります特定の状況に合わせて変更します。

ところで、私は対処するために少し異なる質問を分岐しました 「/」から既存のコードベースを非ルートのコンテキストパスに移行する

0
tardate

私は自分のコアJavaScriptライブラリの一部としてプロパティを作成する傾向があります。それは完璧だとは思いませんが、私が何とか達成した最高のものだと思います。

まず、アプリコアの一部であり、常に利用可能なモジュールがあります。

_(function (APP) {
  var ctx;
  APP.setContext = function (val) {
    // protect rogue JS from setting the context.
    if (ctx) {
      return;
    }
    val = val || val.trim();
    // Don't allow a double slash for a context.
    if (val.charAt(0) === '/' && val.charAt(1) === '/') {
      return;
    }
    // Context must both start and end in /.
    if (val.length === 0 || val === '/') {
      val = '/';
    } else {
      if (val.charAt(0) !== '/') {
        val = '/' + val;
      }
      if (val.slice(-1) !== '/') {
        val += '/';
      }
    }
    ctx = val;
  };
  APP.getContext = function () {
    return ctx || '/';
  };
  APP.getUrl = function (val) {
    if (val && val.length > 0) {
      return APP.getContext() + (val.charAt(0) === '/' ? val.substring(1) : val);
    }
    return APP.getContext();
  };
})(window.APP = window.APP || {});
_

次に、常に以下を含む共通ヘッダーを持つApacheタイルを使用します。

_<script type="text/javascript">
  APP.setContext('${pageContext.request['contextPath']}');
  // If preferred use JSTL cor, but it must be available and declared.
  //APP.setContext('<c:url value='/'/>');
</script>
_

コンテキストを初期化したので、コンテキスト内の指定された入力文字列の絶対パスを返す場所(jsファイルまたはjsp/html内)からgetUrl(path)を使用できます。

以下は意図的に同等であることに注意してください。 getUrlは、絶対パスを常に返します。相対パスでは、最初にコンテキストを知る必要がないからです。

_var path = APP.getUrl("/some/path");
var path2 = APP.getUrl("some/path");
_
0
Brett Ryan

最善の方法は次のとおりです。コンテキストリダイレクトフィルターフィルターは事前一致拡張ポイントで適用する必要があるため、アノテーション@PreMatchingを使用します。

このインターフェースを実装するフィルターは、JAX-RSランタイムによって検出されるように@Providerで注釈を付ける必要があります。コンテナリクエストフィルタインスタンスも検出され、特定のリソースメソッドに動的にバインドされます。

サンプルコードで説明:

http://writeulearn.com/Java-filters/

0
Bindu S

noによる私は、以下がエレガントな問題であると主張することを意味します。実際、後から考えると、(おそらく)パフォーマンスに影響があるため、この問題はお勧めしません。

私たちのWebアプリのJSPは、厳密にはXMLの生データでした。この生データは、適切なCSSタグを適用するXSL(サーバー側)に送信され、XHTMLを出力します。

私たちは単一のtemplate.xslを持っていました。これは、ウェブサイトのさまざまなコンポーネント用に持っていた複数のXSLファイルによって継承されます。パスはすべて、paths.xmlというXSLファイルで定義されています。

<?xml version="1.0" encoding="UTF-8"?>
<paths>
    <path name="account" parent="home">Account/</path>
    <path name="css">css/</path>
    <path name="home">servlet/</path>
    <path name="icons" parent="images">icons/</path>
    <path name="images">images/</path>
    <path name="js">js/</path>
</paths>

内部リンクは次のようにXMLにあります。

<ilink name="link to icons" type="icons">link to icons</ilink>

これはXSLによって処理されます。

<xsl:template match="ilink">
    <xsl:variable name="temp">
        <xsl:value-of select="$rootpath" />
        <xsl:call-template name="paths">
            <xsl:with-param name="path-name"><xsl:value-of select="@type" /></xsl:with-param>
        </xsl:call-template>
        <xsl:value-of select="@file" />
    </xsl:variable>
        <a href="{$temp}" title="{@name}" ><xsl:value-of select="." /></a>
</xsl:template>

$rootPath${applicationScope.contextPath}を使用して各ファイルに渡されました。JSP/ Javaファイルにハードコードするだけでなく、XMLを使用する背後にあるアイデアは、再コンパイルする必要がないことです。

繰り返しますが、このソリューションは良いものではありません...しかし、私たちは一度使用しました!

編集:実際には、ビュー全体にJSPを使用できなかったため、問題の複雑さが生じました。なぜ誰かが${applicationScope.contextPath}を使用してコンテキストパスを取得しないのですか?それは私たちにとってはうまくいきました。

0
Swati