スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Spring MVC PRGパターンでリロード対策を行う

今回は、PRGパターンを使ってリロード対策をしてみたいと思う。

PRGは、POST、REDIRECT、GETの頭文字をそれぞれ取っており、重要なのは、この順番で処理を行う点にある。

実際に、PRGパターン適用前後でどうなるのかもあわせて確認してみたい。

◎動作検証にあたっての各バージョンは以下の通り
  • SpringFramework 3.2.8.RELEASE
  • Java 1.7
  • Tomcat 7.0

1.BookController.java 修正前(関連箇所のみ掲載)

@Controller
@RequestMapping("/book")
@SessionAttributes("bookSearchForm")
public class BookController {

private static Logger logger = LoggerFactory.getLogger(BookController.class);

@Value("${upload.fileSize}")
private int uploadFileSize;

@Autowired
protected BookService<BookEntity> bookService;

@RequestMapping(value = "/edit/update", method = RequestMethod.POST)
public String update(@Valid BookForm form, BindingResult result, Model model,
@RequestParam(value="uploadFile",required=false) MultipartFile file) {

logger.info("update start");
if (file.getSize() > uploadFileSize) {
result.rejectValue("cover", "error.fileSize",
new Object[]{uploadFileSize},"File size error");
}
if (result.hasErrors()) {
return "editBook";
}

if (!file.isEmpty()) {
byte[] fileContent = null;
InputStream is = null;
try {
is = file.getInputStream();
fileContent = IOUtils.toByteArray(is);
form.setCover(fileContent);
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(is);
}
}

BookEntity entity = BookUtil.copyProperties(form);
//登録・編集の判定
if (entity.getBookId() > 0) {
bookService.editEntity(entity);
} else {
bookService.addEntity(entity);
}
model.addAttribute("book", entity);
return "result";
}

//他のメソッド省略
}

今回対象となるメソッドは、新規登録・編集画面の更新処理であるupdate(15行目)となる。現在このupdateメソッドはPOST(14行目)であり、入力チェックで問題なければ、DBの更新処理を行い、49行目の「return "result";」で結果画面を返却している。
これの何が問題となるのか、最初に「リロード対策」と記述したように、新規登録完了画面でF5キーを押下して、確認してみたい。

2.新規登録完了画面
①新規登録画面で「更新」ボタンを押下した後の新規登録完了画面
PRG_1.png

ユーザーの意図によって「更新」ボタンが押下され、新規登録されており、問題ない。

②そのまま新規登録完了画面で「F5」キーを押下
PRG_2.png

すでに登録が完了しているにも関わらず、誤って「F5」キーを押下してリロードした場合、IDがインクリメントされていることから分かるように、同じ内容のものが再び登録されている。

今回は、データの新規登録を例としているが、商品の購入画面などでも同様で、リロードによって同じ商品を購入する状況は避けなければならない。その対策手段として、PRGパターンを適用することとなる。

それでは、どうすればPRGパターンになるのか実装してみる。

3.BookController.java 修正後(関連箇所のみ掲載)

@Controller
@RequestMapping("/book")
@SessionAttributes("bookSearchForm")
public class BookController {

private static Logger logger = LoggerFactory.getLogger(BookController.class);

@Value("${upload.fileSize}")
private int uploadFileSize;

@Autowired
protected BookService<BookEntity> bookService;

@RequestMapping(value = "/edit/update", method = RequestMethod.POST)
public String update(@Valid BookForm form, BindingResult result, Model model,
@RequestParam(value="uploadFile",required=false) MultipartFile file) {

logger.info("update start");
if (file.getSize() > uploadFileSize) {
result.rejectValue("cover", "error.fileSize",
new Object[]{uploadFileSize},"File size error");
}
if (result.hasErrors()) {
return "editBook";
}

if (!file.isEmpty()) {
byte[] fileContent = null;
InputStream is = null;
try {
is = file.getInputStream();
fileContent = IOUtils.toByteArray(is);
form.setCover(fileContent);
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(is);
}
}

BookEntity entity = BookUtil.copyProperties(form);
//登録・編集の判定
if (entity.getBookId() > 0) {
bookService.editEntity(entity);
} else {
bookService.addEntity(entity);
}
//修正前のソースをコメント化している
//model.addAttribute("book", entity);
//return "result";
return "redirect:/book/edit/" + entity.getBookId() + "/update";
}

@RequestMapping(value = "/edit/{id}/update", method = RequestMethod.GET)
public String show (@PathVariable("id") long bookId, BookForm form, Model model) {
logger.info("show start");
BookEntity entity = bookService.findById(bookId);
model.addAttribute("book", entity);
return "result";
}

//他のメソッド省略
}

変更点はupdateメソッドの49~51行目と新たに追加した54~60行目までのshowメソッドとなる。
最初にPOST→REDIRECT→GETの順番が重要と記載したが、その通りに実装している。修正前はPOSTのupdateメソッドで、結果画面を返却していたが、51行目のredirectで一回クライアントに返し、GETのshowメソッドで該当エンティティを取得するように変更している。DB更新処理までをupdateメソッドで行い、登録した情報をshowメソッドで取得するように分割した形となる。PRGパターンを適用することにより、登録したデータを再度取得する処理が増えることにはなるが、誤って「F5」キーを押下された場合でも、実行される処理は最後のshowメソッドとなり、データを取得するだけの安全サイドに倒すことができる。

4.新規登録完了画面
①新規登録画面で「更新」ボタンを押下した後の新規登録完了画面
PRG_3.png

URLが修正前と変わっていることが確認できる。

②そのまま新規登録完了画面で「F5」キーを押下
PRG_3.png

IDがインクリメントされることなく、同じデータが読み込まれているが分かる。

■過去のSpring関連記事
Spring関連記事 Index

スポンサーサイト

コメントの投稿

非公開コメント

プロフィール

bookmount8

Author:bookmount8
システムエンジニア。サーバーサイドでjavaを扱うことが多い。最近は、ミドルやフロント周りも関心あり。

最新記事
カテゴリ
検索フォーム
最新コメント
月別アーカイブ
これまでの訪問者数
ブロとも申請フォーム

この人とブロともになる

RSSリンクの表示
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。