apache / apisix

The Cloud-Native API Gateway
https://apisix.apache.org/blog/
Apache License 2.0
14.35k stars 2.5k forks source link

help request: Change response status depending on response body #10915

Open yehorkovalchuk777 opened 7 months ago

yehorkovalchuk777 commented 7 months ago

Description

I need to validate whether a request or response is valid JSON or XML. I'm trying to solve this with a custom plugin.

In the case of the request, everything is fine, I enter the rewrite phase and check the request body, if it is not valid, I set response status to 400

However, in the case of a response, we can only read the response body in the body_filter phase, but in this phase we cannot change the response status. In this phase we can only change the response body

How can I change the response status in Apisix (using a custom plugin, or another method) depending on the response body?

Environment

shreemaan-abhishek commented 7 months ago

I had tried this earlier but wasn't successful. Technically this should work in set_by_lua feature. But APISIX doesn't support set_by_lua yet. This could be by design.

illidan33 commented 7 months ago

You can change the status of response in the header_filter phase. like this: ngx.header.content_length = #your body's length ngx.header.content_encoding = nil ngx.status = 400 header_filter must work with body_filter when you want to modify both header and body. It worked for me.

yehorkovalchuk777 commented 7 months ago

You can change the status of response in the header_filter phase. like this: ngx.header.content_length = #your body's length ngx.header.content_encoding = nil ngx.status = 400 header_filter must work with body_filter when you want to modify both header and body. It worked for me.

I can change the response status only in the header_filter phase, but I want to change the status only when the response body is invalid. The following situation arises: In the header_filter phase, we can change the status of the response, but we cannot read and validate the response body and set the status based on the validation. In the body_filter phase, we can read and validate the response body, but we cannot change the response status.

illidan33 commented 7 months ago

You can change the status of response in the header_filter phase. like this: ngx.header.content_length = #your body's length ngx.header.content_encoding = nil ngx.status = 400 header_filter must work with body_filter when you want to modify both header and body. It worked for me.

I can change the response status only in the header_filter phase, but I want to change the status only when the response body is invalid. The following situation arises: In the header_filter phase, we can change the status of the response, but we cannot read and validate the response body and set the status based on the validation. In the body_filter phase, we can read and validate the response body, but we cannot change the response status.

function _M.access(conf, ctx)
    local body = ngx.req.get_body_data()
    core.log.info("example2 access phase, get_body_data: ", body)
    if string.find(body, "test") then
        core.log.info("example2 access phase, set filter_flag true")
        ctx.filter_flag = true
    else
        ctx.filter_flag = false
    end
end

function _M.header_filter(conf, ctx)
    core.log.info("example2 header_filter phase, ctx filter_flag: ", ctx.filter_flag)
    if ctx.filter_flag then
        ngx.status = 401
    end
end

Part of the log after I run it is here:

2024/02/06 22:08:06 [info] 75#75: 610 [lua] example-plugin2.lua:63: phase_func(): example2 access phase, get_body_data: {"test":"test"} 2024/02/06 22:08:06 [info] 75#75: 610 [lua] example-plugin2.lua:65: phase_func(): example2 access phase, set filter_flag true 2024/02/06 22:08:06 [info] 75#75: *610 [lua] example-plugin2.lua:73: phase_func(): example2 header_filter phase, ctx filter_flag: true while reading response header from upstream

curl log:

curl -sL -w "http_code:%{http_code} content_type:%{content_type}" -o /dev/null --request POST 'http://xxxxxxx' -d '{"test":"test"}' http_code:401 content_type:application/json; charset=utf-8%

In the access phase, the body is read and the flag is set. In the header_filter phase, the response status can be set according to the flag. Does this work for you?

yehorkovalchuk777 commented 7 months ago

function _M.access(conf, ctx) local body = ngx.req.get_body_data()

Here you are fetching the request body in the access phase which is related to the request (not the response). I am trying to return a 500 status if the response body is invalid

illidan33 commented 7 months ago

function _M.access(conf, ctx) local body = ngx.req.get_body_data()

Here you are fetching the request body in the access phase which is related to the request (not the response). I am trying to return a 500 status if the response body is invalid

yes, you are right. Please ignore this reply.

hanqingwu commented 7 months ago

https://apisix.apache.org/docs/apisix/plugins/response-rewrite/ Does it works ?

kayx23 commented 1 month ago

I checked the response-rewrite for a different use case where I would like to conditionally change the response body based on the actual response body from the upstream service. Correct me if I am wrong but I don't think this is possible.

That said, is there any other outstanding question in this thread?