moqui / moqui-framework

Use Moqui Framework to build enterprise applications based on Java. It includes tools for databases (relational, graph, document), local and web services, web and other UI with screens and forms, security, file/resource access, scripts, templates, l10n, caching, logging, search, rules, workflow, multi-instance, and integration.
http://www.moqui.org
Other
279 stars 199 forks source link

Issues when using sendJsonResponse in service for rest call #610

Closed lightning-pro closed 1 year ago

lightning-pro commented 1 year ago

I am using sendJsonResponse in service for a rest call like belows

And for the rest call, handleServiceRestCall would call the sendJsonResponse again to send the response back to api caller. normally the sendJsonResponse in handleServiceRestCall would be an empty response string like {}, and this empty {} normally won't be send back to api caller. BUT sometimes(I think it is related to the number of the response data), handleServiceRestCall's sendJsonResponse will send the {} appending to the end of the response string in service ec.web.sendJsonResponse(). for my service above, when calling the api by setting the masterEntitiesOnly to true, api will send the right response back to api caller(about 130 rows in result). but when calling it by setting the masterEntitiesOnly to false, there are about 850 rows in my system, and the response back to the api will be the 850 [] list data and an empty {} in the end after the []. this response is a bad json response string for caller.

jonesde commented 1 year ago

What are the steps to reproduce starting with stock moqui-framework? In order to research and fix issues this is the necessary first step.

lightning-pro commented 1 year ago

Here is the steps for reproduce the issue I mentioned

  1. Make sure clone the mantle related components to the project so that we will have lots of entities
  2. In the Example component, at the end of the ExampleServices.xml, add the below service `
    <actions>
        <script>
            List result = ec.entity.getAllEntitiesInfo(orderByField, filterRegexp, masterEntitiesOnly, excludeViewEntities)
            ec.web.sendJsonResponse(result)
        </script>
    </actions>

    `

  1. In the example.rest.xml, add the below resource to the end of file `

    `

  2. Open the http://localhost:8081/qapps/tools/dashboard(use your own port) and click the Example REST API(29) button to open the Swagger UI to test the api (http://localhost:8081/toolstatic/lib/swagger-ui/index.html?url=http://localhost:8081/rest/service.swagger/example#/entities/get_entities)

  3. Select the masterEntitiesOnly with true value, and select the excludeViewEntities with false value; The request url will be http://localhost:8081/rest/s1/example/entities?masterEntitiesOnly=true&excludeViewEntities=false; and the Response body will be a good response as [ ... , { "entityName": "SystemMessageRemote", "package": "moqui.service.message", "isView": "false", "fullEntityName": "moqui.service.message.SystemMessageRemote", "tableName": "SYSTEM_MESSAGE_REMOTE" } ]

  4. Select the masterEntitiesOnly with false value, and select the excludeViewEntities with false value; The request url will be http://localhost:8081/rest/s1/example/entities?masterEntitiesOnly=false&excludeViewEntities=false and the Response body will be a bad response as can't parse JSON. Raw result: [ ... , { "entityName" : "TestNoSqlEntity", "package" : "moqui.test", "isView" : "false", "fullEntityName" : "moqui.test.TestNoSqlEntity", "tableName" : "TEST_NO_SQL_ENTITY" } ]{ }

As I debug the framework code ,I can see that the sendJsonResponse in WebFacadeImpl.groovy will be executed two times because of the service use ec.web.sendJsonResponse(result) to call it first and the handleServiceRestCall in WebFacadeImpl.groovy will call it again after the RestApi.RestResult restResult = eci.serviceFacade.restApi.run(extraPathNameList, eci); and most of the time, the second call won't send the {} back to the client, but it happened when setting masterEntitiesOnly=false to have all the entities definition data response.

jonesde commented 1 year ago

The important details here are that you have a service that calls ec.web.sendJsonResponse() and is called through the Service REST interface. It is doing exactly what you are telling it do.

In general, you shouldn't use any ec.web methods in a service because ec.web is in the UI layer and services are in the logic layer. While this is not enforced, the architecture pattern is that the UI layer uses the logic layer, but the logic layer should not use the UI layer. UI details should be handled at the UI layer, like in a screen transition or something and not in a service call.

To make this work properly you can just remove the ec.web call from the service and continue calling it through the Service REST interface, or you could call it through a screen transition and call the ec.web method in the screen transition instead of inside the service. Either way, you wouldn't call it in the service.

lightning-pro commented 1 year ago

Thank you for your explanation. and it make sense not to use ec.web in service. BTW In the manle-usl ProjectServices.xml get#ProjectParties, there are ec.web.sendJsonResponse. as the response data is not big, it works fine