Spring MVC-Rest Controllerで単純な文字列をJSONとして返す方法


137

私の質問は基本的にこの質問のフォローアップです。

@RestController
public class TestController
{
    @RequestMapping("/getString")
    public String getString()
    {
        return "Hello World";
    }
}

上記では、Springは「Hello World」を応答本文に追加します。文字列をJSON応答として返すにはどうすればよいですか?引用符を追加できることは理解していますが、それはハックのように感じられます。

この概念の説明に役立つ例を提供してください。

注:これをHTTP応答本文に直接書きたくはありません。文字列をJSON形式で返したいです(有効なJSON形式の応答を必要とするRestyGWTでコントローラーを使用しています)。


マップまたは文字列を含むオブジェクト/エンティティを返すことができます
Denys Denysiuk

では、文字列値をJSON文字列にシリアル化したいということですか?
Sotirios Delimanolis 2015年

回答:


150

リターンのいずれかtext/plain(のように、Spring MVCの3コントローラからの復帰だけ文字列メッセージ)ORあなたの文字列は、いくつかのオブジェクトでラップ

public class StringResponse {

    private String response;

    public StringResponse(String s) { 
       this.response = s;
    }

    // get/set omitted...
}


応答タイプをMediaType.APPLICATION_JSON_VALUE(= "application/json")に設定します

@RequestMapping(value = "/getString", method = RequestMethod.GET,
                produces = MediaType.APPLICATION_JSON_VALUE)

そしてあなたは次のようなJSONを持っています

{  "response" : "your string value" }

124
またCollections.singletonMap("response", "your string value")、ラッパークラスを作成せずに戻って同じ結果を得ることができます。
Bohuslav Burghardt 2015年

@Bohuslavそれは素晴らしいヒントです。
ショーン

6
キーと値が必要なのは本当ではありません。単一の文字列または文字列の配列はどちらも有効なJSONです。同意できない場合は、jsonlint Webサイトが両方を有効なJSONとして受け入れる理由を説明できます。
KyleM 2016

2
ラッパークラスはどのようにJSONに変換されますか?
ロッキーインデ

3
戻るには十分だと思いますCollections.singleton("your string value")
gauee

54

JSONは基本的にPHPまたはJAVAコンテキストの文字列です。つまり、有効なJSONである文字列を応答で返すことができます。以下は動作するはずです。

  @RequestMapping(value="/user/addUser", method=RequestMethod.POST)
  @ResponseBody
  public String addUser(@ModelAttribute("user") User user) {

    if (user != null) {
      logger.info("Inside addIssuer, adding: " + user.toString());
    } else {
      logger.info("Inside addIssuer...");
    }
    users.put(user.getUsername(), user);
    return "{\"success\":1}";
  }

これは単純な文字列応答では問題ありません。ただし、複雑なJSON応答の場合は、Shaunが説明したラッパークラスを使用する必要があります。


7
これはOPの質問に対する正確な回答であったため、受け入れられるべき回答です。
SRy

おかげで、@ ResponseBodyは私が必要としたものでした
Riskop

publicキーワードの前または後の@ResponseBodyの「より良い」位置はどれですか。それは戻り値でより識別されるので、私はいつもそれを後に置きました。
David Bradley

26

1つのプロジェクトでは、JSONObject(maven dependency info)を使用してこれに対処しました。これを選択したのは、ラッパーオブジェクトではなく単純な文字列を返すことを優先したためです。新しい依存関係を追加したくない場合は、代わりに内部ヘルパークラスを簡単に使用できます。

使用例:

@RestController
public class TestController
{
    @RequestMapping("/getString")
    public String getString()
    {
        return JSONObject.quote("Hello World");
    }
}

1
たぶんあなたはあなたの答えで言及する必要があります、それ"\"Hello World\""は余分な依存関係なしでも同様にうまくいくでしょう-それは何をJSONObject.quote()しているのでしょう?
jerico

解決策は好きではありませんが、うまくいきました。:-)
Michael Hegner

22

あなたは簡単に戻すことができJSONStringプロパティでresponse、次のよう

@RestController
public class TestController {
    @RequestMapping(value = "/getString", produces = MediaType.APPLICATION_JSON_VALUE)
    public Map getString() {
        return Collections.singletonMap("response", "Hello World");
    }
}

2
「@RestController」を使用する場合は常に、「@ ResponseBody」を使用する必要はありません
jitendra varshney

12

デフォルトを登録解除するだけです StringHttpMessageConverterインスタンスを。

@Configuration
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
  /**
   * Unregister the default {@link StringHttpMessageConverter} as we want Strings
   * to be handled by the JSON converter.
   *
   * @param converters List of already configured converters
   * @see WebMvcConfigurationSupport#addDefaultHttpMessageConverters(List)
   */
  @Override
  protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.stream()
      .filter(c -> c instanceof StringHttpMessageConverter)
      .findFirst().ifPresent(converters::remove);
  }
}

コントローラーアクションハンドラーメソッドとコントローラー例外ハンドラーの両方でテスト:

@RequestMapping("/foo")
public String produceFoo() {
  return "foo";
}

@ExceptionHandler(FooApiException.class)
public String fooException(HttpServletRequest request, Throwable e) {
  return e.getMessage();
}

最終メモ:

  • extendMessageConvertersはSpring 4.1.3以降で使用できます。以前のバージョンで実行している場合は、を使用して同じ手法を実装できますがconfigureMessageConverters、少し手間がかかります。
  • これは、他の多くの可能なアプローチの1つのアプローチでした。アプリケーションがJSONのみを返し、他のコンテンツタイプを返さない場合は、デフォルトのコンバーターをスキップして、単一のジャクソンコンバーターを追加することをお勧めします。別のアプローチは、デフォルトのコンバーターを追加することですが、ジャクソンコンバーターが文字列コンバーターの前に来るように、順序は異なります。これにより、コントローラアクションメソッドは、応答のメディアタイプに応じて文字列を変換する方法を指示できます。

1
2番目の最後のメモに関するサンプルコードがあればいいでしょう。
トニーバゲット2016

1
converters.removeIf(c -> c instanceof StringHttpMessageConverter)
クリリス

10

私はこの質問が古いことを知っていますが、私も貢献したいと思います。

他の応答の主な違いは、ハッシュマップの戻りです。

@GetMapping("...")
@ResponseBody
public Map<String, Object> endPointExample(...) {

    Map<String, Object> rtn = new LinkedHashMap<>();

    rtn.put("pic", image);
    rtn.put("potato", "King Potato");

    return rtn;

}

これは戻ります:

{"pic":"a17fefab83517fb...beb8ac5a2ae8f0449","potato":"King Potato"}

2
メソッドをHashMapを返すものとして宣言するのはなぜですか?LHMはMapを実装します。
JL_SO

6

シンプルにする:

    @GetMapping("/health")
    public ResponseEntity<String> healthCheck() {
        LOG.info("REST request health check");
        return new ResponseEntity<>("{\"status\" : \"UP\"}", HttpStatus.OK);
    }

ResponseEntityを使用することは、私にとって最先端のようです。+1
アレクサンダー

5

次のような注釈を追加produces = "application/json"@RequestMappingます。

@RequestMapping(value = "api/login", method = RequestMethod.GET, produces = "application/json")

ヒント:戻り値として、ResponseEntity<List<T>>type を使用することをお勧めします。JSONボディで生成されたデータは、単一の単純な文字列ではなく、その仕様に従って配列またはオブジェクトである必要があるためです。それは時々問題を引き起こすかもしれません(例えばAngular2のObservables)。

差:

Stringjsonとして返されます:"example"

List<String>jsonとして返されます:["example"]


3

@ResponseBody出力ストリームに戻りデータを書き込む注釈を追加します。


1
これは私にはうまくいきませんでした。私が持っています@PostMapping(value = "/some-url", produces = APPLICATION_JSON_UTF8_VALUE)
aliopi '30 / 10/30

0

この問題は私を怒らせました:Springは非常に強力なツールですが、JSONとして出力文字列を書き込むという単純なことは、醜いハックなしでは不可能に思えます。

最も煩わしくなく、最も透過的であると私が思う私の解決策(Kotlinで)は、コントローラーのアドバイスを使用して、リクエストが特定のエンドポイントのセット(REST API)に送信されたかどうかを確認することです(REST APIは通常、ここからすべての回答をJSONとして返したいためです)返されたデータがプレーン文字列(「JSON逆シリアル化を行わないでください!」)か、それ以外(「JSON逆シリアル化を実行!」)かどうかに基づいて、フロントエンドで特殊化を行わないでください。これの良い面は、コントローラーが同じままで、ハックなしであるということです。

supportsこの方法は、によって処理されたすべての要求ことを確認しStringHttpMessageConverterて処理されている(ハンドルプレーン文字列を返すすべてのコントローラの出力があることなどコンバータ)とではbeforeBodyWrite方法は、我々は中断し、JSONへの出力を変換したい場合には制御します(それに応じてヘッダーを変更します)。

@ControllerAdvice
class StringToJsonAdvice(val ob: ObjectMapper) : ResponseBodyAdvice<Any?> {
    
    override fun supports(returnType: MethodParameter, converterType: Class<out HttpMessageConverter<*>>): Boolean =
        converterType === StringHttpMessageConverter::class.java

    override fun beforeBodyWrite(
        body: Any?,
        returnType: MethodParameter,
        selectedContentType: MediaType,
        selectedConverterType: Class<out HttpMessageConverter<*>>,
        request: ServerHttpRequest,
        response: ServerHttpResponse
    ): Any? {
        return if (request.uri.path.contains("api")) {
            response.getHeaders().contentType = MediaType.APPLICATION_JSON
            ob.writeValueAsString(body)
        } else body
    }
}

将来的にはHttpMessageConverter、出力に使用する必要があるものをオーバーライドできる簡単な注釈が得られることを願っています。


-5

このアノテーションをメソッドに追加します

@RequestMapping(value = "/getString", method = RequestMethod.GET, produces = "application/json")
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.