Bahar MVC - dinlenme denetleyicisi basit dize JSON olarak döndürme


137

Sorum aslında bu sorunun bir takibi .

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

Yukarıda, Spring yanıt gövdesine "Merhaba Dünya" ekleyecekti. Bir String'i JSON yanıtı olarak nasıl döndürebilirim? Alıntı ekleyebileceğimi anlıyorum, ama bu daha çok bir kesmek gibi geliyor.

Lütfen bu kavramı açıklamaya yardımcı olacak örnekler verin.

Not: Bu doğrudan HTTP Yanıt gövdesine yazılı istemiyorum, Dize JSON biçiminde ( yanıt geçerli JSON biçiminde olmasını gerektirir RestyGWT ile kullanıyorum) dönmek istiyorum .


Harita veya dizenizi içeren herhangi bir nesneyi / nesneyi iade edebilirsiniz
Denys Denysiuk

Yani String değerinin bir JSON dizesine serileştirilmesini mi istiyorsunuz?
Sotirios Delimanolis

Yanıtlar:


151

Ya dönüş text/plain(olduğu gibi Bahar MVC 3 Denetleyicisi'nden sadece dize mesajı Return ) VEYA dize bazı nesnedir sarılıyor

public class StringResponse {

    private String response;

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

    // get/set omitted...
}


Yanıt türünüzü MediaType.APPLICATION_JSON_VALUE(= "application/json") olarak ayarlayın

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

ve benzeyen bir JSON'a sahip olacaksınız

{  "response" : "your string value" }

124
Collections.singletonMap("response", "your string value")Bir sarıcı sınıfı oluşturmak zorunda kalmadan aynı sonucu elde etmek için geri dönebilirsiniz .
Bohuslav Burghardt

@Bohuslav Bu harika bir ipucu.
Shaun

6
Bir anahtar ve bir değer gerektirdiği doğru değil. Tek bir Dize veya bir dizi dizi geçerli JSON'dur. Eğer katılmıyorsanız, jsonlint web sitesinin neden her ikisini de geçerli JSON olarak kabul ettiğini açıklayabilirsiniz.
KyleM

2
wrapper sınıfı nasıl JSON'a dönüştürülür?
Rocky Inde

3
Ben geri dönmek için yeterli olduğunu düşünüyorumCollections.singleton("your string value")
gauee

54

JSON aslında PHP veya JAVA bağlamında bir String'dir. Bu, geçerli JSON olan dizenin yanıt olarak döndürülebileceği anlamına gelir. Aşağıdaki çalışmalıdır.

  @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}";
  }

Bu basit dize yanıtı için uygundur. Ancak karmaşık JSON yanıtı için Shaun tarafından tarif edildiği gibi sarmalayıcı sınıfı kullanmalısınız.


7
Bu, OP'nin sorusunun kesin cevabı olduğu için cevap kabul edilmelidir.
SRy

Teşekkürler, @ResponseBody ihtiyacım olan
şeydi

@ResponseBody için genel anahtar kelimeden önce veya sonra "daha iyi" konum hangisi? Her zaman sonra koydum, çünkü dönüş değeri ile daha fazla tanımlanıyor.
David Bradley

26

Bir projede bunu JSONObject (maven bağımlılık bilgisi) kullanarak ele aldık ) . Bunu seçtik çünkü bir sarmalayıcı nesnesi yerine basit bir String döndürmeyi tercih ettik. Yeni bir bağımlılık eklemek istemiyorsanız bunun yerine dahili bir yardımcı sınıf kolayca kullanılabilir.

Örnek Kullanım:

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

1
Belki cevabınızda, "\"Hello World\""ekstra bağımlılık olmadan da işe yarayacağını belirtmelisiniz - bu ne işe yarar JSONObject.quote(), değil mi?
jerico

Çözümü sevmiyorum, ama benim için çalıştı. :-)
Michael Hegner

22

Kolayca dönebilirsiniz JSONile Stringözelliğinde responseşu şekilde

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

2
'@RestController' kullandığınızda, '@ResponseBody' kullanmanız gerekmez
jitendra varshney

12

Varsayılan StringHttpMessageConverterörneğin kaydını silmeniz yeterlidir :

@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);
  }
}

Hem denetleyici eylem işleyicisi yöntemleri hem de denetleyici özel durum işleyicileri ile test edilmiştir:

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

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

Son notlar:

  • extendMessageConvertersBahar 4.1.3'ten beri kullanılabilir, önceki bir sürümde kullanıyorsanız aynı tekniği kullanarak uygulayabilirsiniz, configureMessageConverterssadece biraz daha fazla iş gerektirir.
  • Bu, diğer birçok olası yaklaşımın bir yaklaşımıydı, eğer uygulamanız sadece JSON döndürüyorsa ve başka içerik türleri yoksa, varsayılan dönüştürücülerden atlayıp tek bir jackson dönüştürücü eklemeniz daha iyi olur. Başka bir yaklaşım, varsayılan dönüştürücüler eklemektir, ancak jackson dönüştürücüsü dizeden öncekidir. Bu, denetleyici eylem yöntemlerinin, cevabın ortam türüne bağlı olarak String'in nasıl dönüştürülmesini istediklerini belirlemesine izin vermelidir.

1
Son notunuzla ilgili örnek bir kod olması iyi olur.
Tony Baguette

1
converters.removeIf(c -> c instanceof StringHttpMessageConverter)
chrylis -koutiouslyoptimistic-

10

Bu sorunun eski olduğunu biliyorum ama ben de katkıda bulunmak istiyorum:

Diğer yanıtlar arasındaki temel fark hashmap geri dönüşüdür.

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

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

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

    return rtn;

}

Bu geri dönecektir:

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

2
Neden yöntemi HashMap döndürdüğünü bildiriyorsunuz? LHM Harita uygular.
JL_SO

6

Ekle produces = "application/json"yılında @RequestMappinggibi açıklama:

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

İpucu: Dönüş değeri olarak ResponseEntity<List<T>>türü kullanmanızı öneririm . Çünkü JSON gövdesinde üretilen verilerin, tek bir basit dize yerine özelliklerine göre bir dizi veya nesne olması gerekir . Bazen sorunlara neden olabilir (örn. Açısal2'deki Gözlenebilirler).

Fark:

Stringjson olarak döndü :"example"

List<String>json olarak döndü :["example"]


6

Basitleştirin:

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

Bir ResponseEntity kullanmak benim için son teknoloji gibi görünüyor . +1
Alexander

3

@ResponseBodyÇıkış akışına dönüş verileri yazacak ek açıklama ekleyin .


1
bu benim için işe yaramadı. Ben var@PostMapping(value = "/some-url", produces = APPLICATION_JSON_UTF8_VALUE)
aliopi

0

Bu sorun beni deli etti: Bahar çok güçlü bir araç ve yine de, JSON gibi bir çıktı String'i yazmak gibi basit bir şey çirkin kesmek olmadan imkansız görünüyor.

En az müdahaleci ve en şeffaf bulduğum çözüm (Kotlin'de), bir denetleyici tavsiyesi kullanmak ve isteğin belirli bir uç nokta kümesine gidip gitmediğini kontrol etmek. ve döndürülen verilerin düz bir dize ("JSON serisini kaldırma!") veya başka bir şey ("JSON serisini kaldırma!") olup olmamasına bağlı olarak ön uçta uzmanlaşma yapmayın. Bunun olumlu yönü denetleyicinin aynı ve korsansız kalmasıdır.

supportsYöntemiyle işlenen tüm istekler emin olur StringHttpMessageConverterişlenir (kolları düz dizeleri döndüren tüm denetleyicileri çıkış olduğunu örn dönüştürücü) ve beforeBodyWriteyöntemle, biz kesmek ve JSON çıktı dönüştürmek istediğiniz durumlarda kontrol (ve başlıkları buna göre değiştirin).

@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
    }
}

Gelecekte HttpMessageConverterçıktı için kullanılması gerekenleri geçersiz kılabileceğimiz basit bir ek açıklama alacağımızı umuyorum .


-5

Bu ek açıklamayı yönteminize ekleyin

@RequestMapping(value = "/getString", method = RequestMethod.GET, produces = "application/json")
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.