JSONペイロードを含むPOSTリクエストをリモートサーバーに送信しようとしています。
このGET curlコマンドは正常に機能します。
curl -H "Accept:application/json" --user [email protected]:aaa "http://www.aaa.com:8080/aaa-project-rest/api/users/1" -i
そして、これPOST=
curl -H "Accept:application/json" -H "Content-Type: application/json" "http://www.aaa.com:8080/aaa-project-rest/api/users/login" -X POST -d "{ \"email\" : \"[email protected]\", \"password\" : \"aaa\" }" -i
そして、私はAndroidアプリでそれを模倣しようとしています。
アプリは最初のGETリクエストで正常に動作しますが、2番目のPOST one。
GETリクエストで機能するコードは次のとおりです。
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
HttpHeaders httpHeaders = Common.createAuthenticationHeaders("[email protected]" + ":" + "aaa");
User user = null;
ResponseEntity<User> responseEntity = restTemplate.exchange("http://" + REST_Host + ":8080/aaa-project-rest/api/users/" + 1L, HttpMethod.GET, new HttpEntity<Object>(httpHeaders), User.class);
POSTリクエストのソースコードは次のとおりです。
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
User user = null;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
httpHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
JSONObject jsonCredentials = new JSONObject();
jsonCredentials.put("email", REST_LOGIN);
jsonCredentials.put("password", REST_PASSWORD);
ResponseEntity<User> responseEntity = restTemplate.exchange("http://" + REST_Host + ":" + REST_PORT + "/" + REST_APP + "/api/users/login",
HttpMethod.POST, new HttpEntity<Object>(jsonCredentials, httpHeaders), User.class);
ただし、次のメッセージが表示されます。
Could not write request: no suitable HttpMessageConverter found for request type [org.json.JSONObject] and content type [application/json]
Spring RESTコントローラ:
@RequestMapping(value = RESTConstants.SLASH + RESTConstants.LOGIN, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<UserResource> login(@Valid @RequestBody CredentialsResource credentialsResource, UriComponentsBuilder builder) {
HttpHeaders responseHeaders = new HttpHeaders();
User user = credentialsService.checkPassword(credentialsResource);
userService.clearReadablePassword(user);
if (user == null) {
return new ResponseEntity<UserResource>(responseHeaders, HttpStatus.NOT_FOUND);
} else {
tokenAuthenticationService.addTokenToResponseHeader(responseHeaders, credentialsResource.getEmail());
responseHeaders.setLocation(builder.path(RESTConstants.SLASH + RESTConstants.USERS + RESTConstants.SLASH + "{id}").buildAndExpand(user.getId()).toUri());
UserResource createdUserResource = userResourceAssembler.toResource(user);
ResponseEntity<UserResource> responseEntity = new ResponseEntity<UserResource>(createdUserResource, responseHeaders, HttpStatus.CREATED);
return responseEntity;
}
}
@RequestMapping(value = RESTConstants.SLASH + "{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<UserResource> findById(@PathVariable Long id, UriComponentsBuilder builder) {
HttpHeaders responseHeaders = new HttpHeaders();
User user = userService.findById(id);
if (user == null) {
return new ResponseEntity<UserResource>(responseHeaders, HttpStatus.NOT_FOUND);
} else {
UserResource userResource = userResourceAssembler.toResource(user);
responseHeaders.setLocation(builder.path(RESTConstants.SLASH + RESTConstants.USERS + RESTConstants.SLASH + "{id}").buildAndExpand(user.getId()).toUri());
ResponseEntity<UserResource> responseEntity = new ResponseEntity<UserResource>(userResource, responseHeaders, HttpStatus.OK);
return responseEntity;
}
}
CredentialsResourceクラスのコード:
public class CredentialsResource extends ResourceSupport {
@NotEmpty
@Email
private String email;
@NotEmpty
private String password;
public CredentialsResource() {
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
返事はかなり遅れましたが、私はちょうど同じ問題にぶつかり、それを解決するのに少し時間がかかりました。だから、私はそれを共有し、私の解決策を追跡する方が良いと思う。
実際、スローされた例外は完全に誤解を招くものです。問題は、MappingJackson2HttpMessageConverter
は私のオブジェクトをマーシャリングする方法を知りませんでした-これはJSONであるように見えますが、基礎となるObjectMapper
の構成です。
私がやったのは、disableプロパティSerializationFeature.FAIL_ON_EMPTY_BEANS
そのような
restTemplate = new RestTemplate();
MappingJackson2HttpMessageConverter jsonHttpMessageConverter = new MappingJackson2HttpMessageConverter();
jsonHttpMessageConverter.getObjectMapper().configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
restTemplate.getMessageConverters().add(jsonHttpMessageConverter);
そして、すべてが期待どおりに動作し始めました。
これを機能させるには、いくつかのことをしなければなりませんでした。
まず、次のようにJSONObjectを文字列に変換する必要がありました。
HttpEntity<String> entityCredentials = new HttpEntity<String>(jsonCredentials.toString(), httpHeaders);
その理由は、JSONObjectクラスにはマッピングメッセージコンバーターがあり、Stringクラスにはマッピングコンバーターがあるためです。
次に、RestTemplateコンストラクターに真の値を渡す必要がありました。そうしないと、400 Bad Requestを受け取ります。
RestTemplate restTemplate = new RestTemplate(true);
True値は、デフォルトのコンバーターを使用するよう残りのテンプレートに指示します。これがなぜそうなのか誰かが知っているなら、私はもっと知りたいです。
3番目に、不要なJacksonコンバーターを削除しました。
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
これらの処理が完了すると、リクエストは正常に機能します。
完全なコードは次のとおりです。
RestTemplate restTemplate = new RestTemplate(true);
User user = null;
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
httpHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
try {
JSONObject jsonCredentials = new JSONObject();
jsonCredentials.put("email", REST_LOGIN);
jsonCredentials.put("password", REST_PASSWORD);
Log.e(Constants.APP_NAME, ">>>>>>>>>>>>>>>> JSON credentials " + jsonCredentials.toString());
HttpEntity<String> entityCredentials = new HttpEntity<String>(jsonCredentials.toString(), httpHeaders);
ResponseEntity<User> responseEntity = restTemplate.exchange("http://" + REST_Host + ":" + REST_PORT + "/" + REST_APP + "/api/users/login",
HttpMethod.POST, entityCredentials, User.class);
if (responseEntity != null) {
user = responseEntity.getBody();
}
return user;
} catch (Exception e) {
Log.e(Constants.APP_NAME, ">>>>>>>>>>>>>>>> " + e.getLocalizedMessage());
}
return null;
Jacksonコンバーターを明示的に使用して、残りのテンプレートコンストラクターの真の値をスキップする方法があると思いますが、これは単なる推測です。
クラスパスにJSON実装が存在しない場合、例外「適切なHTTPMessageConverterが見つかりません」がスローされます。 RestTemplateのソースコードを見てください:
public RestTemplate() {
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter());
this.messageConverters.add(new SourceHttpMessageConverter<Source>());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
this.messageConverters.add(new AtomFeedHttpMessageConverter());
this.messageConverters.add(new RssChannelHttpMessageConverter());
}
if (jackson2XmlPresent) {
this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
}
else if (jaxb2Present) {
this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}
if (jackson2Present) {
this.messageConverters.add(new MappingJackson2HttpMessageConverter());
}
else if (gsonPresent) {
this.messageConverters.add(new GsonHttpMessageConverter());
}
}
クラスパスにJSON実装を追加します。例:
implementation "com.fasterxml.jackson.core:jackson-databind:2.9.0"
遅すぎます。しかし、ここにいます。
Java JSONObjectではなくObject、次のようなものを送信することを修正しました。
JSONObject jsonCredentials = new JSONObject();
jsonCredentials.put("email", REST_LOGIN);
jsonCredentials.put("password", REST_PASSWORD);
// Generic Object. It might be a DTO.
Object jsonCredentialsObj = JsonUtils.stringToObject(jsonCredentials.toString(), Object.class);
ResponseEntity<User> responseEntity = restTemplate.exchange("http://" + REST_Host + ":" + REST_PORT + "/" + REST_APP + "/api/users/login",
HttpMethod.POST, new HttpEntity<Object>(jsonCredentialsObj, httpHeaders), User.class);
JsonUtils.stringToObjectは、独自の変換ユーティリティです。
SpringのRestTemplateは、Android org.json.JSONObjectクラス。org.json.JSONObjectは「通常」/デスクトップJavaでは使用できません)
AndroidをサポートするRestTemplate
Jackson JSONプロセッサー、Jackson 2.x、およびGoogle Gson。
per: https://docs.spring.io/autorepo/docs/spring-Android/1.0.1.RELEASE/reference/html/rest-template.html
ジャクソンをクラスパスに使用すると、RestTemplateはJava Beansをシリアル化する方法を理解する必要があります。
CredentialsResource cr = new CredentialsResource();
cr.setEmail(...);
cr.setPassword(...);
HttpEntity<CredentialsResource> entityCredentials = new HttpEntity<CredentialsResource>(cr, httpHeaders);
追伸JSONObjectを使用して続行したい場合、独自のJSONObjectHttpMessageConverterを書くのは非常に簡単です。コンバーターは.toString()を呼び出すだけです