loentar / ngrest

Fast and easy C++ RESTful WebServices framework
Apache License 2.0
464 stars 93 forks source link

Supporting raw json string in HTTP requests #79

Closed raadiy closed 2 years ago

raadiy commented 2 years ago

Hi,

Thank you for supporting the HTTP PATCH. It is very helpful. I have another request. Would it be possible for the NGREST HTTP methods to pass on the raw json it received, and let the application parse it. I find that you currently provide the parsed output. That is splendid. My request is in addition to what you provide, a seperate set of handlers, through which the application can access the raw Json. I have existing code that I would like to invoke from the ngrest handler. That way ngrest handler would just get a std::string with the json payload, and from the handler I would call the appropriate (pre-existing) function to parse and use it.

Regards Raajesh

loentar commented 2 years ago

Hi, due to restricted architecture it coulnd't implemented easily ATM. But you can hack it by using a custom filter and MessageContext.

First you should bypass body parsing by the engine. You should save it somewhere and nullify it.

Create a filter which creates a new Header and place content of body to it. Set request body to nullptr:

        response->headers = pool->alloc<Header>("X-RealBody", request->body, response->headers);
        request->body = nullptr;

https://github.com/loentar/ngrest/blob/master/tests/filters/src/TestFilterGroup.cpp#L111

Next create a service which takes only one parameter MessageContext and take the actual body from the header created in filter.

https://github.com/loentar/ngrest/blob/master/services/favicon/src/Favicon.cpp#L30

references:

https://github.com/loentar/ngrest/blob/master/core/common/src/Message.h#L59

https://github.com/loentar/ngrest/wiki/Filters-reference

raadiy commented 2 years ago

Thank you. Will try this. Regards Raajesh

raadiy commented 2 years ago

Hi Dmitry,

Thank you for the help. I created a test filter and test filter group as plugin and am able to see the data inside the preDispatch filter. However, inside the service, I am not able to access the data. I think I am doing something incorrect most likely inside the service. Could you please advise.

==== Following is the code snippet inside the preDispatch filter ====

if (context->request->body) {
        printf("\n called predispatch <%s>\n", context->request->body);
        context->response->headers = context->pool->alloc<Header>("X-RealBody",
                                                              context->request->body,
                                                              context->response->headers);
        context->request->body = nullptr;
    }

====

=== Following is the code snippet inside the HTTP PATCH helloworld service === std::string HelloWorld::patchHandler(ngrest::MessageContext &context) { char resp[1024]; ngrest::HttpResponse response = static_cast<ngrest::HttpResponse>(context.response); ngrest::Header* headerContentType = context.pool->alloc("Content-Type", "X-RealBody"); response->headers = headerContentType;

context.response->poolBody->putData(resp, 10);

printf("\npatch data <%s> <%s>\n", resp, context.request->body);

====

Regards Raajesh

loentar commented 2 years ago

your service operation should contain something like this:

    ngrest::Header *headerBody = context.request->getHeader("X-RealBody");
    if (headerBody != nullptr) {
        const char* body = headerBody->value;
        uint64_t bodySize = context.request->bodySize;
        // use body and bodySize here
    }
raadiy commented 2 years ago

Thank you, will try this

raadiy commented 2 years ago

Somehow with the code that you listed inside the service, the headerBody is always NULL, even though I am able to see it via the print in the filter.

loentar commented 2 years ago

because you should manipulate request headers within filter, not response. I did a typo :)

        context->request->headers = context->pool->alloc<Header>("X-RealBody",
                                                              context->request->body,
                                                              context->request->headers);
raadiy commented 2 years ago

I think the test filter has to be like this: if (context->request->body) {

         context->request->headers = context->pool->alloc<Header>("X-RealBody",
190                                                                   context->request->body,
191                                                                   context->request->headers);
192             context->request->body = nullptr;
193         }
raadiy commented 2 years ago

yes... i got it... thank you...

raadiy commented 2 years ago

I have to do something like this for the response too, so that the service does not automatically send a json like this... "{ "result":...}".. I think I will have to do similar thing for response for the "PostDispatch" Filter.... correct?

Also - thank you once again for the help and immediate response. Best Regards Raajesh

loentar commented 2 years ago

I think it's enough to just write to response's poolBody, like Favicon service do:

https://github.com/loentar/ngrest/blob/master/services/favicon/src/Favicon.cpp#L40

raadiy commented 2 years ago

okay, thank you

raadiy commented 2 years ago

yes, it works.. thank you for resolving these issues