首页 > 解决方案 > Boto3 SES 抛出 TypeError: sequence item 0: expected str instance, tuple found

问题描述

我正在尝试将亚马逊简单电子邮件服务集成到我的应用程序中。我的应用程序是一个 python3 烧瓶应用程序。

我正在使用boto3。我目前在尝试发送原始电子邮件时遇到错误。

我创建了一个单独的路由,我在其中调用此类并获取发送“SendEmail”方法。

我有一个示例数据 json,必须在评论中的 SendEmail 中传递

我也提供了下面的错误日志

class SES():
    def __init__(self, **kwargs):
        """Create template emails that can be used in the code.
        The template emails will be stored in the brokerportal
        will then get used by the aws ses boto3 client
        When sending template emails:
        1. Check if the template is part of the listed templates
        2. Call the necessary template to build the message
        3. Send it to the parties using send email """
        self.region = 'ap-south-1'
        self.aws_access_key_id = current_app.config['ses_access_key_id'],  
        self.aws_secret_access_key = current_app.config['ses_secret_access_key']
        self.ses = boto3.client('ses', region_name = self.region,
            aws_access_key_id = self.aws_access_key_id, 
            aws_secret_access_key = self.aws_secret_access_key)    

    def SendEmail(self, data, **kwargs):
        """takes the data and transforms the data into the appropriate frmat
        before sending the email
        data should have the following:
        data = {
            'toList':['s@domain.com'],
            'ccList':['b@domain.com'],
            'bccList':['p@domain.com'],
            'TemplateData':{vars},
            'Attachment':True,
            'fileKey':'<S3path>',
            'bucketName': <Amazon s3 bucket name>
            'Subject': 'This is a subject',
            'TemplateName':'TemplateName'
        }"""
        CHARSET = "utf-8"
        Destination = {
            'ToAddresses':data['toList'],
            'CcAddresses':data['ccList'],
            'BccAddresses':data['bccList']
        }
        # ReplyToAddresses = [current_app.config['MAIL_USERNAME']],
        ReplyToAddresses = ["a@domain.com"],
        msg = MIMEMultipart('mixed')
        msg['Subject'] = data['Subject']
        msg['From'] = "a@domain.com"
        if Destination['ToAddresses']:
            msg['To'] = ', '.join(str(v) for v in Destination['ToAddresses'])
        if Destination['CcAddresses']:
            msg['Cc'] = ', '.join(str(v) for v in Destination['CcAddresses'])
        if Destination['BccAddresses']:
            msg['Bcc'] = ', '.join(str(v) for v in Destination['BccAddresses'])
        msg_body = MIMEMultipart('alternative')
        #Obtain the template details
        # v = current_app.config['db'].view('_design/Email/_view/templates')
        v = current_app.config['db'].view('_design/Email/_view/templates')
        for row in v[data['TemplateName']]:
            Tmplte = Jinja2Template(row.value['HtmlPart'])
        HtmlPart = Tmplte.render(data = data['TemplateData'])
        htmlpart = MIMEText(HtmlPart)
        msg_body.attach(htmlpart)
        msg.attach(msg_body)
        #Attachment
        if data['Attachment']:
            filehelper = FileHelper(bucketName=data['bucketName'])
            att = MIMEApplication(filehelper.load(data['fileKey']))
            att.add_header('Content-Disposition','attachment',filename=fileKey)
            msg.attach(att)
        else:
            pass
        try:
            current_app.logger.debug(msg)
            print(msg)
            response = self.ses.send_raw_email(
                RawMessage = {
                    'Data': msg.as_string()
                })
            return msg
        except ClientError as e:
            current_app.logger.debug(e.response['Error']['Message'])
            return ""

错误:

  File "/usr/local/lib/python3.6/dist-packages/flask/app.py", line 1997, in __call__
    return self.wsgi_app(environ, start_response)   File "/usr/local/lib/python3.6/dist-packages/flask/app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)   File "/usr/local/lib/python3.6/dist-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))   File "/usr/local/lib/python3.6/dist-packages/flask/app.py", line 1540, in handle_exception
    reraise(exc_type, exc_value, tb)   File "/usr/local/lib/python3.6/dist-packages/flask/_compat.py", line 33, in reraise
    raise value   File "/usr/local/lib/python3.6/dist-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()   File "/usr/local/lib/python3.6/dist-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)   File "/usr/local/lib/python3.6/dist-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))   File "/usr/local/lib/python3.6/dist-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)   File "/usr/local/lib/python3.6/dist-packages/flask/_compat.py", line 33, in reraise
    raise value   File "/usr/local/lib/python3.6/dist-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()   File "/usr/local/lib/python3.6/dist-packages/flask/app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)   File "/home/sunil/perilwise/brokerportalbe/main.py", line 522, in sesmailcheck
    response = ses.SendEmail(data = data)   File "/home/sunil/perilwise/brokerportalbe/repository/sesRepository.py", line 117, in SendEmail
    'Data': msg.as_string()   File "/usr/local/lib/python3.6/dist-packages/botocore/client.py", line 312, in _api_call
    return self._make_api_call(operation_name, kwargs)   File "/usr/local/lib/python3.6/dist-packages/botocore/client.py", line 592, in _make_api_call
    operation_model, request_dict)   File "/usr/local/lib/python3.6/dist-packages/botocore/endpoint.py", line 141, in make_request
    return self._send_request(request_dict, operation_model)   File "/usr/local/lib/python3.6/dist-packages/botocore/endpoint.py", line 166, in _send_request
    request = self.create_request(request_dict, operation_model)   File "/usr/local/lib/python3.6/dist-packages/botocore/endpoint.py", line 150, in create_request
    operation_name=operation_model.name)   File "/usr/local/lib/python3.6/dist-packages/botocore/hooks.py", line 227, in emit
    return self._emit(event_name, kwargs)   File "/usr/local/lib/python3.6/dist-packages/botocore/hooks.py", line 210, in _emit
    response = handler(**kwargs)   File "/usr/local/lib/python3.6/dist-packages/botocore/signers.py", line 90, in handler
    return self.sign(operation_name, request)   File "/usr/local/lib/python3.6/dist-packages/botocore/signers.py", line 154, in sign
    auth.add_auth(request)   File "/usr/local/lib/python3.6/dist-packages/botocore/auth.py", line 366, in add_auth
    self._inject_signature_to_request(request, signature)   File "/usr/local/lib/python3.6/dist-packages/botocore/auth.py", line 369, in _inject_signature_to_request
    l = ['AWS4-HMAC-SHA256 Credential=%s' % self.scope(request)]   File "/usr/local/lib/python3.6/dist-packages/botocore/auth.py", line 319, in scope
    return '/'.join(scope) TypeError: sequence item 0: expected str instance, tuple found

标签: amazon-web-servicesflaskamazon-ses

解决方案


我通过使 SES() 像一个可以从 main.py 调用的模块来解决这个问题(我给出了下面的代码):

class SES(object):
def __init__(self, app = None):
    """Create template emails that can be used in the code.
    The template emails will be stored in the brokerportal
    will then get used by the aws ses boto3 client
    When sending template emails:
    1. Check if the template is part of the listed templates
    2. Call the necessary template to build the message
    3. Send it to the parties using send email """
    if app is not None:
        self.init_app(app)
def init_app(self, app):
    self.region = app.config.get('ap-south-1')
    self.aws_access_key_id = app.config.get('ses_access_key_id')  
    self.aws_secret_access_key = app.config.get('ses_secret_access_key')
    self.ses = self._connect()

def _connect():
    clt = boto3.client('ses', region_name = self.region,
        aws_access_key_id = self.aws_access_key_id, 
        aws_secret_access_key = self.aws_secret_access_key)  
    return clt

def SendEmail(self, data, **kwargs):
    """takes the data and transforms the data into the appropriate frmat
    before sending the email
    data should have the following:
    data = {
        'toList':['s@domain.com'],
        'ccList':['b@domain.com'],
        'bccList':['p@domain.com'],
        'TemplateData':{vars},
        'Attachment':True,
        'fileKey':'<S3path>',
        'bucketName': <Amazon s3 bucket name>
        'Subject': 'This is a subject',
        'TemplateName':'TemplateName'
    }"""
    CHARSET = "utf-8"
    Destination = {
        'ToAddresses':data['toList'],
        'CcAddresses':data['ccList'],
        'BccAddresses':data['bccList']
    }
    # ReplyToAddresses = [current_app.config['MAIL_USERNAME']],
    ReplyToAddresses = ["a@domain.com"],
    msg = MIMEMultipart('mixed')
    msg['Subject'] = data['Subject']
    msg['From'] = "a@domain.com"
    if Destination['ToAddresses']:
        msg['To'] = ', '.join(str(v) for v in Destination['ToAddresses'])
    if Destination['CcAddresses']:
        msg['Cc'] = ', '.join(str(v) for v in Destination['CcAddresses'])
    if Destination['BccAddresses']:
        msg['Bcc'] = ', '.join(str(v) for v in Destination['BccAddresses'])
    msg_body = MIMEMultipart('alternative')
    #Obtain the template details
    # v = current_app.config['db'].view('_design/Email/_view/templates')
    v = current_app.config['db'].view('_design/Email/_view/templates')
    for row in v[data['TemplateName']]:
        Tmplte = Jinja2Template(row.value['HtmlPart'])
    HtmlPart = Tmplte.render(data = data['TemplateData'])
    htmlpart = MIMEText(HtmlPart)
    msg_body.attach(htmlpart)
    msg.attach(msg_body)
    #Attachment
    if data['Attachment']:
        filehelper = FileHelper(bucketName=data['bucketName'])
        att = MIMEApplication(filehelper.load(data['fileKey']))
        att.add_header('Content-Disposition','attachment',filename=fileKey)
        msg.attach(att)
    else:
        pass
    try:
        current_app.logger.debug(msg)
        print(msg)
        response = self.ses.send_raw_email(
            RawMessage = {
                'Data': msg.as_string()
            })
        return msg
    except ClientError as e:
        current_app.logger.debug(e.response['Error']['Message'])
        return ""

然后我在 main.py 路由中调用 ses = SES(app) 并像函数一样调用 ses

这解决了我的问题


推荐阅读