【问题标题】:SendGrid API is grouping tables togetherSendGrid API 将表格分组在一起
【发布时间】:2021-05-28 15:07:52
【问题描述】:

当尝试使用 sendgrid 发送电子邮件时,它会将表格组合在一起。这就是我希望电子邮件的外观

但它最终看起来像这样

我已经尝试过重新格式化和一切。这是我用来创建电子邮件的代码。在获取 $bodyjson 的输出时,看起来表格已正确拆分。

$StartDates = @(
        [PSCustomObject]@{
            Type      = "CurrentDay"
            startDate = [DateTime]::UtcNow.AddDays(-1).ToString('yyyy-MM-dd')
        }, [PSCustomObject]@{
            Type      = "MonthToDate"
            startDate = [DateTime]::UtcNow.ToString('yyyy-MM-01')
        }
)

$EmailStyle = "<style>BODY{font-family: Arial; font-size: 10pt;}"
$EmailStyle += "TABLE{border: 1px solid black; border-collapse: collapse;}"
$EmailStyle += "TH{border: 1px solid black; background: #D8D8D8; padding: 5px; }"
$EmailStyle += "TD{border: 1px solid black; padding: 5px; }"
$EmailStyle += "</style>"
$Html = ''

$Subscriptions = Get-AzSubscription | Select-Object -First 1

Foreach ($Subscription in $Subscriptions)
{
    Set-AzContext -Subscription $Subscription.SubscriptionId

    Foreach ($Date in $StartDates)
    {    
        $Consumption = Get-AzConsumptionUsageDetail -StartDate $Date.startDate -EndDate ([DateTime]::UtcNow.ToString('yyyy-MM-dd'))
    
        $Costs = $Consumption | Select-Object InstanceId, InstanceName, InstanceLocation, PretaxCost, Product, SubscriptionName
    
        $Costs | % {Add-Member -InputObject $_ -NotePropertyName ResourceGroup -NotePropertyValue ($_.InstanceId.split('/')[4]).ToLower()}
    
        $Groups = $Costs | Group-Object InstanceName
    
        #Write-Output $Groups
    
        $GroupsCost = $Groups | % {[PSCustomObject]@{ResourceGroup=$_.Group[0].ResourceGroup; Item=$_.Name; Description=$_.Group[0].Product; Location=$_.Group[0].InstanceLocation; Sum=($_.Group | Measure -Property 'PretaxCost' -Sum).Sum}}
    
        $TotalCost = ($GroupsCost | Measure -Property 'Sum' -Sum).Sum
        
        $RoundedTotal = [math]::Round($TotalCost, 2)
    
        $TopFive = $GroupsCost | Sort-Object Sum -Descending | Select-Object -First 5
    
        $TopFive | % {Add-Member -InputObject $_ -NotePropertyName NewSum -NotePropertyValue ([Math]::Round($_.Sum,2))}
    
        $TopFive = $TopFive | Select-Object ResourceGroup, Item, Description, Location, NewSum
    
        $TopFive = $TopFive | Add-Member -MemberType AliasProperty -Name Sum -Value NewSum -PassThru | Select-Object ResourceGroup, Item, Description, Location, Sum
    
        Write-Output "$($Subscription.Name) with Total Cost of $($RoundedTotal) for $($Date.Type)"
        Write-Output "Top 5 Spenders:"
        Write-Output $TopFive

        $Html += "<div>$($Subscription.Name) with Total Cost of $($RoundedTotal) for $($Date.Type)</div>"
        $Html += "<br></br>"
        $Html += "Top 5 Spenders:"
        $Html += "<div>$($TopFive | ConvertTo-Html -Head $EmailStyle)</div>"
        $Html += "<br></br>"
    }
}

##Send Email##
$Html += "<br></br>"
$Html += "For troubleshooting this email, the automation for this can be found at this location: "
$Html += "<a href="+ $($AutomationLink) +">Azure Portal: Expiring Security Items</a>"

$SENDGRID_API_KEY = Get-AzKeyVaultSecret -VaultName <vaultname> -Name <keyname> -AsPlainText
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer " + $SENDGRID_API_KEY)
$headers.Add("Content-Type", "application/json")

$body = @{
    personalizations = @(
        @{
            to = @(
                    @{
                        email = $EmailTo
                    }
            )
        }
    )
    from = @{
        email = $EmailFrom
    }
    subject = $Subject
    content = @(
        @{
            type = "text/html"
            value = $Html
        }
    )
}

$bodyJson = $body | ConvertTo-Json -Depth 4

$response = Invoke-RestMethod -Uri https://api.sendgrid.com/v3/mail/send -Method Post -Headers $headers -UseBasicParsing -Body $bodyJson

【问题讨论】:

  • 我们还没有收到您的来信.. 我的回答解决了您的问题吗?如果是这样,请通过单击左侧的大复选标记图标 来考虑accepting。这将帮助其他有类似问题的人更轻松地找到它。

标签: html json powershell sendgrid


【解决方案1】:

我不能完全确定 SendGrid 对 HTML 的作用,因为我没有那个,但我认为问题在于您在哪里使用 ConvertTo-Html -Head 来形成表格。
在那里你想使用开关-Fragment而不是-Head,因为你只需要在HTML的&lt;head&gt;..&lt;/head&gt;标签内定义一次css样式。

我最喜欢的构建 HTML 的方式是使用模板 Here-Strings,您可以通过填写占位符重复使用:

# create a HTML template Here-String which has three placeholders:
# {0}  --> is filled in with the style
# {1}  --> will receive the tables
# {2}  --> the $AutomationLink  (although I couldn't find the definition for that in your code..)
$htmlTemplate = @"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
{0}
</head>
<body>
{1}
<br></br>
For troubleshooting this email, the automation for this can be found at this location: 
<a href="{2}">Azure Portal: Expiring Security Items</a>
</body>
</html>
"@

# create the CSS style
$styleTemplate = @"
<style>
    BODY  {font-family: Arial; font-size: 10pt;}
    TABLE {border: 1px solid black; border-collapse: collapse;}
    TH    {border: 1px solid black; background: #D8D8D8; padding: 5px; }
    TD    {border: 1px solid black; padding: 5px; }
</style>
"@

# and another template to use for each of the tables (4 placeholders here)
# {0}  --> is filled in with the $Subscription.Name
# {1}  --> will receive the calculated $RoundedTotal
# {2}  --> is replaced by $Date.Type
# {3}  --> is replaced by the $TopFive converted to HTML table
$tableTemplate = @"
    <div>{0} with Total Cost of {1} for {2}</div>
    <br></br>
    Top 5 Spenders:
    <div>{3}</div>
    <br></br>
"@


$StartDates = @(
    [PSCustomObject]@{
        Type      = "CurrentDay"
        startDate = [DateTime]::UtcNow.AddDays(-1).ToString('yyyy-MM-dd')
    }, 
    [PSCustomObject]@{
        Type      = "MonthToDate"
        startDate = [DateTime]::UtcNow.ToString('yyyy-MM-01')
    }
)

# create a StringBuilder object to store the divs and tables in html format in
$sbTables = [System.Text.StringBuilder]::new()

$Subscriptions = Get-AzSubscription | Select-Object -First 1

foreach ($Subscription in $Subscriptions) {
    Set-AzContext -Subscription $Subscription.SubscriptionId

    foreach ($Date in $StartDates) {    
        $Consumption = Get-AzConsumptionUsageDetail -StartDate $Date.startDate -EndDate ([DateTime]::UtcNow.ToString('yyyy-MM-dd'))
        $Costs = $Consumption | Select-Object InstanceId, InstanceName, InstanceLocation, PretaxCost, Product, SubscriptionName
        $Costs | ForEach-Object {Add-Member -InputObject $_ -NotePropertyName ResourceGroup -NotePropertyValue ($_.InstanceId.split('/')[4]).ToLower()}
        $Groups = $Costs | Group-Object InstanceName

        #Write-Output $Groups

        $GroupsCost = $Groups | ForEach-Object {[PSCustomObject]@{ResourceGroup=$_.Group[0].ResourceGroup; Item=$_.Name; Description=$_.Group[0].Product; Location=$_.Group[0].InstanceLocation; Sum=($_.Group | Measure -Property 'PretaxCost' -Sum).Sum}}
        $TotalCost = ($GroupsCost | Measure -Property 'Sum' -Sum).Sum
        $RoundedTotal = [math]::Round($TotalCost, 2)
        $TopFive = $GroupsCost | Sort-Object Sum -Descending | Select-Object -First 5
        $TopFive | ForEach-Object {Add-Member -InputObject $_ -NotePropertyName NewSum -NotePropertyValue ([Math]::Round($_.Sum,2))}
        $TopFive = $TopFive | Select-Object ResourceGroup, Item, Description, Location, NewSum
        $TopFive = $TopFive | Add-Member -MemberType AliasProperty -Name Sum -Value NewSum -PassThru | Select-Object ResourceGroup, Item, Description, Location, Sum

        # Write-Output "$($Subscription.Name) with Total Cost of $($RoundedTotal) for $($Date.Type)"
        # Write-Output "Top 5 Spenders:"
        # Write-Output $TopFive

        # convert the $TopFive data into a HTML table
        $TopFiveTable = ($TopFive | ConvertTo-Html -Fragment) -join [environment]::NewLine
        # use the $tableTemplate string to fill in the placeholders
        $htmlTable = $tableTemplate -f $Subscription.Name, $RoundedTotal, $Date.Type, $TopFiveTable
        # add all this to the StringBuilder
        [void]$sbTables.AppendLine($htmlTable)
    }
}

# fill in the placeholders of the $htmlTemplate to complete it
$htmlBody = $htmlTemplate -f $styleTemplate, $sbTables.ToString(), $AutomationLink

##Send Email##

$SENDGRID_API_KEY = Get-AzKeyVaultSecret -VaultName <vaultname> -Name <keyname> -AsPlainText
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer " + $SENDGRID_API_KEY)
$headers.Add("Content-Type", "application/json")

$body = @{
    personalizations = @(
        @{
            to = @(
                    @{
                        email = $EmailTo
                    }
            )
        }
    )
    from = @{
        email = $EmailFrom
    }
    subject = $Subject
    content = @(
        @{
            type = "text/html"
            value = $htmlBody
        }
    )
}

$bodyJson = $body | ConvertTo-Json -Depth 4

$response = Invoke-RestMethod -Uri https://api.sendgrid.com/v3/mail/send -Method Post -Headers $headers -UseBasicParsing -Body $bodyJson

【讨论】:

  • 是的,它与 sendgrid api 有关。通过一个 smtp 服务器它工作得很好。
  • @RealisticMagician 如果您将第一个 &lt;br&gt;&lt;/br&gt; 替换为 &lt;p&gt; 并将第二个 &lt;br&gt;&lt;/br&gt; 替换为 &lt;/p&gt;$tableTemplate here-string 中是否有帮助,所以表格位于段落?
  • @RealisticMagician 将 HTML 换行符更改为段落是否有帮助?
  • 它没有。无论出于何种原因,json 将它们全部组合在一起
  • 我刚刚重新格式化了脚本,以完全按照您构建表格的方式进行操作,并且成功了。非常感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-10
  • 1970-01-01
  • 2023-01-23
  • 2010-12-12
  • 2020-03-03
  • 2020-07-20
  • 1970-01-01
相关资源
最近更新 更多