次のようなコントローラーを使用してjson RESTサービスを構築しました。
@Controller
@RequestMapping(value = "/scripts")
public class ScriptController {
@Autowired
private ScriptService scriptService;
@RequestMapping(method = RequestMethod.GET)
@ResponseBody
public List<Script> get() {
return scriptService.getScripts();
}
}
正常に機能しますが、すべての応答を変更し、すべての応答に「ステータス」フィールドと「メッセージ」フィールドを追加する必要があります。私はいくつかのソリューションについて読んだことがあります:
コントローラメソッドから返された値をクラスのオブジェクトにラップしたい場合、他の一般的な正しい解決策を提案できますか?
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
}
同様の問題に遭遇したので、サーブレットフィルターを使用して解決することをお勧めします。
サーブレットフィルタはJavaクラスであり、バックエンドでリソースにアクセスする前にクライアントからのリクエストをインターセプトするため、またはクライアントに返される前にサーバーからの応答を操作するためにサーブレットプログラミングで使用できます。
フィルターはjavax.servlet.Filterインターフェースを実装し、3つのメソッドをオーバーライドする必要があります。
public void doFilter (ServletRequest, ServletResponse, FilterChain)
このメソッドは、チェーンの最後にあるリソースに対するクライアントリクエストのために、リクエスト/レスポンスのペアがチェーンを通過するたびに呼び出されます。
public void init(FilterConfig filterConfig)
フィルターがサービスを開始する前に呼び出され、フィルターの構成オブジェクトを設定します。
public void destroy()
フィルターがサービスを停止した後に呼び出されます。
任意の数のフィルターを使用する可能性があり、実行順序は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>
...
そのため、このフィルターはコントローラーの応答を取得し、Stringに変換し、RestResponseクラスオブジェクト(ステータスフィールドとメッセージフィールドを含む)にフィールドとして追加し、オブジェクトをJsonにシリアル化し、完全な応答をクライアントに送信します。
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();
}
}
chain.doFilter(request、responseWrapper)メソッドは、チェーン内の次のフィルターを呼び出すか、呼び出し側フィルターがチェーン内の最後のフィルターである場合、サーブレットロジックを呼び出します。
HTTPサーブレット応答ラッパーは、サーブレットが書き込みを終了した後、ラッパーが応答データを操作できるようにするカスタムサーブレット出力ストリームを使用します。通常、これは、サーブレット出力ストリームが閉じられた後(基本的に、サーブレットがコミットした後)には実行できません。これが、ServletOutputStreamクラスにフィルター固有の拡張機能を実装する理由です。
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);
}
}
FilterServletOutputStreamクラスを使用するには、応答オブジェクトとして機能できるクラスを実装する必要があります。このラッパーオブジェクトは、サーブレットによって生成された元の応答の代わりにクライアントに送り返されます。
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();
}
}
このアプローチはあなたの問題の良い解決策になると思います。
不明な点がある場合は質問してください。間違っている場合は修正してください。
Spring 4.1以降を使用している場合、 ResponseBodyAdvice を使用して、本文が書き込まれる前に応答をカスタマイズできます。