Request description
Nous devons implémenter un GET d’un fichier PDF (DocumentDB) via les API mappées (RESTMappedObjectsExternalObject). Nous avons réussi à “coder” la chose mais si le résultat est délivré comme attendu, la manière n’est peut-être pas très respectueuse de l’état de l’art des API socle Simplicité. Notamment, lors de GET, nous avons une erreur résiduelle dans les logs systèmes directement liée à la manière de coder l’opération dans notre service (cf. détournement de l’output stream de la réponse suivi d’un return null
).
Dans ce contexte, nous sollicitons votre support pour déterminer ce qui pourrait être mieux fait (voire moins fait dans l’hypothèse ou le socle pourrait mieux supporter ce cas d’usage).
public class legalTextServicesV1 extends RESTTranslatedObjectExternalObjectCommons {
private static final long serialVersionUID = 1L;
private static final String CONST_SLASH = "/";
private static final String CONST_ACTION_KEY = "_action";
...
@Override
public Object get(Parameters params) throws HTTPException {
List<String> parts = getURIParts();
if (parts.isEmpty()) return root();
if ("getPdf".equals(params.getParameter(CONST_ACTION_KEY)) && "contents".equals(getTokenFromLast(params.getLocation(), CONST_SLASH, 2))) {
ObjectDB obj = null;
try {
obj = borrowAPIObject(dObjects.get("contents"));
synchronized (obj.getLock()) {
String rowId = getTokenFromLast(params.getLocation(), CONST_SLASH, 1);
if (obj.select(rowId)) {
String cdisp = rlog(params.getParameter("_contentdisposition"), "get", "cdisp", null, -1);
String docId = obj.getFieldValue("LegalTextContentId.ContentDocPdf");
DocumentDB doc = DocTool.getDocument(getGrant(), docId, false, false);
String contentType = rlog(doc.getMIME(doc.getPath()), "get", "contentType", null, -1);
try (InputStream in = DocTool.readFile(doc.getPath());) {
HttpServletResponse response = ((ServletParameters)params).getResponse();
response.setHeader("Content-Type", contentType);
response.setHeader("Content-Disposition", cdisp + "; filename=" + rlog(obj.getFieldValue("LegalTextContentId.ContentLocalProductName").replaceAll(" ", "")+".pdf", "get", "filename", null, -1));
ServletOutputStream out = response.getOutputStream();
Tool.copy((InputStream)in, (OutputStream)out);
out.flush();
log("get", "Streaming done.", null, -1);
}
}
}
returnAPIObject(obj);
return null;
} catch(Exception e) {
returnAPIObject(obj);
return error(500, e.getMessage());
}
}
return super.get(params);
}
Steps to reproduce
This request concerns an up-to-date Simplicité instance
and these are the steps to reproduce it:
Technical information
Instance /health
[Platform]
Status=OK
Version=6.1.17
BuiltOn=2024-12-13 15:55
Git=6.1/aa3670b64d55ed51cf3ca7a63b904920673ae1f5
Encoding=UTF-8
EndpointIP=100.88.241.22
EndpointURL=http://lbc-77449-app-7cc8cb8f44-xbc86:8080
TimeZone=Europe/Paris
SystemDate=2024-12-17 14:41:57
Simplicité logs
2024-12-17 13:03:27,503|SIMPLICITE|ERROR||http://lbc-77449-app-7cc8cb8f44-xbc86:8080||ERROR|irn-77449_dev_pkjwt_mkv4lvgr8oor|com.simplicite.webapp.servlets.api.ExternalObjectServlet|service||Evénement: Unexpected error
com.simplicite.util.exceptions.PlatformException: Null content returned by display method
at com.simplicite.webapp.servlets.AbstractExternalObjectServlet.service(AbstractExternalObjectServlet.java:159)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:623)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:199)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:168)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:642)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:416)
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:348)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:285)
at com.simplicite.webapp.filters.RewriteFilter.doFilter(RewriteFilter.java:54)
at com.simplicite.webapp.filters.AbstractFilter.doFilter(AbstractFilter.java:49)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:168)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144)
at com.simplicite.webapp.filters.HTTPHeadersFilter.doFilter(HTTPHeadersFilter.java:39)
at com.simplicite.webapp.filters.AbstractFilter.doFilter(AbstractFilter.java:49)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:168)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:156)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:660)
at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:761)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:346)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:396)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:937)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1791)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
at java.base/java.lang.Thread.run(Thread.java:1583)
2024-12-17 13:03:27,502|SIMPLICITE|INFO||http://lbc-77449-app-7cc8cb8f44-xbc86:8080||INFO|system|com.simplicite.commons.RenaultLogger.LoggerTool|get||Event: Streaming done.
2024-12-17 13:03:27,501|SIMPLICITE|INFO||http://lbc-77449-app-7cc8cb8f44-xbc86:8080||INFO|system|com.simplicite.commons.RenaultLogger.LoggerTool|get||Event: filename r=.pdf
2024-12-17 13:03:27,420|SIMPLICITE|INFO||http://lbc-77449-app-7cc8cb8f44-xbc86:8080||INFO|system|com.simplicite.commons.RenaultLogger.LoggerTool|get||Event: contentType r=application/pdf
2024-12-17 13:03:27,415|SIMPLICITE|INFO||http://lbc-77449-app-7cc8cb8f44-xbc86:8080||INFO|system|com.simplicite.commons.RenaultLogger.LoggerTool|get||Event: cdisp r=attachment
Browser logs
ubuntu@FRLH158926:~$ clear ; curl --insecure -X GET -H 'Content-Type:application/pdf' -H 'Accept:application/pdf' -H "Authorization:Bearer $TOKEN" -H "apikey:$APIKEY" -H 'Cache-Control:no-cache' "https://$BACKEND/lbc/v1/contents/190?_action=getPdf&_contentdisposition=attachment" -OJ --verbose
Note: Unnecessary use of -X or --request, GET is already inferred.
* Uses proxy env variable https_proxy == 'http://***'
* Trying ***...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to *** (***) port *** (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to lbc-app.ext.gke2.dev.gcp.renault.com:443
* Proxy auth using Basic with user '***'
> CONNECT ***:443 HTTP/1.1
> Host: ***:443
> Proxy-Authorization: Basic ***
> User-Agent: curl/7.71.1
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established
<
* Proxy replied 200 to CONNECT request
* CONNECT phase completed!
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: none
} [5 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* CONNECT phase completed!
* CONNECT phase completed!
{ [5 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [122 bytes data]
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
{ [19 bytes data]
* TLSv1.3 (IN), TLS handshake, Certificate (11):
{ [2603 bytes data]
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
{ [264 bytes data]
* TLSv1.3 (IN), TLS handshake, Finished (20):
{ [52 bytes data]
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.3 (OUT), TLS handshake, Finished (20):
} [52 bytes data]
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=*.***
* start date: Nov 2 16:52:49 2024 GMT
* expire date: Jan 31 16:52:48 2025 GMT
* issuer: C=US; O=Let's Encrypt; CN=R10
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
} [5 bytes data]
* Using Stream ID: 1 (easy handle 0x7fffced95e20)
} [5 bytes data]
> GET /lbc/v1/contents/190?_action=getPdf&_contentdisposition=attachment HTTP/2
> Host: ***
> user-agent: curl/7.71.1
> content-type:application/pdf
> accept:application/pdf
> authorization:Bearer eyJra***O3jEA
> cache-control:no-cache
>
{ [5 bytes data]
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
{ [57 bytes data]
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
{ [57 bytes data]
* old SSL session ID is stale, removing
{ [5 bytes data]
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
} [5 bytes data]
< HTTP/2 200
< date: Tue, 17 Dec 2024 13:47:02 GMT
< content-type: application/pdf
< set-cookie: APPlbc77449SESSIONID=fd6351bfea72ea17a75a1c73a67ad644|3db3e53f894d9d3c52cb687dc16fb7a3; Path=/; Secure; HttpOnly
< traceresponse: 00-40b30773eec775dc9ff0e81bd1e11e98-97baad4a16ca1ebb-01
< x-dt-tracestate: b8e9c79f-c11c5b27@dt
< content-disposition: attachment; filename=Dacia-Digital-CGUV-MyDaciaWeb-Importer-B2CADAPTATIONv1.0.1.0CA.pdf
< strict-transport-security: max-age=31536000; includeSubDomains
<
{ [3740 bytes data]
100 638k 0 638k 0 0 2143k 0 --:--:-- --:--:-- --:--:-- 2150k
* Connection #0 to host *** left intact
ubuntu@FRLH158926:~$ ls -l Dacia-Digital-CGUV-MyDaciaWeb-Importer-B2CADAPTATIONv1.0.1.0CA.pdf
-rw-rw-r-- 1 ubuntu ubuntu 653979 Dec 17 14:47 Dacia-Digital-CGUV-MyDaciaWeb-Importer-B2CADAPTATIONv1.0.1.0CA.pdf
Other relevant information
----E.g. type of deployment, browser vendor and version, etc.----