Mam przykładowy RestController w Spring Boot:
@RestController
@RequestMapping("/api")
class MyRestController
{
@GetMapping(path = "/hello")
public JSONObject sayHello()
{
return new JSONObject("{'aa':'bb'}");
}
}
Używam biblioteki JSON org.json
Kiedy /hello
trafiam na API , pojawia się wyjątek mówiący:
Servlet.service () dla serwletu [dispatcherServlet] w kontekście ze ścieżką [] zgłosił wyjątek [przetwarzanie żądania nie powiodło się; zagnieżdżony wyjątek to java.lang.IllegalArgumentException: nie znaleziono konwertera dla wartości zwracanej typu: class org.json.JSONObject] z główną przyczyną
java.lang.IllegalArgumentException: nie znaleziono konwertera dla wartości zwracanej typu: class org.json.JSONObject
Jaki jest problem? Czy ktoś może wyjaśnić, co dokładnie się dzieje?
java
json
spring-boot
response
iwekesi
źródło
źródło
@GetMapping(path = "/hello") @ResponseBody public String sayHello() {return"{'aa':'bb'}";}
Odpowiedzi:
Ponieważ używasz sieci Spring Boot, zależność Jacksona jest niejawna i nie musimy jawnie definiować.
pom.xml
Jeśli używasz zaćmienia, możesz sprawdzić zależność Jacksona na swojej karcie hierarchii zależności.Jak już dodałeś,
@RestController
nie ma potrzeby wykonywania jawnej konwersji json. Po prostu zwróć POJO, a serializator jackson zajmie się konwersją do json. Jest równoważne użyciu,@ResponseBody
gdy jest używane z @Controller. Zamiast umieszczać@ResponseBody
na każdej metodzie kontrolera, którą umieszczamy@RestController
zamiast wanilii@Controller
i@ResponseBody
domyślnie jest stosowana do wszystkich zasobów w tym kontrolerze.Skorzystaj z tego linku: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-responsebody
Problem polega na tym, że zwrócony obiekt (JSONObject) nie ma funkcji pobierającej dla niektórych właściwości. Twoim zamiarem nie jest serializacja tego obiektu JSONObject, ale serializacja POJO. Więc po prostu zwróć POJO.
Skorzystaj z tego linku: https://stackoverflow.com/a/35822500/5039001
Jeśli chcesz zwrócić serializowany ciąg json, po prostu zwróć ciąg. W tym przypadku Spring użyje StringHttpMessageConverter zamiast konwertera JSON.
źródło
Map<String,Object>
lubProperties
obiektuPrzyczyną, dla której obecne podejście nie działa, jest to, że Jackson jest domyślnie używany do serializacji i deserializacji obiektów. Jednak nie wie, jak serializować
JSONObject
. Jeśli chcesz stworzyć dynamiczną strukturę JSON, możesz użyćMap
na przykład:@GetMapping public Map<String, String> sayHello() { HashMap<String, String> map = new HashMap<>(); map.put("key", "value"); map.put("foo", "bar"); map.put("aa", "bb"); return map; }
Doprowadzi to do następującej odpowiedzi JSON:
{ "key": "value", "foo": "bar", "aa": "bb" }
Jest to trochę ograniczone, ponieważ dodawanie obiektów podrzędnych może być nieco trudniejsze. Jackson ma jednak swój własny mechanizm, używając
ObjectNode
iArrayNode
. Aby z niego skorzystać, musisz automatycznie podłączyćObjectMapper
się do usługi / kontrolera. Następnie możesz użyć:@GetMapping public ObjectNode sayHello() { ObjectNode objectNode = mapper.createObjectNode(); objectNode.put("key", "value"); objectNode.put("foo", "bar"); objectNode.put("number", 42); return objectNode; }
Takie podejście umożliwia dodawanie obiektów podrzędnych, tablic i używanie wszystkich różnych typów.
źródło
ObjectMapper
którego powinieneś automatycznie połączyć (zobacz akapit nad moim ostatnim fragmentem kodu).java.util.HashMap
jest podstawową funkcjonalnością Javy od wersji 1.2.Możesz zwrócić odpowiedź zgodnie z
String
sugestią @vagaasen lub użyćResponseEntity
obiektu dostarczonego przez Spring, jak poniżej. W ten sposób możesz również wrócić,Http status code
co jest bardziej pomocne w wywołaniu usługi internetowej.@RestController @RequestMapping("/api") public class MyRestController { @GetMapping(path = "/hello", produces=MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<Object> sayHello() { //Get data from service layer into entityList. List<JSONObject> entities = new ArrayList<JSONObject>(); for (Entity n : entityList) { JSONObject entity = new JSONObject(); entity.put("aa", "bb"); entities.add(entity); } return new ResponseEntity<Object>(entities, HttpStatus.OK); } }
źródło
JSONObject
, ale ponieważ Spring używa Jacksona, zmieniłem go naHashMap
a i wtedy kod, który przeczytałem w tej odpowiedzi, zadziałał.możesz również użyć do tego hashmap
@GetMapping public HashMap<String, Object> get() { HashMap<String, Object> map = new HashMap<>(); map.put("key1", "value1"); map.put("results", somePOJO); return map; }
źródło
@RequestMapping("/api/status") public Map doSomething() { return Collections.singletonMap("status", myService.doSomething()); }
PS. Działa tylko dla 1 wartości
źródło
posługiwać się
ResponseEntity<ResponseBean>
Tutaj możesz użyć ResponseBean lub Any java bean, jak chcesz, aby zwrócić swoją odpowiedź API i jest to najlepsza praktyka. Użyłem Enum do odpowiedzi. zwróci kod stanu i komunikat o stanie interfejsu API.
@GetMapping(path = "/login") public ResponseEntity<ServiceStatus> restApiExample(HttpServletRequest request, HttpServletResponse response) { String username = request.getParameter("username"); String password = request.getParameter("password"); loginService.login(username, password, request); return new ResponseEntity<ServiceStatus>(ServiceStatus.LOGIN_SUCCESS, HttpStatus.ACCEPTED); }
dla odpowiedzi ServiceStatus lub (ResponseBody)
public enum ServiceStatus { LOGIN_SUCCESS(0, "Login success"), private final int id; private final String message; //Enum constructor ServiceStatus(int id, String message) { this.id = id; this.message = message; } public int getId() { return id; } public String getMessage() { return message; } }
Spring REST API powinien mieć poniższy klucz w odpowiedzi
ostateczną odpowiedź otrzymasz poniżej
{ "StatusCode" : "0", "Message":"Login success" }
możesz użyć ResponseBody (java POJO, ENUM itp.) zgodnie z wymaganiami.
źródło
Bardziej poprawne tworzenie DTO dla zapytań API, na przykład entityDTO:
Ale jeśli potrzebujesz zwrócić różne parametry mapy, możesz użyć następnych dwóch przykładów
2. Aby zwrócić jeden parametr, taki jak map:
źródło
Jeśli chcesz zwrócić obiekt JSON za pomocą ciągu, to powinno działać:
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.http.ResponseEntity; ... @RestController @RequestMapping("/student") public class StudentController { @GetMapping @RequestMapping("/") public ResponseEntity<JsonNode> get() throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); JsonNode json = mapper.readTree("{\"id\": \"132\", \"name\": \"Alice\"}"); return ResponseEntity.ok(json); } ... }
źródło