krakend-ce: Send error response according to Backend response

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:

package main

import (
	"flag"
	"log"
	"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())
	}
	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
	}

	routerFactory := krakendgin.NewFactory(krakendgin.Config{
		Engine:         gin.Default(),
		ProxyFactory:   customProxyFactory{logger, proxy.DefaultFactory(logger)},
		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.New().Run(serviceConfig)
}

// customProxyFactory adds a logging middleware wrapping the internal factory
type customProxyFactory struct {
	logger  logging.Logger
	factory proxy.Factory
}

// New implements the Factory interface
func (cf customProxyFactory) New(cfg *config.EndpointConfig) (p proxy.Proxy, err error) {
	p, err = cf.factory.New(cfg)
	if err == nil {
		p = proxy.NewLoggingMiddleware(cf.logger, cfg.Endpoint)(p)
	}
	return
}

Could you please help me out on what should I change here? Thank you.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 16 (7 by maintainers)

Most upvoted comments

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

image

main.go

package 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? Thank you.

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. Congrats for your great project.

Thanks!