JSONとして送る想定のように見えますがそうなっていませんね。
Jqueryのajaxを使ってSpringBootのRESTコントローラーにデータを渡したい
解決したいこと
jQueryのajaxを使ってSpringBootのRESTコントローラーにデータを渡したい
発生している問題・エラー
SpringBootでjQuery経由でRESTを使用してデータベース更新を行うプログラムの中で
htmlのデータをコントローラーに引き渡す箇所でスタックしています。
・①コードの★①部分/htmlのformタグ内のデータをjqueryで抽出→OK
・ajaxの処理は指定のコントローラーに移っています。
・②コードの★②部分/@RestControllerメソッドでデータを受け取る→ここがNG
のような状況です。
③はhtmlのデータを受け取るフォームクラスです。
詳細は以下のコードをご参照いただければと思います。
質問が拙く申し訳ないのですが、ご教授いただければ幸いです。
何卒、宜しくお願いいたします。
該当するソースコード (「→→→」は出力結果)
①detail.js (htmlからformデータを抽出しajax処理を行う)
★①の箇所をご覧いただければと思いますが、htmlからデータは取れています。
/**
* pdetail.htlmの変更ボタンを押した時に、画面データをデータベースにupdateする処理
*/
'use strict'
/**画面ロード時の処理 */
jQuery(function($){
$('#btn-alteration').click(function(event){
update();
});
});
function update(){
//htmlのformタグ内のinputタグ情報を取得
let programForm=$('#programdetail-form').serialize();
console.log('#programdetail-form='+programForm);
//★①この部分のログを確認すると
//→→→programname=test1&programcode=111&inUse=2
//とhtmlのformタグの情報が取れています。
$.ajax({
type:'PUT',
//コントローラのurlパターン
url:'/rest/alteration',
//ここで★①のデータを渡す
data:programForm,
dataType:'json',
contentType:'application/json;charset=UTF-8',
cache:false,
timeout:5000,
}).done(function(data){
alert('情報を変更します。\n一覧画面で結果を確認して下さい。');
window.location.href='/programlist';
}).fail(function(jqXHR,textStatus,errorThrown){
alert('通信エラー');
}).always(function(){
});
}
②DetailRestController.java(htmlのデータを引き取り処理するクラス)
★②の箇所をご覧いただければと思いますが、引数で受け取ったオブジェクトのtoString()の結果は、全プロパティがnullとなっています。
import org.modelmapper.ModelMapper;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.AllArgsConstructor;
import test.form.ProgramForm;
import test.model.ProgramData;
import test.service.ProgramDataService;
@RequestMapping("/rest")
@RestController
@AllArgsConstructor
public class ProgramDataRestController {
private ProgramDataService programDataService;
private ModelMapper modelMapper;
@PutMapping("/alteration")
//ProgramFormクラスはhtmlのデータを抽出するフォームクラス(③参照)
public int putAltProgramData(ProgramForm programForm){
//★②ここで引数のprogramFormオブジェクトを出力
System.out.println("programForm="+programForm);
//→→→ProgramForm(programname=null, programcode=null, inUse=null)
//となっており、★①のデータが届いていません。
//フォームデータをエンティティにマッピング
ProgramData data=modelMapper.map(programForm,ProgramData.class);
//doUpdate()は、mysql のUPDATE句。WHEREはprogramcode。
//戻り値は更新されたデータの数
int result = programDataService.doUpdate(data);
System.out.println("result="+result);
//→→→programcode=nullのため、update対象レコードなしということで
//→→→result=0
return result;
}
}
③ProgramForm.java(htmlのデータを受け取るフォームクラス)
import javax.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class ProgramForm {
@NotBlank
private String myprogramcode;
private String programname;
private Integer inUse;
}
自分で試したこと
・①detail.jsでhtmlフォームタグのデータ取得をserialize()からserializeArray()にしてみましたが、結果は変わりませんでした。
let programForm=$('#programdetail-form').serializeArray();
・②DetailRestController.javaのputAltProgramData()メソッドの引数内に@RequestParam("programForm")を付加してみましが、結果は変わりませんでした。
@PutMapping("/alteration")
public int putAltProgramData(@RequestParam("programForm") ProgramForm programForm)
4Answer
Comments
@page2
Questioner@woxtu様
早速にコメントありがとうございます。
サーバー⇔クライアント間でオブジェクトのJSONデータは、「クエリ文字列に変換」されるようなのですが、この位置で、クエリ文字列が露出されるのは何かおかしいのか?もしれません。チェックしてみます。- GETリクエストの場合は基本的にbodyを持たないのでURLにクエリ文字列を付加する形でリクエストします。
```
$ curl -X GET 'https://httpbin.org/get?foo=bar'
```
ただ、今回はPUTリクエストなのでbodyにJSONを付加してリクエストすることになると思います。
```
$ curl -X PUT -H 'Content-Type: application/json' -d '{"foo":"bar"}' https://httpbin.org/put
```
というのを抑えておいたほうが良いかもしれませんね。 @page2
Questioner@woxtu様
確かにです。今回PUTなのでリクエストボディが存在します。
意外と基本を忘れていました。
ご指摘、ありがとうございます!
@woxtu さんの回答と同様となりますが、
data:programForm,
contentType:'application/json;charset=UTF-8',
programForm
がapplication/x-www-form-urlencoded
の形式なのに、contentType
がapplication/json;charset=UTF-8
になっているのが気になります。
contentType
の行を削除してみるといかがでしょうか。
Comments
- あと、
(ProgramForm programForm)
を
(@ModelAttribute ProgramForm programForm)
にする必要があります。
https://www.early2home.com/blog/programming/java/post-1746.html @page2
Questioner@yucatio様
コメントありがとうございます!レスポンス遅くなって申し訳ございません。
また、有用なブログ記事の紹介もありがとうございます。
&結合文字列の件を始め、いろいろと試してみました。
◆(@ModelAttribute ProgramForm programForm)の時
結果は同じでした。レストコントローラーの引数の各要素がnullのままで、画面のデータが受け取れていないようでした。
ajax→レストコントローラーなので、JSON形式でデータが渡されているためでしょうか?また、戻り値もJSON形式なので、画面に渡すModelクラスが有効になっていないためなのか?
◆ご紹介いただいたブログでは、@RequestBodyでJSON形式のデータが受け取れるとありましたので、一応、(@RequestBody ProgramForm programForm)でもやってみましたが、
コンソールで400が表示されました。
最初にご指摘いただいた通り、データがJSON形式なのか?怪しい感じでしょうか?
◆①detail.jsの★①の部分の&で結合された文字列について
http://semooh.jp/jquery/api/ajax/jQuery.ajax/options/
のdata属性の箇所に
「サーバに送信する値。オブジェクトが指定された場合、クエリー文字列に変換されてGETリクエストとして付加されます。(省略) &foo=bar1&foo=bar2 のように組み立てられます。」
とありました。
&で結合されたkey=valueがどこかでJSON形式に変換されるのか?
とも読めるのですが・・・。
ちなみに、現在serialize()で取得しているフォームデータをserializeArray()としてみても同じ結果でした。
いただいたアドバイスをもとに、もう少し見直してみます。
ありがとうございます!- > 最初にご指摘いただいた通り、データがJSON形式なのか?怪しい感じでしょうか?
ご自身で確認しているとおり、
> console.log('#programdetail-form='+programForm);
> //★①この部分のログを確認すると
> //→→→programname=test1&programcode=111&inUse=2
programFormの値はprogramname=test1&programcode=111&inUse=2という文字列で、これは"application/x-www-form-urlencoded"という形式です。JSON形式ではありません。
> &で結合されたkey=valueがどこかでJSON形式に変換されるのか?
> ajax→レストコントローラーなので、JSON形式でデータが渡されているためでしょうか?
"application/x-www-form-urlencoded"の形式は自動ではjsonに変換されません。
> @RequestBodyでJSON形式のデータが受け取れるとありましたので
そのためには、リクエストにJSON文字列を指定する必要があります。
ためしに、ajaxで送信するコードを、
data: JSON.stringify({ programname: "test1", programcode: 111, inUse: 2 }),
contentType:'application/json;charset=UTF-8',
のように変えて、
(@RequestBody ProgramForm programForm)
で受け取れるでしょうか。
> 戻り値もJSON形式なので
リクエストの形式と戻り値の形式は関係ありませんので気にしなくて大丈夫です。 - ところで戻り値はintなのでJSON形式ではないと思います。
@yucatio様
返信遅くなって申し訳ございません。アドバイスいただき、誠にありがとうございます!
アドバイスいただいた点を反映させて試してしておりました。
結果以下のコードで受け取れるようになりました。
【受け取れる状況】
programdetail.jsのcontentType:'application/json;charset=UTF-8'をコメントアウト
(プロパティにDate型が入っているのが怪しいと思ったのですが、
const json = JSON.stringify(formData);
でコンソール上に出してみると、ちゃんとJSON化出来ていました。)
serialize(),serializeArray()でcontentType,@ResponseBodyのありなしで検証した結果を添付しました。(小さいので、拡大しないと見えないのですが・・・)
@ModelAttribute付加では、エラーはでないのですが、全てのパターンで値が受け取れない状況でした。
悲しいことに中身は理解できておりませんが、感謝の気持ちを込めて、ご報告させていただきます。
【programdetail.jsの状況】
確認①
let formData=$('#programdetail-form').serialize();
で取得したデータは、JSON化可能なデータであることを確認
確認②
contentType:'application/json;charset=UTF-8',
をコメントアウト
すると、DetailRestController.javaは変更なしでデータを受け取れました。
【DetailRestController.java】
確認①
@ModelAttribute付加では、値取得できませんでした。
確認②
@RequestBody付加では、
contentType:'application/json;charset=UTF-8'アリの時は400(BadRequest)
contentType:'application/json;charset=UTF-8'無しの時は415(サポートされていないペイロード)
が返ってきました。
function programAlter(){
let formData=$('#programdetail-form').serialize();
//確認①
const json = JSON.stringify(formData);
//ajax
$.ajax({
type:"PUT",
cache:false,
url:'/rest/alteration',
data:formData,
dataType:'json',
//確認②
//contentType:'application/json;charset=UTF-8',
timeout:5000,
}).done(function(data){
//ajax成功時の処理
console.log(data.toString());
//jsonを変数に入れる
//dataはレスポンスのJSONデータ
alert('情報を変更します。\n一覧画面で結果を確認して下さい。');
window.location.href='/admin/programlist';
}).fail(function(jqXHR,textStatus,errorThrown){
alert('通信エラー');
//jax失敗時の処理
console.log("jqXHR:"+jqXHR);
console.log("textStatus:"+textStatus);
console.log("errorThrown:"+errorThrown);
console.log('XMLHttpRequest : ' + XMLHttpRequest.status);
console.log('textStatus : ' + textStatus);
console.log('errorThrown : ' + errorThrown.message);
}).always(function(){
![Something went wrong]()
});
}
data: JSON.stringify({ programname: "test1", programcode: 111, inUse: 2 }),
contentType:'application/json;charset=UTF-8',
と
(@RequestBody ProgramForm programForm)
のパターンは試したでしょうか。
@RequestBody
は送信の値がJSONやXMLのときに有効です。
Comments
@page2
Questioner@yucatio様
試してみました。
ご指摘の表記は実行できました!
データ形式の認識が甘かったようです。
アドバイスいただきとても勉強になりました。
ありがとうございました。