[🐛] 13차 푸시 확인 - 에러 처리 및 여러가지 코드 정리, 푸시 전송 기능 완료
Signed-off-by: seonkyu.kim <sean.kk@daum.net>
This commit is contained in:
parent
846f5c91a3
commit
a8e2a63db0
|
@ -1,15 +1,23 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace AcaMate.Common.Models;
|
namespace AcaMate.Common.Models;
|
||||||
|
|
||||||
public class APIResponseStatus<T>
|
public class APIResponseStatus<T>
|
||||||
{
|
{
|
||||||
public Status status { get; set; }
|
public Status status { get; set; }
|
||||||
public T data { get; set; }
|
public T? data { get; set; }
|
||||||
|
|
||||||
|
public string JsonToString()
|
||||||
|
{
|
||||||
|
return JsonSerializer.Serialize(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Status
|
public class Status
|
||||||
{
|
{
|
||||||
public string code { get; set; }
|
public string code { get; set; }
|
||||||
public string message { get; set; }
|
public string message { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class DefaultResponse
|
public static class DefaultResponse
|
||||||
|
@ -22,7 +30,7 @@ public static class DefaultResponse
|
||||||
// // 외부 초기화 방지
|
// // 외부 초기화 방지
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public static readonly APIResponseStatus<string> Success = new APIResponseStatus<string>
|
public static APIResponseStatus<string> Success = new APIResponseStatus<string>
|
||||||
{
|
{
|
||||||
status = new Status()
|
status = new Status()
|
||||||
{
|
{
|
||||||
|
@ -31,7 +39,7 @@ public static class DefaultResponse
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly APIResponseStatus<string> InvalidInputError = new APIResponseStatus<string>
|
public static APIResponseStatus<string> InvalidInputError = new APIResponseStatus<string>
|
||||||
{
|
{
|
||||||
status = new Status()
|
status = new Status()
|
||||||
{
|
{
|
||||||
|
@ -40,7 +48,7 @@ public static class DefaultResponse
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly APIResponseStatus<string> NotFoundError = new APIResponseStatus<string>
|
public static APIResponseStatus<string> NotFoundError = new APIResponseStatus<string>
|
||||||
{
|
{
|
||||||
status = new Status()
|
status = new Status()
|
||||||
{
|
{
|
||||||
|
@ -49,9 +57,9 @@ public static class DefaultResponse
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly APIResponseStatus<string> InternalSeverError = new APIResponseStatus<string>
|
public static APIResponseStatus<string> InternalSeverError = new APIResponseStatus<string>
|
||||||
{
|
{
|
||||||
status = new Status()
|
status = new Status
|
||||||
{
|
{
|
||||||
code = "003",
|
code = "003",
|
||||||
message = "통신에 오류가 발생하였습니다."
|
message = "통신에 오류가 발생하였습니다."
|
||||||
|
@ -59,7 +67,7 @@ public static class DefaultResponse
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
public static readonly APIResponseStatus<string> UnknownError = new APIResponseStatus<string>
|
public static APIResponseStatus<string> UnknownError = new APIResponseStatus<string>
|
||||||
{
|
{
|
||||||
status = new Status()
|
status = new Status()
|
||||||
{
|
{
|
||||||
|
|
|
@ -56,7 +56,8 @@ public class AppController : ControllerBase
|
||||||
|
|
||||||
string jsonString = JsonSerializer.Serialize(response);
|
string jsonString = JsonSerializer.Serialize(response);
|
||||||
|
|
||||||
return Ok(jsonString);
|
// return Ok(jsonString);
|
||||||
|
return Ok(response.JsonToString());
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using AcaMate.Common.Models;
|
||||||
using AcaMate.V1.Models;
|
using AcaMate.V1.Models;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
@ -14,7 +15,6 @@ public class PushController : ControllerBase
|
||||||
{
|
{
|
||||||
|
|
||||||
private readonly IWebHostEnvironment _env;
|
private readonly IWebHostEnvironment _env;
|
||||||
|
|
||||||
public PushController(IWebHostEnvironment env)
|
public PushController(IWebHostEnvironment env)
|
||||||
{
|
{
|
||||||
_env = env;
|
_env = env;
|
||||||
|
@ -27,17 +27,44 @@ public class PushController : ControllerBase
|
||||||
return Ok("SEND");
|
return Ok("SEND");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a push notification to the specified device token with the provided payload.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deviceToken">The device token to send the notification to.</param>
|
||||||
|
/// <param name="payload">The payload of the push notification.</param>
|
||||||
|
/// <returns>An IActionResult indicating the result of the operation.</returns>
|
||||||
|
/// <response code="200">Push notification sent successfully.</response>
|
||||||
|
/// <response code="400">Invalid input parameters.</response>
|
||||||
|
/// <response code="500">Internal server error occurred.</response>
|
||||||
|
/// <response code="999">Service unavailable.</response>
|
||||||
[HttpPost("send")]
|
[HttpPost("send")]
|
||||||
[CustomOperation("푸시전송", "저장된 양식으로, 사용자에게 푸시를 전송한다.", "푸시")]
|
[CustomOperation("푸시전송", "저장된 양식으로, 사용자에게 푸시를 전송한다.", "푸시")]
|
||||||
// public async Task<IActionResult> SendPush(string deviceToken, string title, string body, int badge)
|
[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))
|
||||||
|
{
|
||||||
|
var inputError = DefaultResponse.InvalidInputError;
|
||||||
|
inputError.status.message = "Deviece Toekn 오류";
|
||||||
|
return StatusCode(500,inputError);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (payload == null)
|
||||||
|
{
|
||||||
|
var inputError = DefaultResponse.InvalidInputError;
|
||||||
|
inputError.status.message = "payload 입력 오류";
|
||||||
|
return StatusCode(500,inputError);
|
||||||
|
}
|
||||||
|
|
||||||
|
string uri = "";
|
||||||
string p12Path = "";
|
string p12Path = "";
|
||||||
string p12PWPath = "/src/private/appleKeys.json";
|
string p12PWPath = "/src/private/appleKeys.json";
|
||||||
// string p12PWPath = "private/appleKeys.json";
|
// string p12PWPath = "private/appleKeys.json";
|
||||||
string apnsTopic = "me.myds.ipstein.acamate.AcaMate";
|
string apnsTopic = "me.myds.ipstein.acamate.AcaMate";
|
||||||
string uri = "";
|
|
||||||
|
|
||||||
if (_env.IsDevelopment())
|
if (_env.IsDevelopment())
|
||||||
{
|
{
|
||||||
|
@ -56,7 +83,23 @@ public class PushController : ControllerBase
|
||||||
var pushService = new ApnsPushService(uri, p12Path, p12PWPath, apnsTopic);
|
var pushService = new ApnsPushService(uri, p12Path, p12PWPath, apnsTopic);
|
||||||
|
|
||||||
// 푸시 알림 전송
|
// 푸시 알림 전송
|
||||||
await pushService.SendPushNotificationAsync(deviceToken, payload);
|
var result = await pushService.SendPushNotificationAsync(deviceToken, payload);
|
||||||
return Ok("done");
|
if (result.Success)
|
||||||
|
{
|
||||||
|
return Ok(DefaultResponse.Success.JsonToString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
var apnsError = DefaultResponse.InternalSeverError;
|
||||||
|
apnsError.status.message = $"{result.Message}";
|
||||||
|
return result.Code switch
|
||||||
|
{
|
||||||
|
"002" => StatusCode(002, apnsError),
|
||||||
|
"003" => StatusCode(003, apnsError),
|
||||||
|
"999" => StatusCode(999, apnsError)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
15
Program/V1/Models/APIResult.cs
Normal file
15
Program/V1/Models/APIResult.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace AcaMate.V1.Models;
|
||||||
|
|
||||||
|
public class APIResult
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string Code { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
public string JsonToString()
|
||||||
|
{
|
||||||
|
return JsonSerializer.Serialize(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
namespace AcaMate.V1.Models;
|
namespace AcaMate.V1.Models;
|
||||||
|
@ -20,7 +21,10 @@ public class Aps
|
||||||
sound = "default";
|
sound = "default";
|
||||||
content_available = 1;
|
content_available = 1;
|
||||||
}
|
}
|
||||||
|
[Required(ErrorMessage = "필수 입력 누락 (alert)")]
|
||||||
public Alert alert { get; set; }
|
public Alert alert { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 입력 누락 (badge")]
|
||||||
public int badge { get; set; } // 앱 아이콘 표시 배지 숫자 설정
|
public int badge { get; set; } // 앱 아이콘 표시 배지 숫자 설정
|
||||||
public string sound { get; set; } // 사운드 파일 이름 default = "default"
|
public string sound { get; set; } // 사운드 파일 이름 default = "default"
|
||||||
public int content_available { get; set; } // 백그라운드 알림 활성화: 필수 (1)
|
public int content_available { get; set; } // 백그라운드 알림 활성화: 필수 (1)
|
||||||
|
@ -29,7 +33,9 @@ public class Aps
|
||||||
|
|
||||||
public class Alert
|
public class Alert
|
||||||
{
|
{
|
||||||
|
[Required(ErrorMessage = "필수 입력 누락 (title")]
|
||||||
public string title { get; set; } // 제목
|
public string title { get; set; } // 제목
|
||||||
|
[Required(ErrorMessage = "필수 입력 누락 (body)")]
|
||||||
public string body { get; set; } // 내용
|
public string body { get; set; } // 내용
|
||||||
public string? subtitle { get; set; } // 부제목 (선택)
|
public string? subtitle { get; set; } // 부제목 (선택)
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
@ -22,9 +23,21 @@ public class ApnsPushService
|
||||||
_apnsTopic = apnsTopic ?? throw new ArgumentNullException(nameof(apnsTopic));
|
_apnsTopic = apnsTopic ?? throw new ArgumentNullException(nameof(apnsTopic));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendPushNotificationAsync(string deviceToken, Payload payload)
|
public async Task<APIResult> SendPushNotificationAsync(string deviceToken, Payload payload)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// 존재 안하면 예외 던져 버리는거
|
||||||
|
if (!File.Exists(_p12PWPath))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"File not found: {_p12PWPath}");
|
||||||
|
return new APIResult
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Code = "003",
|
||||||
|
Message = "서버 오류 : p12 PW 파일 확인 필요"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
var jsonPayload = JsonSerializer.Serialize(payload);
|
var jsonPayload = JsonSerializer.Serialize(payload);
|
||||||
|
|
||||||
var keys = JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText(_p12PWPath));
|
var keys = JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText(_p12PWPath));
|
||||||
|
@ -56,23 +69,58 @@ public class ApnsPushService
|
||||||
request.Headers.Add("apns-topic", _apnsTopic);
|
request.Headers.Add("apns-topic", _apnsTopic);
|
||||||
request.Headers.Add("apns-push-type", "alert");
|
request.Headers.Add("apns-push-type", "alert");
|
||||||
|
|
||||||
Console.WriteLine("Sending push notification...");
|
|
||||||
Console.WriteLine($"Payload: {jsonPayload}");
|
Console.WriteLine($"Send -> Payload: {jsonPayload}");
|
||||||
|
|
||||||
var response = await client.SendAsync(request);
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
if (response.IsSuccessStatusCode)
|
if (response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Push notification sent successfully.");
|
return new APIResult
|
||||||
|
{
|
||||||
|
Success = true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var errorContent = await response.Content.ReadAsStringAsync();
|
var errorContent = await response.Content.ReadAsStringAsync();
|
||||||
Console.WriteLine($"Failed. Status Code: {response.StatusCode}, Error: {errorContent}");
|
return new APIResult
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Code = "003",
|
||||||
|
Message = $"APN 통신 실패 = {response.StatusCode} : {errorContent}"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (HttpRequestException httpEx)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"HttpRequestException: {httpEx.Message}");
|
||||||
|
return new APIResult
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Code = "003",
|
||||||
|
Message = $"통신 실패 : {httpEx.Message}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (CryptographicException ex)
|
||||||
|
{
|
||||||
|
return new APIResult
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Code = "999",
|
||||||
|
Message = $"오류 발생 : {ex.Message}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Error sending push notification: {ex.Message}");
|
return new APIResult
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Code = "999",
|
||||||
|
Message = $"오류 발생 : {ex.Message}"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user