AWS API Gateway - 集成响应正文映射

Posted

技术标签:

【中文标题】AWS API Gateway - 集成响应正文映射【英文标题】:AWS API Gateway - Integration Response body mapping 【发布时间】:2018-05-02 09:35:33 【问题描述】:

在 AWS API Gateway 集成响应正文映射中,我有以下代码:

#set($inputRoot = $input.path('$.Item'))
[
#foreach($elem in $inputRoot.Events)
 
  "id": $elem.id,
  "from" : $elem.from,
  "to" : $elem.to,
  "spent" : $elem.spent,
  #if("$!elem.comment" != "")
    "comment": $elem.comment,    
  #end
  "project" : 
    "id" : $elem.project.id,
    "number" : $elem.project.number,
    "name" : $elem.project.name
  
  
#if($foreach.hasNext),#end
#end
]

数据来自查询 DynamoDB 表的 lambda 函数

API 网关返回数据如下:

[
 
  "id": 123443214,
  "from" : 19:34,
  "to" : 22:30,
  "spent" : 02:56,
    "project" : 
    "id" : 4321,
    "number" : CIB,
    "name" : Backend
  
  
, 
  "id": 12341234,
  "from" : 19:34,
  "to" : 22:30,
  "spent" : 02:56,
    "project" : 
    "id" : 12341234,
    "number" : CIB,
    "name" : Backend
  
  
]

所以它已经格式化了。如何让 APi Gateway 返回未格式化的响应?所以它只是纯 json,没有折线、缩进等?

提前致谢!

【问题讨论】:

【参考方案1】:

(简短的初步说明:您在 JSON 字符串值周围缺少一些引号)。

可以使用## 删除换行符,使用#**# 删除缩进,如下所示,但模板看起来有点难看:

#set($inputRoot = $input.path('$.Item'))##
[##
#foreach($elem in $inputRoot.Events)##
##
#**#"id":$elem.id,##
#**#"from": $elem.from,##
#**#"to":$elem.to,##
#**#"spent":$elem.spent,##
#if("$!elem.comment" != "")##
#*  *#"comment":$elem.comment,##
#end##
#**#"project":##
#**#"id":$elem.project.id,##
#**#"number":"$elem.project.number",##
#**#"name":"$elem.project.name"##
##
##
#if($foreach.hasNext),#end##
#end##
]##

由于缩进一开始在这里的唯一原因是模板的可读性,我会转向另一个方向。

例如,您可以使用org.json 在您的 View servlet 中添加一个后处理整洁的格式化程序:

import org.json.JSONObject;
  ....
Writer writer = new StringWriter();
getVelocityView().merge(template, context, writer);
String compactJSON = new JSONObject(writer.toString()).toString();
response.getWriter().write(compactJSON);

但是这仅适用于小型 JSON 文件,因为响应被缓冲到内存中,所以让我们继续寻找更优雅的解决方案。

要走的路是预处理您的模板,使用自定义 ResouceLoader。

CompactJSONResourceLoader.java

package my.custom.loader;

import java.io.InputStream;
import java.io.IOException;
import org.apache.commons.collections.ExtendedProperties;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.resource.Resource;
import org.apache.velocity.runtime.resource.loader.ResourceLoader;
import org.apache.velocity.runtime.resource.loader.ResourceLoaderFactory;

public class CompactJSONResourceLoader extends ResourceLoader

    protected ResourceLoader innerLoader = null;

    @Override
    public void init(ExtendedProperties configuration)
    
        try
        
            String innerLoaderID = configuration.getString("innerLoader") + ".resource.loader";
            String innerLoaderClass = rsvc.getConfiguration().getString(innerLoaderID + ".class");
            innerLoader = ResourceLoaderFactory.getLoader(rsvc, innerLoaderClass);
            ExtendedProperties innerConfiguration = rsvc.getConfiguration().subset(innerLoaderID);
            innerLoader.commonInit(rsvc, innerConfiguration);
            innerLoader.init(innerConfiguration);
        
        catch (Exception e)
        
            log.error("could not initialize CompactJSONResourceLoader inner loader", e);
        
    

    protected class CompactJSONInputStream extends InputStream
    
        InputStream innerStream = null;
        boolean insideQuotes = false;

        public CompactJSONInputStream(InputStream innerStream)
        
            this.innerStream = innerStream;
        

        @Override
        public int read() throws IOException
        
            int ch;
            do
            
                ch = innerStream.read();
                if (insideQuotes)
                
                    if (ch == '"') insideQuotes = false;
                    break;
                
                else if (!Character.isWhitespace(ch))
                
                    if (ch == '"') insideQuotes = true;
                    break;
                
            
            while (ch != -1);
            return ch;
        
    

    @Override
    public InputStream getResourceStream(String source) throws ResourceNotFoundException
    
        return new CompactJSONInputStream(innerLoader.getResourceStream(source));
    

    @Override
    public boolean isSourceModified(Resource resource)
    
        return innerLoader.isSourceModified(resource);
    

    @Override
    public long getLastModified(Resource resource)
    
        return innerLoader.getLastModified(resource);
    

然后您需要使用以下属性配置 Velocity:

resource.loader = compact
compact.resource.loader.class = my.custom.loader.CompactJSONResourceLoader

compact.resource.loader.innerLoader = 文件

(当然,您可以将file 替换为您当前使用的资源加载器)。

【讨论】:

我要补充一点,建议的自定义 ResourceLoader 用于 Velocity 1.7 API。如果您使用的是 Velocity 2.0,则需要调整源代码,使其直接与 Readers 一起工作,而不是 InputStreams。 感谢您非常详细的回答!我使用 NodeJs,所以这个 ResourceLoader 可能不可用。但是由于我的 JSON 字符串值周围缺少引号,它工作得很好......我自己真是个愚蠢的错误 这个问题可能应该被标记为velocity.js,而不是velocity。但无论如何,这是一个有趣的回答,我很高兴,这是我第一次接受 velocity.js 速度的回答! 如何配置 AWS API Gateway 以使用它?不使用 Lambda 代理集成。 @Paya 那你就被丑陋的 cmets 解决方案困住了。

以上是关于AWS API Gateway - 集成响应正文映射的主要内容,如果未能解决你的问题,请参考以下文章

AWS API Gateway 集成响应

AWS Lambda 和 API Gateway 响应集成问题

AWS API Gateway - Lambda 代理(集成请求) - 内部服务器错误

使用 AWS API Gateway 和 Java 处理错误响应状态代码/实体

将 AWS Lambda 429 错误映射到 API Gateway 2XX 响应

HTTP 请求正文未通过 AWS API Gateway 访问 AWS lambda 函数