krakend / krakend-ce

KrakenD Community Edition: High-performance, stateless, declarative, API Gateway written in Go.
https://www.krakend.io
Apache License 2.0
1.93k stars 453 forks source link

Send error response according to Backend response #31

Closed ricmoreira closed 6 years ago

ricmoreira commented 6 years ago

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.

taik0 commented 6 years ago

Hi @ricmoreira

Check this link for information about this behaviour: http://www.krakend.io/docs/faq/#i-am-getting-a-500-status-when-the-backend-returns-anything-but-200-201-or-redirects.

Although now you can use the NoOpHTTPStatusHandler to send the same response of the backend.

ricmoreira commented 6 years ago

Thank you for the quick response. I've been through that documentation and I've understood that I've to inject my "own HTTPStatusHandler". In this case, as you said, the "NoOpHTTPStatusHandler" would solve my issue. But where do I put it?

taik0 commented 6 years ago

You need to add your own custom BackendFactory if you are not using the default Factories.

NewHTTPProxyDetailed will allow you to add your custom status handler.

Check this link for an example: https://github.com/devopsfaith/krakend/issues/102#issuecomment-373657911

ricmoreira commented 6 years ago

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.

kpacha commented 6 years ago

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 gin.RegisterRender and adding its name in the endpoint configuration (config.EndpointConfig.OutputEncoding).

your implementation should copy the proxy.Response.Metadata.StatusCode like in https://github.com/devopsfaith/krakend/blob/master/router/gin/render.go#L124

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!

ricmoreira commented 6 years ago

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!

kpacha commented 6 years ago

@ricmoreira that's amazing!

remember you can avoid some of that code by just defining the extra_config for the CORS module like in this example: https://github.com/devopsfaith/krakend-cors#configuration-example

also, you can add "output_encoding": "NoTransformRender" to each endpoint definition in the same config file, so you'll have more control.

enjoy playing with the KrakenD!!!

ricmoreira commented 6 years ago

Thank you very much! Much better now.

I'll leave here my main and my config file for whoever needs an example.

main.go

package 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"
    }
  ]
}
dominiksimek commented 5 years ago

Hello, I'm trying to modify default krakend-ce backendFactory to make it works with NoOpHTTPStatusHandler. Original backendFactory code is (_backendfactory.go):

// NewBackendFactory creates a BackendFactory by stacking all the available middlewares:
// - oauth2 client credentials
// - martian
// - rate-limit
// - circuit breaker
// - metrics collector
// - opencensus collector
func NewBackendFactory(logger logging.Logger, metricCollector *metrics.Metrics) proxy.BackendFactory {
    requestExecutorFactory := func(cfg *config.Backend) client.HTTPRequestExecutor {
        var clientFactory client.HTTPClientFactory
        if _, ok := cfg.ExtraConfig[oauth2client.Namespace]; ok {
            clientFactory = oauth2client.NewHTTPClient(cfg)
        } else {
            clientFactory = httpcache.NewHTTPClient(cfg)
        }
        return opencensus.HTTPRequestExecutor(clientFactory)
    }
    backendFactory := martian.NewConfiguredBackendFactory(logger, requestExecutorFactory)
    backendFactory = juju.BackendFactory(backendFactory)
    backendFactory = cb.BackendFactory(backendFactory)
    backendFactory = metricCollector.BackendFactory("backend", backendFactory)
    backendFactory = opencensus.BackendFactory(backendFactory)
    return backendFactory
}

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.

kpacha commented 5 years ago

hi @dominiksimek

Is it possible to use NoOpHTTPStatusHandler together with all middlewares metioned in the NewBackendFactory function?

yes, it is. You can do it without touching the code by adding "output_encoding": "no-op" at the endpoint config and "encoding": "no-op" at the backend

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?

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

dominiksimek commented 5 years ago

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). On the other hand, response from Krakend contains valid json with signed token when using Backend "encoding": "json". But StatusCode from Krakend response can be only 200 or 400. In the case of unsuccessful user authentication (e.g. wrong password), my backend sends 401 with additional JSON in the response body. Is it possible to proxy such response from backend through Krakend?

Thank you

kpacha commented 5 years ago

@dominiksimek in order to avoid polluting this issue, I'd suggest you to move the discussion to the slack

nadim500 commented 5 years ago

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.

kpacha commented 5 years ago

@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 no-op pipe, so no merging is supported because merging requires the data from the backends to be already decoded and it ignores the Response.Io.

muhammadluth commented 2 years ago

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). On the other hand, response from Krakend contains valid json with signed token when using Backend "encoding": "json". But StatusCode from Krakend response can be only 200 or 400. In the case of unsuccessful user authentication (e.g. wrong password), my backend sends 401 with additional JSON in the response body. Is it possible to proxy such response from backend through Krakend?

Thank you

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. Can anyone help me ?

github-actions[bot] commented 2 years ago

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.