J'ai construit un service json REST avec des contrôleurs comme celui-ci:
@Controller
@RequestMapping(value = "/scripts")
public class ScriptController {
@Autowired
private ScriptService scriptService;
@RequestMapping(method = RequestMethod.GET)
@ResponseBody
public List<Script> get() {
return scriptService.getScripts();
}
}
Cela fonctionne bien, mais maintenant je dois modifier toutes les réponses et ajouter des champs "état" et "message" à chacun d'eux. J'ai lu quelques solutions:
Pouvez-vous suggérer une autre solution, générale et correcte, si je veux encapsuler les valeurs renvoyées par les méthodes du contrôleur dans des objets de classe:
public class RestResponse {
private int status;
private String message;
private Object data;
public RestResponse(int status, String message, Object data) {
this.status = status;
this.message = message;
this.data = data;
}
//getters and setters
}
J'ai rencontré un problème similaire et je vous suggère d'utiliser des filtres de servlet pour le résoudre.
Les filtres de servlet sont Java classes qui peuvent être utilisées dans la programmation de servlet pour intercepter les demandes d'un client avant d'accéder à une ressource à l'arrière-plan ou pour manipuler les réponses du serveur avant qu'elles ne soient renvoyées au client.
Votre filtre doit implémenter l'interface javax.servlet.Filter et remplacer trois méthodes:
public void doFilter (ServletRequest, ServletResponse, FilterChain)
Cette méthode est appelée chaque fois qu'une paire demande/réponse est passée à travers la chaîne en raison d'une demande client pour une ressource à la fin de la chaîne.
public void init(FilterConfig filterConfig)
Appelé avant la mise en service du filtre et définit l'objet de configuration du filtre.
public void destroy()
Appelé après la mise hors service du filtre.
Il existe la possibilité d'utiliser n'importe quel nombre de filtres, et l'ordre d'exécution sera le même que l'ordre dans lequel ils sont définis dans le web.xml.
web.xml:
...
<filter>
<filter-name>restResponseFilter</filter-name>
<filter-class>
com.package.filters.ResponseFilter
</filter-class>
</filter>
<filter>
<filter-name>anotherFilter</filter-name>
<filter-class>
com.package.filters.AnotherFilter
</filter-class>
</filter>
...
Ainsi, ce filtre obtient la réponse du contrôleur, la convertit en chaîne, ajoute comme feild à votre objet de classe RestResponse (avec les champs d'état et de message), sérialise l'objet en Json et envoie la réponse complète au client.
Classe ResponseFilter:
public final class ResponseFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) response);
chain.doFilter(request, responseWrapper);
String responseContent = new String(responseWrapper.getDataStream());
RestResponse fullResponse = new RestResponse(/*status*/, /*message*/,responseContent);
byte[] responseToSend = restResponseBytes(fullResponse);
response.getOutputStream().write(responseToSend);
}
@Override
public void destroy() {
}
private byte[] restResponseBytes(RestResponse response) throws IOException {
String serialized = new ObjectMapper().writeValueAsString(response);
return serialized.getBytes();
}
}
la méthode chain.doFilter (request, responseWrapper) appelle le filtre suivant de la chaîne ou si le filtre appelant est le dernier filtre de la chaîne, appelle la logique de servlet.
L'encapsuleur de réponse de servlet HTTP utilise un flux de sortie de servlet personnalisé qui permet à l'encapsuleur de manipuler les données de réponse une fois l'écriture de la servlet terminée. Normalement, cela ne peut pas être fait après la fermeture du flux de sortie du servlet (essentiellement, après que le servlet l'a validé). C'est la raison de l'implémentation d'une extension spécifique au filtre à la classe ServletOutputStream.
Classe FilterServletOutputStream:
public class FilterServletOutputStream extends ServletOutputStream {
DataOutputStream output;
public FilterServletOutputStream(OutputStream output) {
this.output = new DataOutputStream(output);
}
@Override
public void write(int arg0) throws IOException {
output.write(arg0);
}
@Override
public void write(byte[] arg0, int arg1, int arg2) throws IOException {
output.write(arg0, arg1, arg2);
}
@Override
public void write(byte[] arg0) throws IOException {
output.write(arg0);
}
}
Pour utiliser la classe FilterServletOutputStream, une classe pouvant agir comme objet de réponse doit être implémentée. Cet objet wrapper est renvoyé au client à la place de la réponse d'origine générée par le servlet.
Classe ResponseWrapper:
public class ResponseWrapper extends HttpServletResponseWrapper {
ByteArrayOutputStream output;
FilterServletOutputStream filterOutput;
HttpResponseStatus status = HttpResponseStatus.OK;
public ResponseWrapper(HttpServletResponse response) {
super(response);
output = new ByteArrayOutputStream();
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (filterOutput == null) {
filterOutput = new FilterServletOutputStream(output);
}
return filterOutput;
}
public byte[] getDataStream() {
return output.toByteArray();
}
}
Je pense que cette approche sera une bonne solution pour votre problème.
S'il vous plaît, posez une question, si quelque chose n'est pas clair et corrigez-moi si je me trompe.
Si vous utilisez Spring 4.1 ou supérieur, vous pouvez utiliser ResponseBodyAdvice pour personnaliser la réponse avant l'écriture du corps.