-
Notifications
You must be signed in to change notification settings - Fork 435
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Send error response according to Backend response #31
Comments
Hi @ricmoreira Check this link for information about this behaviour: Although now you can use the |
Thank you for the quick response. |
You need to add your own custom NewHTTPProxyDetailed will allow you to add your custom status handler. Check this link for an example: |
This is what I've done now: (...)
backendFactory := func(backendCfg *config.Backend) proxy.Proxy {
ns := proxy.NoOpHTTPStatusHandler
// the default request executor
re := proxy.DefaultHTTPRequestExecutor(proxy. NewHTTPClient)
// default entity formatter for the given backend
ef := proxy.NewEntityFormatter(backendCfg)
// the default response parser with the required config
rp := proxy.DefaultHTTPResponseParserFactory(proxy.HTTPResponseParserConfig{backendCfg.Decoder, ef})
// build and return the new backend proxy
return proxy.NewHTTPProxyDetailed(backendCfg, re, ns, rp)
}
// build the pipes on top of the custom backend factory
proxyFactory := proxy.NewDefaultFactory(backendFactory, logger)
routerFactory := krakendgin.NewFactory(krakendgin.Config{
Engine: gin.Default(),
ProxyFactory: proxyFactory,
(...) I've managed to get the response body but now an error response (e.g. 400) is transformed to a 200 response. |
hi, @ricmoreira the renders are responsible for the behaviour you're describing (as you can see here: https://github.com/devopsfaith/krakend/blob/master/router/gin/render.go#L90) but you can inject your own render at the router level by registering it with your implementation should copy the on the other hand, please notice you can use the CORS module already available in the master branch. the JOSE package is not ready yet, but we expect to finish it in a couple of weeks. cheers! |
Hello, I have finally a working example: package main
import (
"flag"
"io"
"log"
"net/http"
"os"
"time"
"github.com/gin-gonic/gin"
"gopkg.in/gin-contrib/cors.v1"
"api_gateway_admin/middleware"
"github.com/devopsfaith/krakend/config"
"github.com/devopsfaith/krakend/logging"
"github.com/devopsfaith/krakend/proxy"
krakendgin "github.com/devopsfaith/krakend/router/gin"
)
func main() {
port := flag.Int("p", 0, "Port of the service")
logLevel := flag.String("l", "ERROR", "Logging level")
debug := flag.Bool("d", false, "Enable the debug")
configFile := flag.String("c", "{{path to file}}/configuration.json", "Path to the configuration filename")
flag.Parse()
parser := config.NewParser()
serviceConfig, err := parser.Parse(*configFile)
if err != nil {
log.Fatal("ERROR:", err.Error())
}
// render that does not change response
noTransformRender := func(c *gin.Context, response *proxy.Response) {
if response == nil {
c.Status(http.StatusInternalServerError)
return
}
c.Status(response.Metadata.StatusCode)
for k, v := range response.Metadata.Headers {
c.Header(k, v[0])
}
io.Copy(c.Writer, response.Io)
}
// register the render at the router level
krakendgin.RegisterRender("NoTransformRender", noTransformRender)
// assign NoTransformRender to all endpoints loaded from config file
for _, v := range serviceConfig.Endpoints {
v.OutputEncoding = "NoTransformRender"
}
serviceConfig.Debug = serviceConfig.Debug || *debug
if *port != 0 {
serviceConfig.Port = *port
}
logger, err := logging.NewLogger(*logLevel, os.Stdout, "[KRAKEND]")
if err != nil {
log.Println("ERROR:", err.Error())
return
}
backendFactory := func(backendCfg *config.Backend) proxy.Proxy {
// status handler that does change status
ns := proxy.NoOpHTTPStatusHandler
// the default request executor
re := proxy.DefaultHTTPRequestExecutor(proxy.NewHTTPClient)
// response parser that copies Backend response body to proxy Response IO reader
rp := proxy.NoOpHTTPResponseParser
// build and return the new backend proxy
return proxy.NewHTTPProxyDetailed(backendCfg, re, ns, rp)
}
// build the pipes on top of the custom backend factory
proxyFactory := proxy.NewDefaultFactory(backendFactory, logger)
engine := gin.Default()
routerConfig := krakendgin.Config{
Engine: engine,
ProxyFactory: proxyFactory,
Logger: logger,
HandlerFactory: krakendgin.EndpointHandler,
Middlewares: []gin.HandlerFunc{
cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:4200", "http://127.0.0.1:4200", "http://localhost:8089", "http://localhost:8069", "http://localhost:8080", "http://localhost:8099"},
AllowMethods: []string{"PUT", "PATCH", "POST", "GET", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Accept",
"Accept-Encoding",
"Accept-Language",
"access-control-allow-origin",
"Access-Control-Request-Headers",
"Access-Control-Request-Method",
"authorization",
"Cache-Control",
"Connection",
"Content-Type",
"Host",
"If-Modified-Since",
"Keep-Alive",
"Key",
"Origin",
"Pragma",
"User-Agent",
"X-Custom-Header"},
ExposeHeaders: []string{"Content-Length", "Content-Type"},
AllowCredentials: true,
MaxAge: 48 * time.Hour,
}),
middleware.JwtCheck(),
},
}
routerFactory := krakendgin.NewFactory(routerConfig)
routerFactory.New().Run(serviceConfig)
} Thank you very much for your quick feedback. Thanks! |
@ricmoreira that's amazing! remember you can avoid some of that code by just defining the also, you can add enjoy playing with the KrakenD!!! |
Thank you very much! I'll leave here my main and my config file for whoever needs an example. main.gopackage main
import (
"flag"
"io"
"log"
"net/http"
"os"
"github.com/gin-gonic/gin"
"api_gateway_admin/middleware"
"github.com/devopsfaith/krakend/config"
"github.com/devopsfaith/krakend/logging"
"github.com/devopsfaith/krakend/proxy"
krakendgin "github.com/devopsfaith/krakend/router/gin"
)
func main() {
port := flag.Int("p", 0, "Port of the service")
logLevel := flag.String("l", "ERROR", "Logging level")
debug := flag.Bool("d", false, "Enable the debug")
configFile := flag.String("c", "{{path to file}}/configuration.json", "Path to the configuration filename")
flag.Parse()
parser := config.NewParser()
serviceConfig, err := parser.Parse(*configFile)
if err != nil {
log.Fatal("ERROR:", err.Error())
}
// render that does not change response
noTransformRender := func(c *gin.Context, response *proxy.Response) {
if response == nil {
c.Status(http.StatusInternalServerError)
return
}
c.Status(response.Metadata.StatusCode)
for k, v := range response.Metadata.Headers {
c.Header(k, v[0])
}
io.Copy(c.Writer, response.Io)
}
// register the render at the router level
krakendgin.RegisterRender("NoTransformRender", noTransformRender)
serviceConfig.Debug = serviceConfig.Debug || *debug
if *port != 0 {
serviceConfig.Port = *port
}
logger, err := logging.NewLogger(*logLevel, os.Stdout, "[KRAKEND]")
if err != nil {
log.Println("ERROR:", err.Error())
return
}
backendFactory := func(backendCfg *config.Backend) proxy.Proxy {
// status handler that does change status
ns := proxy.NoOpHTTPStatusHandler
// the default request executor
re := proxy.DefaultHTTPRequestExecutor(proxy.NewHTTPClient)
// response parser that copies Backend response body to proxy Response IO reader
rp := proxy.NoOpHTTPResponseParser
// build and return the new backend proxy
return proxy.NewHTTPProxyDetailed(backendCfg, re, ns, rp)
}
// build the pipes on top of the custom backend factory
proxyFactory := proxy.NewDefaultFactory(backendFactory, logger)
engine := gin.Default()
routerConfig := krakendgin.Config{
Engine: engine,
ProxyFactory: proxyFactory,
Logger: logger,
HandlerFactory: krakendgin.EndpointHandler,
Middlewares: []gin.HandlerFunc{
middleware.JwtCheck(),
},
}
routerFactory := krakendgin.NewFactory(routerConfig)
routerFactory.New().Run(serviceConfig)
} configuration.json{
"version": 2,
"name": "ecommerce-service",
"port": 8080,
"cache_ttl": "1s",
"timeout": "10s",
"host": [
"http://localhost"
],
"extra_config": {
"github_com/devopsfaith/krakend-cors": {
"allow_origins": [ "http://localhost:4200", "http://127.0.0.1:4200", "http://localhost:8089", "http://localhost:8069", "http://localhost:8080", "http://localhost:8099" ],
"allow_methods": [ "PUT", "PATCH", "POST", "GET", "DELETE", "OPTIONS"],
"allow_heathers": [ "Accept-Encoding",
"Accept-Language",
"access-control-allow-origin",
"Access-Control-Request-Headers",
"Access-Control-Request-Method",
"authorization",
"Cache-Control",
"Connection",
"Content-Type",
"Host",
"If-Modified-Since",
"Keep-Alive",
"Key",
"Origin",
"Pragma",
"User-Agent",
"X-Custom-Header"],
"max_age": "48h",
"allow_credentials": true,
"expose_headers": ["Content-Length", "Content-Type"]
}
},
"endpoints": [
{
"endpoint": "/api/v1/product",
"timeout": "800ms",
"method": "POST",
"backend": [
{
"url_pattern": "/api/v1/product",
"host": [
"http://localhost:8069"
]
}
],
"output_encoding": "NoTransformRender"
},
{
"endpoint": "/api/v1/product",
"timeout": "800ms",
"method": "GET",
"querystring_params": [
"page",
"per_page"
],
"backend": [
{
"url_pattern": "/api/v1/product",
"host": [
"http://localhost:8069"
]
}
],
"output_encoding": "NoTransformRender"
},
{
"endpoint": "/api/v1/saft/upload",
"timeout": "800ms",
"method": "POST",
"headers_to_pass": [
"content-type",
"Content-Type"
],
"backend": [
{
"url_pattern": "/api/v1/saft/upload",
"host": [
"http://localhost:8099"
]
}
],
"output_encoding": "NoTransformRender"
}
]
} |
Hello,
Is it possible to use NoOpHTTPStatusHandler together with all middlewares metioned in the NewBackendFactory function? I'm a little confused about how krakend deals with middlewares listed in the krakend.json vs. middlewares "hardcoded" in the NewBackendFactory function. It is possible to use some middleware configured in the krakend.json even if it's not explicitly mentioned in the code of the NewBackendFactory function? Thank you. |
yes, it is. You can do it without touching the code by adding
The system loads all the factories required to support/enable the modules/middlewares included. Each factory looks for its configuration at some part of the config file. If it is not configured, the module/middleware is not added to the final pipe. That's why the config of a not included component is ignored. cheers |
Great, it works well. However, I have an issue with endpoint used to sign JWT (github.com/devopsfaith/krakend-jose/signer). Signed token is not returned from Krakend when using Backend "encoding": "no-op" (it returns only empty JSON). Thank you |
@dominiksimek in order to avoid polluting this issue, I'd suggest you to move the discussion to the slack |
Hello. I am using the example of ricomeira to return the original status and response of a request, which works well, but when doing a merge of three requests, it does not show any results main.gopackage main
import (
"flag"
"io"
"log"
"net/http"
"os"
limit "github.com/aviddiviner/gin-limit"
"github.com/gin-gonic/gin"
"github.com/devopsfaith/krakend/config"
"github.com/devopsfaith/krakend/logging"
"github.com/devopsfaith/krakend/proxy"
"github.com/devopsfaith/krakend/router"
krakendgin "github.com/devopsfaith/krakend/router/gin"
"github.com/devopsfaith/krakend/transport/http/client"
)
func main() {
port := flag.Int("p", 0, "Port of the service")
logLevel := flag.String("l", "ERROR", "Logging level")
debug := flag.Bool("d", false, "Enable the debug")
configFile := flag.String("c", "./krakend.json", "Path to the configuration filename")
flag.Parse()
parser := config.NewParser()
serviceConfig, err := parser.Parse(*configFile)
if err != nil {
log.Fatal("ERROR:", err.Error())
}
// render that does not change response
noTransformRender := func(c *gin.Context, response *proxy.Response) {
if response == nil {
c.Status(http.StatusInternalServerError)
return
}
c.Status(response.Metadata.StatusCode)
for k, v := range response.Metadata.Headers {
c.Header(k, v[0])
}
io.Copy(c.Writer, response.Io)
}
// register the render at the router level
krakendgin.RegisterRender("NoTransformRender", noTransformRender)
serviceConfig.Debug = serviceConfig.Debug || *debug
if *port != 0 {
serviceConfig.Port = *port
}
logger, err := logging.NewLogger(*logLevel, os.Stdout, "[KRAKEND]")
if err != nil {
log.Fatal("ERROR:", err.Error())
}
backendFactory := func(backendCfg *config.Backend) proxy.Proxy {
// status handler that does change status
ns := client.NoOpHTTPStatusHandler
// the default request executor
re := client.DefaultHTTPRequestExecutor(client.NewHTTPClient)
// response parser that copies Backend response body to proxy Response IO reader
rp := proxy.NoOpHTTPResponseParser
// build and return the new backend proxy
return proxy.NewHTTPProxyDetailed(backendCfg, re, ns, rp)
}
// build the pipes on top of the custom backend factory
proxyFactory := proxy.NewDefaultFactory(backendFactory, logger)
// store := cache.NewInMemoryStore(time.Minute)
mws := []gin.HandlerFunc{
limit.MaxAllowed(20),
}
// routerFactory := krakendgin.DefaultFactory(proxy.DefaultFactory(logger), logger)
routerFactory := krakendgin.NewFactory(krakendgin.Config{
Engine: gin.Default(),
ProxyFactory: proxyFactory,
Middlewares: mws,
Logger: logger,
HandlerFactory: krakendgin.EndpointHandler,
RunServer: router.RunServer,
})
routerFactory.New().Run(serviceConfig)
} krakend.json{
"version": 2,
"name": "kraken_test",
"port": 8000,
"cache_ttl": "3600s",
"timeout": "3000ms",
"extra_config": {
"github_com/devopsfaith/krakend-gologging": {
"level": "DEBUG",
"prefix": "[KRAKEND]",
"syslog": false,
"stdout": true
},
"github_com/devopsfaith/krakend-metrics": {
"collection_time": "60s",
"proxy_disabled": false,
"router_disabled": false,
"backend_disabled": false,
"endpoint_disabled": false,
"listen_address": ":8090"
},
"github_com/devopsfaith/krakend-cors": {
"allow_origins": [
"http://192.168.99.100:3000",
"http://localhost:8080"
],
"allow_methods": [
"POST",
"GET"
],
"allow_headers": [
"Origin",
"Authorization",
"Content-Type"
],
"expose_headers": [
"Content-Length"
],
"max_age": "12h"
}
},
"endpoints": [
{
"endpoint": "/abc",
"method": "GET",
"headers_to_pass": [
"Authorization",
"Content-Type"
],
"backend": [
{
"host": [
"http://127.0.0.1:8080"
],
"url_pattern": "/v1/test/a",
"encoding": "json"
},
{
"host": [
"http://127.0.0.1:8080"
],
"url_pattern": "/v1/test/b",
"encoding": "json"
},
{
"host": [
"http://127.0.0.1:8080"
],
"url_pattern": "/v1/test/c",
"encoding": "json"
}
]
},
{
"endpoint": "/200",
"method": "GET",
"headers_to_pass": [
"Authorization",
"Content-Type"
],
"backend": [
{
"host": [
"http://127.0.0.1:8080"
],
"url_pattern": "/v1/test/200",
"encoding": "json",
"extra_config": {
"github.com/devopsfaith/krakend-ratelimit/juju/proxy": {
"maxRate": 1,
"capacity": 1
},
"github.com/devopsfaith/krakend-circuitbreaker/gobreaker": {
"interval": 60,
"timeout": 10,
"maxErrors": 1
}
}
}
],
"output_encoding": "NoTransformRender"
},
{
"endpoint": "/201",
"method": "GET",
"headers_to_pass": [
"Authorization",
"Content-Type"
],
"backend": [
{
"host": [
"http://127.0.0.1:8080"
],
"url_pattern": "/v1/test/201",
"encoding": "json",
"extra_config": {
"github.com/devopsfaith/krakend-ratelimit/juju/proxy": {
"maxRate": 1,
"capacity": 1
},
"github.com/devopsfaith/krakend-circuitbreaker/gobreaker": {
"interval": 60,
"timeout": 10,
"maxErrors": 1
}
}
}
],
"output_encoding": "NoTransformRender"
},
{
"endpoint": "/400",
"method": "GET",
"headers_to_pass": [
"Authorization",
"Content-Type"
],
"backend": [
{
"host": [
"http://127.0.0.1:8080"
],
"url_pattern": "/v1/test/400",
"encoding": "json",
"extra_config": {
"github.com/devopsfaith/krakend-ratelimit/juju/proxy": {
"maxRate": 1,
"capacity": 1
},
"github.com/devopsfaith/krakend-circuitbreaker/gobreaker": {
"interval": 60,
"timeout": 10,
"maxErrors": 1
}
}
}
],
"output_encoding": "NoTransformRender"
},
{
"endpoint": "/401",
"method": "GET",
"headers_to_pass": [
"Authorization",
"Content-Type"
],
"backend": [
{
"host": [
"http://127.0.0.1:8080"
],
"url_pattern": "/v1/test/401",
"encoding": "json",
"extra_config": {
"github.com/devopsfaith/krakend-ratelimit/juju/proxy": {
"maxRate": 1,
"capacity": 1
},
"github.com/devopsfaith/krakend-circuitbreaker/gobreaker": {
"interval": 60,
"timeout": 10,
"maxErrors": 1
}
}
}
],
"output_encoding": "NoTransformRender"
},
{
"endpoint": "/404",
"method": "GET",
"headers_to_pass": [
"Authorization",
"Content-Type"
],
"backend": [
{
"host": [
"http://127.0.0.1:8080"
],
"url_pattern": "/v1/test/404",
"encoding": "json",
"extra_config": {
"github.com/devopsfaith/krakend-ratelimit/juju/proxy": {
"maxRate": 1,
"capacity": 1
},
"github.com/devopsfaith/krakend-circuitbreaker/gobreaker": {
"interval": 60,
"timeout": 10,
"maxErrors": 1
}
}
}
],
"output_encoding": "NoTransformRender"
},
{
"endpoint": "/500",
"method": "GET",
"headers_to_pass": [
"Authorization",
"Content-Type"
],
"backend": [
{
"host": [
"http://127.0.0.1:8080"
],
"url_pattern": "/v1/test/500",
"encoding": "json",
"extra_config": {
"github.com/devopsfaith/krakend-ratelimit/juju/proxy": {
"maxRate": 1,
"capacity": 1
},
"github.com/devopsfaith/krakend-circuitbreaker/gobreaker": {
"interval": 60,
"timeout": 10,
"maxErrors": 1
}
}
}
],
"output_encoding": "NoTransformRender"
}
]
} This is my test server where I generate the APIs : webnode Could you please help me? |
@nadim500, notice the pipe of that example has two important customizations:
These two details are very important because they make the pipe behave like a |
It's the same for me. I can't get response body from backend when my backend return invalid password but krakend returns response 400 with empty response body. |
This issue was marked as resolved a long time ago and now has been automatically locked as there has not been any recent activity after it. You can still open a new issue and reference this link. |
Hello,
I'm finding difficult to get an working example for getting error responses according to backend response and not the default 500 error response.
I've implemented my Gateway according to your Gin example and, at the moment, it is like this:
Could you please help me out on what should I change here?
Thank you.
The text was updated successfully, but these errors were encountered: