2

編集4

私がやりたかったのは、forgotPasswordページを実装することです。例として、以下の例を取り上げましたが、セッションスコープでユーザー名を保持するのは実際のユーザー関連の質問ではありません。

index.xhtmlユーザー名を入力するforgotPasswordページになります。ユーザー名を入力した後、クリックWelcome Me - ActionしてchkMe()、そのユーザーを確認し、そのユーザーの電子メールIDとwelcome.xhtmlで新しいパスワードを送信しますHi User ABC, we have sent new password at asdfasdf@dasf.com


メインポスト

2つのケースで1つのBeanから別のBeanにデータを印刷しようとしています。以下は私が持っているコードです。

index.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://java.sun.com/jsf/core"      
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>JSF 2.0 Hello World</title>
    </h:head>
    <h:body>
        <h3>JSF 2.0 Hello World Example - hello.xhtml</h3>
        <h:form>
           <h:inputText value="#{helloBean.name}"></h:inputText>
           <h:commandButton value="Welcome Me - Plain" action="welcome"></h:commandButton>
           <h:commandButton value="Welcome Me - Action" action="#{helloBean.chkMe()}"></h:commandButton>
        </h:form>
    </h:body>
</html>

welcome.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"    
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>JSF 2.0 Hello World</title>
    </h:head>
    <h:body bgcolor="white">
        <h3>JSF 2.0 Hello World Example - welcome.xhtml</h3>
        <h4>Welcome --#{helloBean.name}--</h4>
    </h:body>
</html>

HelloBean.java

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import java.io.Serializable;
import javax.faces.bean.RequestScoped;

@ManagedBean
@RequestScoped
public class HelloBean implements Serializable {

    private static final long serialVersionUID = 1L;
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String chkMe() {
        return takeMeToAnotherPage("welcome");
    }

    public String takeMeToAnotherPage(String linkToGo) {
        return linkToGo + "?faces-redirect=true";
    }
}

Checkingtextfieldのようにテキストを入力してボタンをクリックすると、welcome.xhtmlにテキストがWelcome Me - Plainテキストとして表示されますが、をクリックするとテキストが表示されません(として表示されます)Welcome --Checking--Welcome Me - ActionWelcome ----

なぜこれが起こっているのか分かりません。

これが起こっている理由のアイデア/提案。


編集1

これはすべて原因だと?faces-redirect=true思いますが、使用しないかのように使用する必要が?faces-redirect=trueあります。アドレスバーのURLは以前のURLです。

たとえば、page1.xhtmlを使用してpage2.xhtmlにアクセスした場合でも、URLにはpage1.xhtmlと表示されます。

したがって、そのような場合に何をすべきかわからない。


編集2

さて、私が実際にやりたいのは、index.xhtmlにユーザー名を入力するforgotPasswordページです(上記の例を考慮)。そのユーザー名が正しい場合は、welcome.xhtmlにHi User ABC, Please use new password for next login. We have sent you email at blah@blah.com

RequestScopeは完全に機能していましたが、問題はURLアドレスにあるため、を追加しまし?faces-redirect=trueた。しかし、リダイレクトとして、httpセッションが閉じているため、welcome.xhtmlでは、値が取得されません(これが上記で発生していることです)。

からの別の解決策はFlashScopeskuntsel使用することでしたが、ここでも問題は、welcome.xhtmlを更新すると、データが失われ、私を夢中にさせることです。

誰かが何をする必要があるかについて提案できますか?


編集3

セッションスコープの問題は以下のとおりです。

2つのタブを開き、両方のタブにindex.xhtmlがあるとします。tab1で、入力FahimしてクリックしWelcome Me - Actionました。tab1に、welcome.xhtmlが表示され、テキストがとして表示されWelcome Fahimます。これは完璧です。

次に、tab2に移動し、名前を「」と入力し、 「welcome.xhtml」XYZをクリックすると、テキストが「」と表示されます。これも完璧です。Welcome Me - ActionWelcome XYZ

問題は、tab1に戻ってページを更新するときです。tab1(welcome.xhtml)を更新するとWelcome XYZ、以前と同じようにどちらが間違っているかがわかりWelcome FahimますWelcome Fahim

4

8 に答える 8

2

私の好みでは、セッションスコープで現在のユーザーを使用することをお勧めします。

ただし、それがあなたに合わない場合は、さらにいくつかの選択肢を提供できます。

ビューパラメータとしてユーザー名を渡す

これは

<h:form>
    <h:inputText value="#{helloBean.name}"/>
    <h:commandButton value="Welcome Me - Action" action="#{helloBean.chkMe}"/>
</h:form>

public String chkMe() {
    return takeMeToAnotherPage("welcome");
}

public String takeMeToAnotherPage(String linkToGo) {
    return linkToGo + "?faces-redirect=true&username=" + name;
}

および追加のビューパラメータwelcome.xhtml

<f:metadata>
    <f:viewParam name="username"/>
</f:metadata>

別のオプションは、別のリクエストスコープのBeanをちょうど間に合うようにインスタンス化し、それに情報を渡すことです。

<h:form>
   <h:inputText value="#{helloBean.name}"/>
   <h:commandButton value="Welcome Me - Plain" action="welcome">
       <f:setPropertyActionListener value="#{helloBean.name}" target="#{welcomePageBean.username}"/>
   </h:commandButton>
</h:form>

@ManagedBean
@RequestScoped
WelcomePageBean {

    private String username;//+getter + setter
    //other fields associated with the welcome view

}

Flashオブジェクトの使用

詳細エントリビュー(フラグメント)base.xhtml、:

<h:form>
    <h:outputText value="Enter user name for password reset: " />
    <h:inputText value="#{flash.username}" />
    <br/>
    <h:commandButton value="Send me a confirmation email" action="#{forgotBean.changePassword}" />
<h:form>

ForgotBeanbase.xhtml

@ManagedBean
@RequestScoped
public class ForgotBean {

    public ForgotBean() {   }

    public String changePassword() {
        //check user constraints and return failure outcome in case somthing is wrong
        //generate new password and persist it to the database
        //send a configmation e-mail
        return "successful-reset?faces-redirect=true";
    }

}

サクセスビュー(フラグメント)successful-reset.xhtml、:

<h:outputText value="Password was reset for user #{receptorBean.username}, e-mail configmation sent." />
<br/>
<h:link value="View homepage" outcome="home" />

ReceptorBeansuccessful-reset.xhtml

@ManagedBean
@RequestScoped
public class ReceptorBean {

    @ManagedProperty("#{flash}")
    private Flash flash;

    private String username;

    public ReceptorBean() {   }

    public String getUsername() {
        if(username == null) {
            String uname = (String)flash.get("username");
            flash.keep("inputText");
            username= uname;
        }
        return username;
    }

    public Flash getFlash() {
        return flash;
    }

    public void setFlash(Flash flash) {
        this.flash = flash;
    }

}
于 2013-02-23T07:59:20.777 に答える
1

ページを更新しているときは、新しいリクエストをサーバーに送信しています。HTTP プロトコルはステートレスであるため、URL はタブ間で一意であるため、URL に追加することで、私が「user1」であることをサーバーに伝えることができます。

URL にパラメータを追加したくない場合、サーバーはセッションを使用して 2 つのリクエスト間の関係を維持します。その後、セッションを使用して変数などを保存できます。

あなたの場合、タブ間でデータを共有したくないため、機能しません。タブ間でデータが共有される理由は、セッション Cookie がタブで共有されるためです。

Cookie を無効にし、URL 書き換えを使用してセッションを維持する必要があります。

http://docstore.mik.ua/orelly/java-ent/servlet/ch07_03.htm

使用しているアプリケーション サーバーは、sessionid が追加された jsf で URL を維持する必要がありますが、リダイレクト中に URL にセッション ID を追加する必要がある場合があります。

    public String takeMeToAnotherPage(String linkToGo) {
           HttpServletRequest req = (HttpServletRequest)FacesContext.getCurrentInstance().
getExternalContext().getRequest(); 
           return linkToGo + "?faces-redirect=true&jsessionid=" 
+ req.getSession().getId();
        }

アプリケーションは複数のタブで使用されるため、セッション Cookie を無効にし、URL 書き換えを使用してセッションを維持できます。

http://jf.omnis.ch/archives/2004/12/disabling-session-cookie-in-tomcat.html

https://community.jboss.org/thread/141685

もう 1 つのオプションは、ユーザーがウェルカム ページにアクセスするたびに新しい会話を開始する会話スコープを使用することです。アプリケーションは 2 つの異なる会話を 2 つのタブで維持します。詳細については、こちら を参照してください。

http://www.andygibson.net/blog/tutorial/cdi-conversations-part-2/

于 2013-02-28T02:38:48.777 に答える
1

Flash オブジェクトの使用例

何が機能していないのか、実際にはわかりません。以下は、最初maindependentビューのコマンド ボタンが依存ビューへのリダイレクトをトリガーし、JSFFlashオブジェクトを使用してデータ転送を処理するビューの実際の例です。提供された回答では、2 番目のページ データ ( username)は、ポストバックとページ更新アクションの両方で存続します

もう一度コードを再投稿します。

メイン ビュー:

<h:form>
    <h:outputText value="Enter your name: " />
    <h:inputText value="#{flash.username}" />
    <h:commandButton value="Reset my password" action="#{forgotBean.changePassword}" />
</h:form>

豆を忘れた:

@ManagedBean
@RequestScoped
public class ForgotBean {

    public ForgotBean() {   }

    public String changePassword() {
        return "/dependent?faces-redirect=true";
    }

}

従属ビュー:

<h:form>
   <h:outputText value="#{confirmBean.username}: your password has been changed and the confirmation has been sent to [get value from your bean]" />
   <h:commandButton value="Postback" />
</h:form>

豆の確認:

@ManagedBean
@RequestScoped
public class ConfirmBean {

    @ManagedProperty("#{flash}")
    private Flash flash;

    private String username;

    public ReceivedBean() {   }

    public String getUsername() {
        if(username == null) {
            String uname = (String)flash.get("username");
            flash.keep("username");
            username = uname;
        }
        return username;
    }

    public Flash getFlash() {
        return flash;
    }

    public void setFlash(Flash flash) {
        this.flash = flash;
    }

}

または、 の で前処理 (ユーザー名の検証、電子メールなどのデータの収集など) を行うこともでき@PostConstructますConfirmBean@ViewScopedまた、ポストバックで前処理が行われないようにすることもできます。

メソッドのキックオフ例@PostConstruct:

@PostConstruct
public void init() {
    //without managed property flash object is also available via
    //FacesContext.getCurrentInstance().getExternalContext().getFlash()
    String name = flash.get("username");
    flash.keep("username");
    //do necessary validations
    //get necessary data from your service
    //handle wrong user input
    this.username = name;
    //set up other data of the bean
    //change setter and getter of the field username to 'ordinary'
}

セッション スコープ Bean を使用した例

Bean をページの更新後も存続させたい (つまり@ViewScoped失敗する) 場合、Post-Redirect-Get を実装したい (したがって失敗する) 場合、基本的に同じデータ (事実上<f:setPropertyActionListener>未定義) で作業したい場合、異なるタブ (したがって、失敗する)、意味のある get 要求を避けたい場合 (したがって、失敗する)、私が見る唯一の方法 ( JSF 内にとどまる) は、ユーザー入力データをコレクションに保持する a を使用することです. このように、ページの更新ポストバックリダイレクトマルチタブ、および意味のあるビュー パラメータFlash<f:viewParam>@SessionScoped @ManagedBean制限が緩和され、アプリケーションで有効になります。

提供されたコードは、例外処理/チェックを考慮していないことに注意してください。そして、より重要で関連性の高いのは、マップ内のキーとしてインクリメントされた整数を、 UUIDなどのランダムな値に置き換える方が適切であることです

ビュー (インデックスは m、ウェルカムは d):

( m.xhtml):

<h:body>
    <h:form>
        <h:inputText value="#{forgotBean2.username}" />
        <h:commandButton value="Change my password" action="#{forgotBean2.changePassword}" />
    </h:form>
</h:body>

( d.xhtml):

<f:metadata>
    <f:viewParam name="id" required="true" />
</f:metadata>
<h:body>
    <h:form>
        <h:outputText value="#{confirmBean2.username}: your password has been changed" />
        <h:commandButton value="Postback" action ="#{confirmBean2.postbackAction}" />
    </h:form>
</h:body>

Bean ( ForgotBean2- でのビジネス アクション用m.xhtmlConfirmBean2- での表示および (可能な) アクション用d.xhtmlUserRequestsBean- セッションでの情報の格納用):

( ForgotBean2):

@ManagedBean
@RequestScoped
public class ForgotBean2 {

    @ManagedProperty("#{userRequestsBean}")
    private UserRequestsBean userRequestsBean;

    private String username;

    public ForgotBean2() {   }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public UserRequestsBean getUserRequestsBean() {
        return userRequestsBean;
    }

    public void setUserRequestsBean(UserRequestsBean userRequestsBean) {
        this.userRequestsBean = userRequestsBean;
    }

    public String changePassword() {
        //do business job
        Map<Integer, String> fMap = userRequestsBean.getRequests();
        int id = 0;
        if(fMap.containsValue(username)) {
            for(Map.Entry<Integer, String> entry : fMap.entrySet()) {
                if(entry.getValue().equals(username)) {
                    id = entry.getKey();
                    break;
                }
            }
        } else {
            if(fMap.isEmpty()) {
                id = 1;
            } else {
                id = (int)Collections.max(fMap.keySet()) + 1;
            }
            fMap.put(id, username);
        }
        return "/q15038451/d?faces-redirect=true&id=" + Integer.toString(id);
    }

}

( ConfirmBean2):

@ManagedBean
@ViewScoped
public class ConfirmBean2 implements Serializable {

    @ManagedProperty("#{userRequestsBean}")
    private UserRequestsBean userRequestsBean;

    private Integer id;

    private String username;

    public ConfirmBean2() {   }

    @PostConstruct
    public void init() {
        if(id == null) {
            String vpid = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("id");
            id = Integer.parseInt(vpid);
        }
        Map<Integer, String> fMap = userRequestsBean.getRequests();
        String uname = fMap.get(id);
        //do necessary validations
        //get necessary data from your service
        //handle wrong user input
        this.username = uname;
        //set up other data of the bean
    }

    public UserRequestsBean getUserRequestsBean() {
        return userRequestsBean;
    }

    public void setUserRequestsBean(UserRequestsBean userRequestsBean) {
        this.userRequestsBean = userRequestsBean;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String postbackAction() {
        return null;
    }

}

( UserRequestsBean):

@ManagedBean
@SessionScoped
public class UserRequestsBean implements Serializable {

    private Map<Integer, String> requests = new HashMap<Integer, String>();

    public UserRequestsBean() {   }

    public Map<Integer, String> getRequests() {
        return requests;
    }

    public void setRequests(Map<Integer, String> requests) {
        this.requests = requests;
    }

}

前述のようUUIDに、インクリメントされた s の代わりに s を生成する (一意のランダムな値を提供する) ことが必要になる場合がありますInteger

于 2013-02-27T08:27:26.573 に答える
1

JSF から抽象化し、HTTP レベルからの要件を分析してみましょう。

URL は、welcome.xhtml に変更する必要があります

これは、ボタンがクリックされたときに、2 つのリクエストをサーバーに送信する必要があることを意味します。

POST index.xhtml (with username parameter in the request body)- ユーザーの入力をサーバーに送信する

GET welcome.xhtml- 新しいページを取得する

サーバーGET welcome.xhtmlがリクエストを処理するとき、データを取得する方法は 2 つあります。

  • クライアントのリクエストからデータを取得します (GET リクエストでデータを渡す唯一の方法は、URL に入れることです)。
  • サーバーで使用できるようにします (「POST index.xhtml」処理で保存する必要があります)。

ページは更新に耐える必要があります

リフレッシュは、最後のリクエストの単なる繰り返しです。GET welcome.xhtmlリクエストを繰り返していることを意味します。ここでも、データを取得する同じ 2 つの方法があります (URL からとサーバーから)。

URL にデータ (ユーザー名) を含めることはできません

これは、クライアントの要求からデータを取得できないことを意味します。したがって、データを取得する唯一の方法は、「POST index.xhtml」処理中に最初にサーバーに保存することです。

異なるデータを持つ複数のタブ

おっと、ここまで生き残った唯一の解決策は、最後の要件を満たすことができません。タブは同じブラウザー セッション (同じ Cookie など) を共有するため、異なるタブからの要求を区別する方法はありません。そのため、異なるタブからのリクエストはまったく同じように見えます。ユーザーがページを更新したのか、URL をコピーして新しいタブを開いたのかを判断する方法はありません。

結論

要件を厳密にすると、必要なものを実装することはできません (これは JSF の制限ではなく、HTTP の仕組みです)。したがって、要件を緩和する必要があります。

複数のタブのドロップのサポート - セッション スコープを使用できます。

ドロップ サバイバル リフレッシュ - フラッシュ スコープを使用できます。


そして最も興味深いのは、URL でデータを渡さないという要件を緩和したことです。

最も簡単にできるのは、ユーザー名パラメーターをそのまま渡すことです。これはセキュリティ リスクだと言うかもしれませんが、リスクはPOST index.xhtmlリクエストでユーザー名を渡すのとほとんど同じだと思います (パラメータの渡し方が少し異なります)。私が見た追加のセキュリティ リスクは、誰かがユーザーのコンピューターを通り過ぎて、ブラウザーの URL バーでユーザー名を詮索できることです。

より高度なアプローチは、ユーザー名を暗号化し、暗号化された値を URL に渡し、welcome.xhtml の処理時に復号化することです。

そして、このアプローチをさらに発展させれば、いわゆる Conversation スコープに近づきます (JBoss Seam の長期にわたる会話を確認してください)。これは、(ユーザーの観点から) 無意味なパラメーターを URL に追加する場合であり、パラメーターの値は、サーバー上のデータ マップのキーとして機能する識別子です。

Conversation スコープのダミー実装:

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

@ManagedBean
@SessionScoped
public class Bean implements Serializable {

    private int prevCid = 0;

    private Map<String, Object> conversationScope = new HashMap<String, Object>();

    public String getName() {
        return (String) getData();
    }

    public void setName(String name) {
        setData(name);
    }

    public String chkMe() {
        return takeMeToAnotherPage("welcome");
    }

    public String takeMeToAnotherPage(String linkToGo) {
        return linkToGo + "?cid=" + getCid() + "&faces-redirect=true";
    }

    private Object getData() {
        return conversationScope.get(getCid());
    }

    private void setData(Object o) {
        conversationScope.put(getCid(), o);
    }

    private String getCid() {
        HttpServletRequest req = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
        String cid = (String) req.getAttribute("cid");

        if (cid == null) {
            cid = req.getParameter("cid");
            if (cid == null) {
              cid = "" + prevCid++;
            }
            req.setAttribute("cid", cid);
        }

        return cid;
    }
}
于 2013-02-27T09:37:50.307 に答える
1

このコードをインデックス ページの body タグの下に追加します

<t:saveState value="#{helloBean}" />

t tagライブラリ用

<%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>

tomahawk lib も追加します。

savestate または param の受け渡しについてわからない場合は、このリンクを参照してください

これは、値を渡すための基本的な手順に役立ちます。jsfのページ間。

于 2013-02-26T10:09:42.723 に答える
1

ページを更新しているときは、新しいリクエストをサーバーに送信しています。HTTP プロトコルはステートレスであるため、URL はタブ間で一意であるため、URL に追加することで、私が「user1」であることをサーバーに伝えることができます。

URL にパラメータを追加したくない場合、サーバーはセッションを使用して 2 つのリクエスト間の関係を維持します。その後、セッションを使用して変数などを保存できます。

あなたの場合、タブ間でデータを共有したくないため、機能しません。タブ間でデータが共有される理由は、セッション Cookie がタブで共有されるためです。

Cookie を無効にし、URL 書き換えを使用してセッションを維持する必要があります。

http://docstore.mik.ua/orelly/java-ent/servlet/ch07_03.htm

使用しているアプリケーション サーバーは、sessionid が追加された jsf で URL を維持する必要がありますが、リダイレクト中に URL にセッション ID を追加する必要がある場合があります。

  public String takeMeToAnotherPage(String linkToGo) {
           HttpServletRequest req = (HttpServletRequest)FacesContext.getCurrentInstance().
getExternalContext().getRequest(); 
           return linkToGo + "?faces-redirect=true&jsessionid=" 
+ req.getSession().getId();
        }

アプリケーションは複数のタブで使用されるため、セッション Cookie を無効にし、URL 書き換えを使用してセッションを維持できます。

http://jf.omnis.ch/archives/2004/12/disabling-session-cookie-in-tomcat.html

https://community.jboss.org/thread/141685

もう 1 つのオプションは、ユーザーがウェルカム ページにアクセスするたびに新しい会話を開始する会話スコープを使用することです。アプリケーションは 2 つの異なる会話を 2 つのタブで維持します。詳細については、こちら を参照してください。

http://www.andygibson.net/blog/tutorial/cdi-conversations-part-2/

于 2013-02-28T20:30:29.580 に答える
1

したがって、Bean は @RequestScoped である可能性があります。セッション スコープ Bean に変更します。

于 2013-02-23T07:10:09.273 に答える
1

faces-redirect=true は、http 転送の代わりに http リダイレクトを行うと思います。リダイレクトは、新しい http 要求を送信するブラウザーによって処理されます。RequestScoped Mbean を使用しているため、新しいリクエストは新しい MBean でレンダリングされます。したがって、SessionScoped Mbean を使用するか、リダイレクトせずにナビゲートしてください。

于 2013-02-23T07:06:09.123 に答える