forked from AcaMate/AcaMate_API
[♻️] PUSH API 리팩토링 진행 중_3 : 대용량 발신 위한 버전 작성 중
This commit is contained in:
parent
59fa5bd014
commit
242f1a48df
|
@ -12,6 +12,7 @@
|
|||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
|
||||
<PackageReference Include="Polly" Version="8.5.2" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.1.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="7.1.0" />
|
||||
|
|
|
@ -114,7 +114,7 @@ else
|
|||
|
||||
|
||||
// 로컬 테스트 위한 부분 (올릴때는 꺼두기)
|
||||
// builder.WebHost.UseUrls("http://0.0.0.0:5144");
|
||||
builder.WebHost.UseUrls("http://0.0.0.0:5144");
|
||||
|
||||
///// ===== builder 설정 부 ===== /////
|
||||
|
||||
|
@ -134,7 +134,7 @@ else
|
|||
}
|
||||
|
||||
// 로컬 테스트 위한 부분 (올릴떄는 켜두기)
|
||||
app.UseHttpsRedirection();
|
||||
// app.UseHttpsRedirection();
|
||||
|
||||
app.UseRouting();
|
||||
// app.MapControllers();
|
||||
|
|
|
@ -4,10 +4,9 @@ using Microsoft.AspNetCore.Mvc;
|
|||
|
||||
using AcaMate.V1.Services;
|
||||
|
||||
|
||||
|
||||
namespace AcaMate.V1.Controllers;
|
||||
|
||||
|
||||
[ApiController]
|
||||
[Route("/api/v1/in/push")]
|
||||
[ApiExplorerSettings(GroupName = "공통")]
|
||||
|
@ -16,10 +15,12 @@ public class PushController : ControllerBase
|
|||
|
||||
private readonly IWebHostEnvironment _env;
|
||||
private readonly ILogger<PushController> _logger;
|
||||
public PushController(IWebHostEnvironment env, ILogger<PushController> logger)
|
||||
private readonly IPushQueue _pushQueue;
|
||||
public PushController(IWebHostEnvironment env, ILogger<PushController> logger, IPushQueue pushQueue)
|
||||
{
|
||||
_env = env;
|
||||
_logger = logger;
|
||||
_pushQueue = pushQueue;
|
||||
}
|
||||
|
||||
[HttpGet()]
|
||||
|
@ -44,48 +45,58 @@ public class PushController : ControllerBase
|
|||
[HttpPost("send")]
|
||||
[CustomOperation("푸시전송", "저장된 양식으로, 사용자에게 푸시를 전송한다.(로컬 테스트 불가)", "푸시")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus<object>))]
|
||||
public async Task<IActionResult> SendPush(string deviceToken, [FromBody] Payload? payload)
|
||||
public async Task<IActionResult> SendPush(string deviceToken, [FromBody] Payload payload)
|
||||
{
|
||||
|
||||
if (string.IsNullOrWhiteSpace(deviceToken) || payload == null)
|
||||
return BadRequest(APIResponse.InvalidInputError());
|
||||
|
||||
var isDev = _env.IsDevelopment();
|
||||
|
||||
var pushFile = new PushFile()
|
||||
var pushRequest = new PushRequest
|
||||
{
|
||||
uri = isDev ? "https://api.sandbox.push.apple.com/" : "https://api.push.apple.com/",
|
||||
p12Path = isDev ? "/src/private/AM_Push_Sandbox.p12" : "/src/private/AM_Push.p12",
|
||||
p12PWPath = "/src/private/appleKeys.json",
|
||||
apnsTopic = "me.myds.ipstein.acamate.AcaMate"
|
||||
deviceToken = deviceToken,
|
||||
payload = payload
|
||||
};
|
||||
_pushQueue.Enqueue(pushRequest);
|
||||
return Ok("[푸시] 접수 완료");
|
||||
|
||||
try
|
||||
{
|
||||
if (await new ApnsPushService().SendPushNotificationAsync(deviceToken, pushFile, payload))
|
||||
{
|
||||
return Ok(APIResponse.Send("000", "정상", Empty));
|
||||
}
|
||||
else
|
||||
{
|
||||
return StatusCode(500, APIResponse.UnknownError());
|
||||
}
|
||||
|
||||
}
|
||||
catch (ServiceConnectionFailedException failEx)
|
||||
{
|
||||
_logger.LogError($"[푸시][에러] : {failEx}");
|
||||
return StatusCode(300, APIResponse.InternalSeverError());
|
||||
}
|
||||
catch (HttpRequestException httpEx)
|
||||
{
|
||||
_logger.LogError($"[푸시][에러] : {httpEx}");
|
||||
return StatusCode(300, APIResponse.InternalSeverError());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError($"[푸시][에러] : {ex}");
|
||||
return StatusCode(500, APIResponse.UnknownError());
|
||||
}
|
||||
//
|
||||
//
|
||||
// var isDev = _env.IsDevelopment();
|
||||
//
|
||||
// var pushFileSetting = new PushFileSetting()
|
||||
// {
|
||||
// uri = isDev ? "https://api.sandbox.push.apple.com/" : "https://api.push.apple.com/",
|
||||
// p12Path = isDev ? "/src/private/AM_Push_Sandbox.p12" : "/src/private/AM_Push.p12",
|
||||
// p12PWPath = "/src/private/appleKeys.json",
|
||||
// apnsTopic = "me.myds.ipstein.acamate.AcaMate"
|
||||
// };
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// if (await new ApnsPushService().SendPushNotificationAsync(deviceToken, pushFileSetting, payload))
|
||||
// {
|
||||
// return Ok(APIResponse.Send("000", "정상", Empty));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return StatusCode(500, APIResponse.UnknownError());
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// catch (ServiceConnectionFailedException failEx)
|
||||
// {
|
||||
// _logger.LogError($"[푸시][에러] : {failEx}");
|
||||
// return StatusCode(300, APIResponse.InternalSeverError());
|
||||
// }
|
||||
// catch (HttpRequestException httpEx)
|
||||
// {
|
||||
// _logger.LogError($"[푸시][에러] : {httpEx}");
|
||||
// return StatusCode(300, APIResponse.InternalSeverError());
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// _logger.LogError($"[푸시][에러] : {ex}");
|
||||
// return StatusCode(500, APIResponse.UnknownError());
|
||||
// }
|
||||
}
|
||||
}
|
|
@ -43,10 +43,16 @@ public class Alert
|
|||
/// <summary>
|
||||
/// 푸시 등록하기 위한 여러 데이터 목록
|
||||
/// </summary>
|
||||
public class PushFile
|
||||
public class PushFileSetting
|
||||
{
|
||||
public string uri { get; set; }
|
||||
public string p12Path { get; set; }
|
||||
public string p12PWPath { get; set; }
|
||||
public string apnsTopic { get; set; }
|
||||
}
|
||||
|
||||
public class PushRequest
|
||||
{
|
||||
public string deviceToken { get; set; }
|
||||
public Payload payload { get; set; }
|
||||
}
|
|
@ -6,47 +6,55 @@ using System.Security.Cryptography.X509Certificates;
|
|||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Polly;
|
||||
|
||||
using AcaMate.V1.Models;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Version = System.Version;
|
||||
|
||||
namespace AcaMate.V1.Services;
|
||||
public class ApnsPushService
|
||||
|
||||
public interface IApnsPushService
|
||||
{
|
||||
public ApnsPushService()
|
||||
Task SendPushNotificationAsync(string deviceToken, Payload payload);
|
||||
}
|
||||
public class ApnsPushService: IApnsPushService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly PushFileSetting _setting;
|
||||
|
||||
public ApnsPushService(HttpClient httpClient, IOptions<PushFileSetting> options)
|
||||
{
|
||||
|
||||
_httpClient = httpClient;
|
||||
_setting = options.Value;
|
||||
}
|
||||
|
||||
public async Task<bool> SendPushNotificationAsync(string deviceToken, PushFile pushFile, Payload payload)
|
||||
public async Task SendPushNotificationAsync(string deviceToken, Payload payload)
|
||||
{
|
||||
|
||||
// 존재 안하면 예외 던져 버리는거
|
||||
if (!File.Exists(pushFile.p12Path) || !File.Exists(pushFile.p12PWPath))
|
||||
if (!File.Exists(_setting.p12Path) || !File.Exists(_setting.p12PWPath))
|
||||
throw new FileNotFoundException("[푸시] : p12 관련 파일 확인 필요");
|
||||
|
||||
var jsonPayload = JsonSerializer.Serialize(payload);
|
||||
var keys =
|
||||
JsonSerializer.Deserialize<Dictionary<string, string>>(await File.ReadAllTextAsync(pushFile.p12PWPath))
|
||||
?? throw new FileContentNotFoundException("[푸시] : 파일 내부의 값을 읽어오지 못함");
|
||||
|
||||
try
|
||||
{
|
||||
// var certificate = new X509Certificate2(pushFile.p12Path, keys["Password"]);
|
||||
|
||||
var handler = new HttpClientHandler();
|
||||
handler.ClientCertificates
|
||||
.Add(new X509Certificate2(pushFile.p12Path, keys["Password"]));
|
||||
handler.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;
|
||||
|
||||
|
||||
using var client = new HttpClient(handler)
|
||||
{
|
||||
BaseAddress = new Uri(pushFile.uri),
|
||||
Timeout = TimeSpan.FromSeconds(60)
|
||||
};
|
||||
// var keys =
|
||||
// JsonSerializer.Deserialize<Dictionary<string, string>>(await File.ReadAllTextAsync(_setting.p12PWPath))
|
||||
// ?? throw new FileContentNotFoundException("[푸시] : 파일 내부의 값을 읽어오지 못함");
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// var handler = new HttpClientHandler();
|
||||
// handler.ClientCertificates
|
||||
// .Add(new X509Certificate2(_setting.p12Path, keys["Password"]));
|
||||
// handler.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;
|
||||
//
|
||||
//
|
||||
// using var client = new HttpClient(handler)
|
||||
// {
|
||||
// BaseAddress = new Uri(_setting.uri),
|
||||
// Timeout = TimeSpan.FromSeconds(60)
|
||||
// };
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, $"/3/device/{deviceToken}")
|
||||
{
|
||||
|
@ -55,22 +63,36 @@ public class ApnsPushService
|
|||
};
|
||||
|
||||
// 필수 헤더 추가
|
||||
request.Headers.Add("apns-topic", pushFile.apnsTopic);
|
||||
request.Headers.Add("apns-topic", _setting.apnsTopic);
|
||||
request.Headers.Add("apns-push-type", "alert");
|
||||
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
if (response.IsSuccessStatusCode) return true;
|
||||
else
|
||||
var policy = Policy.Handle<HttpRequestException>()
|
||||
.WaitAndRetryAsync(3, retryAttempt =>
|
||||
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
|
||||
|
||||
await policy.ExecuteAsync(async () =>
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
throw new ServiceConnectionFailedException($"[푸시] : APNS 통신 실패 - {errorContent}");
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException httpEx)
|
||||
{
|
||||
Console.WriteLine($"HttpRequestException: {httpEx.Message}");
|
||||
throw new ServiceConnectionFailedException($"[푸시] : APNS 통신 실패 - {httpEx.Message}");
|
||||
}
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
throw new ServiceConnectionFailedException($"[푸시] : APNS 통신 실패 - {errorContent}");
|
||||
}
|
||||
});
|
||||
|
||||
// var response = await client.SendAsync(request);
|
||||
//
|
||||
// if (response.IsSuccessStatusCode) return true;
|
||||
// else
|
||||
// {
|
||||
// var errorContent = await response.Content.ReadAsStringAsync();
|
||||
// throw new ServiceConnectionFailedException($"[푸시] : APNS 통신 실패 - {errorContent}");
|
||||
// }
|
||||
// }
|
||||
// catch (HttpRequestException httpEx)
|
||||
// {
|
||||
// Console.WriteLine($"HttpRequestException: {httpEx.Message}");
|
||||
// throw new ServiceConnectionFailedException($"[푸시] : APNS 통신 실패 - {httpEx.Message}");
|
||||
// }
|
||||
}
|
||||
}
|
15
README.md
15
README.md
|
@ -7,10 +7,11 @@
|
|||
- JetBrains Rider
|
||||
|
||||
### 추가 패키지
|
||||
| No. | Name | Version | Description |
|
||||
|:---:|:-------------------------------------------:|:-------:|:---------------------------|
|
||||
| 1 | Microsoft.AspNetCore.OpenApi | 8.0.10 | OpenAPI 를 지원하기 위해 사용 |
|
||||
| 2 | Microsoft.EntityFrameworkCore | 8.0.10 | 데이터베이스 작업을 간편하게 수행하기 위해 사용 |
|
||||
| 3 | Pomelo.EntityFrameworkCore.MySql | 8.0.2 | MariaDB 연결 |
|
||||
| 4 |Microsoft.AspNetCore.Authentication.JwtBearer| 8.0.10 | |
|
||||
| 5 |Dapper|2.1.35|SQL 직접 작성|
|
||||
| No. | Name | Version | Description |
|
||||
|:---:|:---------------------------------------------:|:-------:|:---------------------------|
|
||||
| 1 | Microsoft.AspNetCore.OpenApi | 8.0.10 | OpenAPI 를 지원하기 위해 사용 |
|
||||
| 2 | Microsoft.EntityFrameworkCore | 8.0.10 | 데이터베이스 작업을 간편하게 수행하기 위해 사용 |
|
||||
| 3 | Pomelo.EntityFrameworkCore.MySql | 8.0.2 | MariaDB 연결 |
|
||||
| 4 | Microsoft.AspNetCore.Authentication.JwtBearer | 8.0.10 | |
|
||||
| 5 | Dapper | 2.1.35 | SQL 직접 작성 |
|
||||
| 6 | Polly | 8.5.2 | 일시적 장애 및 예외상황 대처용| |
|
||||
|
|
|
@ -4,5 +4,11 @@
|
|||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"PushFileSetting": {
|
||||
"uri": "https://api.sandbox.push.apple.com/",
|
||||
"p12Path": "/src/private/AM_Push_Sandbox.p12",
|
||||
"p12PWPath": "/src/private/appleKeys.json",
|
||||
"apnsTopic": "me.myds.ipstein.acamate.AcaMate"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,5 +5,11 @@
|
|||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
"PushFileSetting": {
|
||||
"Uri": "https://api.push.apple.com/",
|
||||
"P12Path": "/src/private/AM_Push.p12",
|
||||
"P12PWPath": "/src/private/appleKeys.json",
|
||||
"ApnsTopic": "me.myds.ipstein.acamate.AcaMate"
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
Loading…
Reference in New Issue
Block a user