J'ai une application spring-boot 1.1.7 qui utilise Thymeleaf pour une grande partie de l'interface utilisateur. La réponse de mes contrôleurs n'a donc pas vraiment posé de problème. Cependant, je dois maintenant fournir une réponse XML lorsqu'un utilisateur envoie une demande via une URL.
Voici une demande typique:
http://localhost:9001/remote/search?sdnName=Victoria&address=123 Maple Ave
Voici la majeure partie de ma configuration de grade:
project.ext {
springBootVersion = '1.1.7.RELEASE'
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
compile("org.springframework.boot:spring-boot-starter-thymeleaf")
compile("org.springframework.boot:spring-boot-starter-security")
compile("org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion")
compile("org.springframework.security:spring-security-web:4.0.0.M1")
compile("org.springframework.security:spring-security-config:4.0.0.M1")
compile('org.thymeleaf.extras:thymeleaf-extras-springsecurity3:2.1.1.RELEASE')
compile("org.springframework.boot:spring-boot-starter-actuator")
compile('com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.5.0')
}
Et voici mon contrôleur:
@Controller
public class RemoteSearchController {
@Autowired
private SdnSearchService sdnSearchService;
@RequestMapping(value = "/remote/search", method = RequestMethod.GET, produces = MediaType.APPLICATION_XML_VALUE)
public List<Sdn> search(@ModelAttribute SdnSearch sdnSearch) {
List<Sdn> foundSdns = sdnSearchService.find( sdnSearch );
return foundSdns;
}
Voici mon objet à retourner:
@Entity
public class Sdn {
@Id
private long entNum;
private String sdnName;
...
//getters & setters here
}
Je peux recevoir la demande via le client REST (tel que CocoaREST) et la gérer. Mais quand je retourne la liste des SDN, j'obtiens l'exception suivante, même si j'ai Jackson & jackson-dataformat-xml sur mon classpath:
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.Java:229)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.Java:301)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.Java:248)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.Java:57)
at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.Java:299)
Mon REST client inclut un en-tête Accept de "text/xml" (mais, en toute honnêteté, je préférerais qu'il ne soit pas nécessaire de le définir. Idéalement, tout appel de ce contrôleur obtiendra toujours le code XML, même si cet en-tête est présent). .
Y at-il un moyen de gérer cela? Je pensais que les convertisseurs de média étaient inclus et revenaient tout ce que le contrôleur leur avait demandé?
SOLUTION: Voir ci-dessous la réponse que j'ai postée.
SOLUTION: J'ai utilisé une combinaison des deux réponses ci-dessous (merci beaucoup!). Je poste ici au cas où quelqu'un aurait besoin d'aide.
Mon contrôleur modifié:
@Controller
public class RemoteSearchController {
@Autowired
private SdnSearchService sdnSearchService;
@RequestMapping(value = "/remote/search", method = RequestMethod.GET, produces = { "application/xml", "text/xml" }, consumes = MediaType.ALL_VALUE )
@ResponseBody
public SdnSearchResults search(@ModelAttribute SdnSearch sdnSearch) {
List<Sdn> foundSdns = sdnSearchService.find( sdnSearch );
SdnSearchResults results = new SdnSearchResults();
results.setSdns( foundSdns );
return results;
}
}
Et sur mon client, j'ai défini les en-têtes de requête:
Content-type: application/text Accepter: text/xml Je pense que le problème était que les en-têtes de mes clients n'étaient pas correctement définis. Il est donc possible que je n'ai pas dû effectuer certaines de ces modifications. Mais j’ai aimé l’idée d’une classe SearchResults contenant une liste de résultats:
@XmlRootElement
public class SdnSearchResults {
private List<Sdn> sdns;
...
}
J'ai eu exactement le même problème et j'ai trouvé la solution sur le site Web de documentation Spring: ici
En synthèse, j'ai ajouté la dépendance suivante au pom.xml
de mon projet:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
Ensuite, j'ai ajouté le bloc de code suivant à la classe que le service devait renvoyer:
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Greeting {...}
Et ça a fonctionné.
Il peut être préférable de créer une nouvelle classe:
public class SdnSearchResult {
private List<Sdn> sdns;
...
}
Ensuite, un léger changement devra être apporté aux classes existantes comme suit:
public interface SdnSearchService {
SdnSearchResult find(SdnSearch sdnSearch);
}
@Controller
public class UISearchController {
@Autowired
private SdnSearchService sdnSearchService;
@RequestMapping("/search")
public ModelAndView search(@ModelAttribute SdnSearch sdnSearch) {
return new ModelAndView("pages/search/results", "sdns", sdnSearchService.find(sdnSearch).getSdns());
}
}
Une fois que cela est fait, l'autre contrôleur doit être codé comme suit:
@Controller
public class RemoteSearchController {
@Autowired
private SdnSearchService sdnSearchService;
@RequestMapping("/remote/search")
@ResponseBody
public SdnSearchResult search(@RequestBody SdnSearch sdnSearch) {
return sdnSearchService.find(sdnSearch);
}
}
Une explication rapide des modifications de votre code:
@RequestBody
désérialisera automatiquement l'intégralité du corps de la requête HTTP en une instance SdnSearch
. Les applications externes soumettent généralement les données de la demande sous forme de corps HTTP. @RequestBody
s'assure donc que la désérialisation en objet Java a lieu automatiquement.@ResponseBody
sérialisera automatiquement la valeur de retour en fonction des capacités du client externe et des bibliothèques disponibles sur le chemin d'accès aux classes. Si Jackson est disponible sur le chemin de classe et que le client a indiqué qu'il peut accepter JSON, la valeur de retour sera automatiquement envoyée en tant que JSON. Si le JRE est 1.7 ou supérieur (ce qui signifie que JAXB est inclus dans le JRE) et que le client a indiqué qu'il peut accepter le format XML, la valeur de retour sera automatiquement envoyée au format XML.List<Sdn>
doit être remplacé par SdnSearchResult
pour que l'application puisse échanger les formats JSON, XML, RSS et ATOM avec une seule méthode de contrôleur, car XML (et les formats basés sur XML) nécessitent une balise racine sur la sortie, qu'un List<Sdn>
ne peut pas traduit en.Une fois ces modifications effectuées, démarrez un client REST tel que l'extension Postman pour Chrome et envoyez une demande à /remote/search
avec les informations suivantes:
Accepts
défini sur application/json
.Content-Type
défini sur application/json
.{ "sdnName" : "Victoria", "address" : "123 Maple Ave" }
.Cela vous donnera une réponse JSON.
Vous avez marqué la méthode du contrôleur comme produisant des réponses application/xml
(produces = MediaType.APPLICATION_XML_VALUE
). L'en-tête d'acceptation de la demande (Accept: text/xml
) ne correspond pas, donc Spring détermine que votre méthode search
ne peut pas gérer la demande.
Il existe différentes manières de résoudre ce problème sur le serveur, en fonction de vos besoins:
produces
entièrementproduces = { "application/xml", "text/xml" }
En plus de ce que Michael a dit dans sa réponse , j’ai aussi ajouté les dépendances suivantes à pom.xml
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>
<version>4.4.1</version>
</dependency>
Pour une raison quelconque, le fichier jackson-dataformat-xml à lui seul n'a pas aidé . Je me suis également assuré que ResponseEntity est renvoyé dans l'appel get et j'ai supprimé le produit: MediaType de l'annotation RequestMapping.
Avec ces modifications, j'ai pu obtenir les données correctes mais je devais donner l'extension de type mime à l'URL REST lors de l'appel. c'est-à-dire, spécifiez explicitement comme: http: // localhost: 8080/hello.xml ou http: // localhost: 8080/hello.json dans le navigateur
Dans mon cas, je voulais renvoyer une chaîne XML formatée et tout était combiné en une seule ligne.
L'ajout de produits = {"application/xml", "text/xml"} au mappage de la demande était suffisant pour renvoyer la chaîne au format XML (avec indentation).
exemple:
@RequestMapping(method= RequestMethod.GET, value="/generate/{blabla}", produces = { "application/xml", "text/xml" })
public String getBlaBla(@PathVariable("param") String param) throws IOException {
}
Bonne chance.