【问题标题】:.NET Core 2.1 Apple Push Notifications.NET Core 2.1 Apple 推送通知
【发布时间】:2019-04-22 01:34:19
【问题描述】:

我必须使用将在 Windows 2008 Server R2 上执行的 .Net Core WebAPI 向特定 iOS 设备发送推送通知。服务器本身不应该是问题,因为它正在使用 node.js 库。但我希望它与 ASP .Net Core 2.1 中的 WepAPI 一起使用,该 WepAPI 由内置的 Kestrel 服务器自托管。也许你已经知道如何解决这个问题了。

我的代码:

// This will encode the jason web token apns needs for the authorization
// get the base64 private key of the .p8 file from apple
string p8File = System.IO.File.ReadAllText(Settings.Apn.PrivateKey);
p8File = p8File.Replace("-----BEGIN PRIVATE KEY-----", string.Empty);
p8File = p8File.Replace("-----END PRIVATE KEY-----", string.Empty);
p8File = p8File.Replace(" ", string.Empty);

byte[] keyData = Convert.FromBase64String(p8File);
ECDsa key = new ECDsaCng(CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob));

ECDsaSecurityKey securityKey = new ECDsaSecurityKey(key) { KeyId = Settings.Apn.KeyId };
SigningCredentials credentials = new SigningCredentials(securityKey, "ES256");

SecurityTokenDescriptor descriptor =
    new SecurityTokenDescriptor
        {
            IssuedAt = DateTime.Now,
            Issuer = Settings.Apn.TeamId,
            SigningCredentials = credentials
        };

JwtSecurityTokenHandler jwtHandler = new JwtSecurityTokenHandler();
string encodedToken = jwtHandler.CreateEncodedJwt(descriptor);
this.log?.LogInformation($"Created JWT: {encodedToken}");

// The hostname is: https://api.development.push.apple.com:443
HttpClient client = new HttpClient { BaseAddress = new Uri(Settings.Apn.Hostname) };
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
this.log?.LogInformation("Initialized new HttpClient.");

// payload content for the apns
JObject payloadData = new JObject
                          {
                              new JProperty("alert", data.Message),
                              new JProperty("badge", 2),
                              new JProperty("sound", "default")
                          };
JObject payload = new JObject
                       {
                           new JProperty("aps", payloadData)
                       };
this.log?.LogInformation($"Setup payload: {payload}");

// HttpRequestMessage that should be send
HttpRequestMessage request = new HttpRequestMessage(
                                 HttpMethod.Post,
                                 $"{Settings.Apn.Hostname}/3/device/{data.DeviceId}")
                                 {
                                     Content = new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, "application/json")
                                 };
this.log?.LogInformation("Setup HttpRequestMessage.");

// Setup the header
request.Headers.Add("Authorization", $"Bearer {encodedToken}");
request.Headers.Add("apns-id", Guid.NewGuid().ToString());
request.Headers.Add("apns-expiration", DateTime.Now.AddDays(1).ToString(CultureInfo.InvariantCulture));
request.Headers.Add("apns-priority", "10");
request.Headers.Add("apns-topic", "de.gefasoft-engineering.FabChat");

// Debug logging
this.log.LogDebug(request.ToString());
this.log.LogDebug(await request.Content.ReadAsStringAsync());
this.log.LogDebug(request.RequestUri.Host + request.RequestUri.Port);

// Send request
var result = await client.SendAsync(request);
this.log?.LogInformation("Sent request.");
this.log?.LogInformation(await result.Content.ReadAsStringAsync());

我总是抛出以下异常:

System.Net.Http.HttpRequestException:无法建立 SSL 连接 成立,见内部异常。 ---> System.Security.Authentication.AuthenticationException:身份验证 失败,请参阅内部异常。 ---> System.ComponentModel.Win32Exception:收到的消息是 意外或格式错误 --- 内部异常堆栈结束 追踪---

【问题讨论】:

  • 我遇到了类似的问题,在节点应用程序中运行良好,但在 dotnet 核心中运行良好,您找到解决方案了吗?
  • @jphillips05 抱歉回答迟了。我在下面发布了我的代码!

标签: c# asp.net-core apple-push-notifications


【解决方案1】:

使用 CorePush 库

它非常轻巧。我在所有项目中都使用它来发送 Firebase Android/WebPush 和 Apple iOS 推送通知。有用的链接:

  1. NuGet package
  2. Documentation

界面非常简洁:

发送 APN 消息

var apn = new ApnSender(settings, httpClient);
await apn.SendAsync(notification, deviceToken);

如果需要也可以发送Android FCM消息

var fcm = new FcmSender(settings, httpClient);
await fcm.SendAsync(deviceToken, notification);

【讨论】:

    【解决方案2】:

    我已经对@civilator 的回答发表了评论。但我认为,有些人读过它,所以我再次发布它。 这是对我有用的代码。抱歉回复晚了!

    private readonly string hostname = "gateway.sandbox.push.apple.com";
    private readonly int port = 2195;
    
    public async Task<RestResult<JObject>> SendPushNotification(string deviceToken, string message)
        {
            this.log?.LogInformation("Trying to send push notification.");
            X509Certificate2Collection certificatesCollection;
    
            // Setup and read the certificate
            // NOTE: You should get the certificate from your apple developer account.
            try
            {
                string certificatePath = Settings.Apn.Certificate;
                X509Certificate2 clientCertificate = new X509Certificate2(
                    File.ReadAllBytes(certificatePath),
                    Settings.Apn.Password);
                certificatesCollection = new X509Certificate2Collection(clientCertificate);
                this.log?.LogInformation("Setup certificates.");
            }
            catch (Exception e)
            {
                this.log?.LogError(e.ToString());
                return new RestResult<JObject> { Result = "exception", Message = "Failed to setup certificates." };
            }
    
            // Setup a tcp connection to the apns
            TcpClient client = new TcpClient(AddressFamily.InterNetwork);
            this.log?.LogInformation("Created new TcpClient.");
            try
            {
                IPHostEntry host = Dns.GetHostEntry(this.hostname);
                await client.ConnectAsync(host.AddressList[0], this.port);
                this.log?.LogInformation($"Opened connection to {this.hostname}:{this.port}.");
            }
            catch (Exception e)
            {
                this.log?.LogError("Failed to open tcp connection to the apns.");
                this.log?.LogError(e.ToString());
            }
    
            // Validate the Certificate you get from the APN (for more information read the documentation:
            // https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1).
            SslStream sslStream = new SslStream(
                client.GetStream(),
                false,
                new RemoteCertificateValidationCallback(this.ValidateServerCertificate),
                null);
    
            try
            {
                await sslStream.AuthenticateAsClientAsync(this.hostname, certificatesCollection, SslProtocols.Tls, false);
                MemoryStream memoryStream = new MemoryStream();
                BinaryWriter writer = new BinaryWriter(memoryStream);
                writer.Write((byte)0);
                writer.Write((byte)0);
                writer.Write((byte)32);
    
                writer.Write(HexStringToByteArray(deviceToken.ToUpper()));
    
                // Creating an payload object to send key values to the apns
                JObject aps = new JObject
                                  {
                                      new JProperty("alert", message),
                                      new JProperty("badge", 0),
                                      new JProperty("sound", "default")
                                  };
    
                JObject payload = new JObject
                                      {
                                        new JProperty("aps", aps)
                                      };
    
                string payloadString = JsonConvert.SerializeObject(payload);
                writer.Write((byte)0);
                writer.Write((byte)payloadString.Length);
    
                byte[] b1 = System.Text.Encoding.UTF8.GetBytes(payloadString);
                writer.Write(b1);
                writer.Flush();
    
                byte[] array = memoryStream.ToArray();
                sslStream.Write(array);
                sslStream.Flush();
                client.Dispose();
            }
            catch (AuthenticationException ex)
            {
                this.log?.LogError(ex.ToString());
                client.Dispose();
                return new RestResult<JObject> { Result = "exception", Message = "Authentication Exception." };
            }
            catch (Exception e)
            {
                this.log?.LogError(e.ToString());
                client.Dispose();
                return new RestResult<JObject> { Result = "exception", Message = "Exception was thrown." };
            }
    
            this.log?.LogInformation("Notification sent.");
            return new RestResult<JObject> { Result = "success", Message = "Notification sent. Check your device." };
        }
    
        #region Helper methods
    
        private static byte[] HexStringToByteArray(string hex)
        {
            return Enumerable.Range(0, hex.Length)
                .Where(x => x % 2 == 0)
                .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                .ToArray();
        }
    
        // The following method is invoked by the RemoteCertificateValidationDelegate.
        private bool ValidateServerCertificate(
            object sender,
            X509Certificate certificate,
            X509Chain chain,
            SslPolicyErrors sslPolicyErrors)
        {
            if (sslPolicyErrors == SslPolicyErrors.None)
            {
                this.log?.LogInformation("Server Certificate validated.");
                return true;
            }
    
            this.log?.LogError($"Server Certificate error: {sslPolicyErrors}");
    
            // Do not allow this client to communicate with unauthenticated servers.
            return false;
        }
    
        #endregion
    

    【讨论】:

    • 我使用相同的方法向 Apple 设备发送推送通知。但是什么也没发生,我在 .NET 中搜索解决方案,但总是找到相同的代码。有没有人可以帮我解决?
    【解决方案3】:

    您能否尝试在 apns-topic 行之后将版本信息添加到您的请求中,如下所示?它运行完成,添加以下行后我第一次收到“错误的设备令牌”错误。

    request.Version = new Version(2, 0);
    System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
    

    我在下面的帖子中看到了版本设置命令。

    How to implement apple token based push notifications (using p8 file) in C#?

    【讨论】:

    • 我用 .p12 文件做到了这一点,现在它可以工作了。 janaks.com.np/…
    • Sebi E,你能弄清楚如何将通知发送到生产环境,发送到从应用商店安装的应用吗?我可以在调试模式下收到通知,但是当我从应用商店安装应用程序时,它没有收到通知。
    • civilator,我还没测试过。但我认为您需要在您的苹果开发者帐户上为已发布的应用程序创建一个新证书并将请求发送到“gateway.push.apple.com:2195”而不是“gateway.sandbox.push.apple.com:2195” .也许这会有所帮助。
    • @Sebi.E 您使用的是旧的二进制格式,而不是新的基于 http/2 的 api
    猜你喜欢
    • 2019-03-07
    • 2017-07-07
    • 2018-05-25
    • 2010-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-18
    • 2013-04-27
    相关资源
    最近更新 更多