.NET 6 - Build Line Notify Web API

文章目錄

此篇文章將會透過 .NET 6 來實作Line Notify,傳送文字、貼圖與相片至Line。

詳細的Line API規格請至Line官方文件查詢。

Implementation

1. Line Notify服務申請

自Line Notify申請服務以取得Client ID、Client Secret,並設定Callback URL。

Line Notify申請服務

2. 基本設定

  • 在appsettings.json中,設定client_idclient_secretredirect_uri
"LineNotifySetting": {
    "client_id": "...",      // Client ID
    "client_secret": "...",  // Client Secret
    "redirect_uri": "http://localhost:4200" // Callback URL
},
  • 建立資料模型(data model)。
public class LineNotifySetting
{
    public string Client_ID { get; set; } = null!;
    public string Client_Secret { get; set; } = null!;
    public string Redirect_URI { get; set; } = null!;
}
  • 建立服務(service)。
    • 宣告欲使用的LINE Notify API。
    • 注入IConfiguration,以取得appsettings.json中的LineNotifySetting
    • 注入IHttpClientFactory,並在program.cs中呼叫AddHttpClient方法進行註冊,以建立HttpClient執行個體來發送Http請求。
      // Add HttpClient service
      builder.Services.AddHttpClient();
      
public class LineNotifyService
{
    private const string LineAuthorizeAPI = "https://notify-bot.line.me/oauth/authorize";
    private const string LineTokenAPI = "https://notify-bot.line.me/oauth/token";
    private const string LineNotifyAPI = "https://notify-api.line.me/api/notify";
    private const string LineRevokeAPI = "https://notify-api.line.me/api/revoke";

    private readonly IConfiguration _configuration;
    private readonly LineNotifySetting _lineNotifySetting = new LineNotifySetting();
    private readonly IHttpClientFactory _httpClientFactory;

    public LineNotifyService(IConfiguration configuration, IHttpClientFactory httpClientFactory)
    {
        _configuration = configuration;
        _lineNotifySetting.Client_ID = _configuration["LineNotifySetting:client_id"];
        _lineNotifySetting.Client_Secret = _configuration["LineNotifySetting:client_secret"];
        _lineNotifySetting.Redirect_URI = _configuration["LineNotifySetting:redirect_uri"];
        _httpClientFactory = httpClientFactory;
    }
}

2. 導向Line Notify授權網頁

將使用者導向Line Notify服務授權之頁面。

使用者授權使用Line Notify服務使用者授權使用Line Notify服務
  • LineNotifyService.cs

    public string GetAuthorizationUrl(string name)
    {
        var URL = LineAuthorizeAPI;
        URL += "?response_type=code";
        URL += $"&client_id={_lineNotifySetting.Client_ID}";
        URL += $"&redirect_uri={_lineNotifySetting.Redirect_URI}";
        URL += "&scope=notify";
        URL += $"&state={name}";
        URL += "&response_mode=form_post";
        return URL;
    }
    
  • LineNotifyController.cs

    [HttpGet("{name}")]
    public IActionResult GetAuthorizationUrl(string name)
    {
        var URL = _lineNotifyService.GetAuthorizationUrl(name);
        return Ok(Content(URL));
    }
    

3. 取得AccessToken

授權成功後,將自動導向至服務路徑(redirect_uri),並帶有兩參數codestate。該服務路徑應自動發送Post請求,透過code來取得AccessToken。此範例將取得的AccessToken傳回前台,實務上應將取得的AccessToken存入資料庫。 取得Line AccessToken

  • LineNotifyService.cs

    public async Task<HttpResponseMessage> PostTokenAsync(string code)
    {
        HttpClient client = _httpClientFactory.CreateClient();
        client.Timeout = new TimeSpan(0, 0, 60);
    
        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("grant_type", "authorization_code"),
            new KeyValuePair<string, string>("code", code),
            new KeyValuePair<string, string>("redirect_uri", _lineNotifySetting.Redirect_URI),
            new KeyValuePair<string, string>("client_id", _lineNotifySetting.Client_ID),
            new KeyValuePair<string, string>("client_secret", _lineNotifySetting.Client_Secret)
        });
        return await client.PostAsync(LineTokenAPI, content);
    }
    
  • LineNotifyController.cs

    [HttpPost]
    public async Task<IActionResult> PostTokenAsync([FromBody] LineAuthRes authRes)
    {
        try
        {
            var res = await _lineNotifyService.PostTokenAsync(authRes.Code);
            var msg = await res.Content.ReadAsStringAsync();
            return StatusCode((int)res.StatusCode, msg);
        }
        catch (Exception ex)
        {
            return StatusCode((int)HttpStatusCode.InternalServerError, ex.Message);
        }
    }
    
  • LineAuthRes.cs

    public class LineAuthRes
    {
        public string Code { get; set; } = null!;
        public string State { get; set; } = null!;
    }
    

4. 發送訊息

在Header加上Authorization Bearer <access_token>,以發送Line訊息;認證方式為Bearer,憑證則為使用者的Access Token。

訊息可為一般文字、貼圖或相片,但須注意message參數不可為空。

發送Line訊息

  • LineNotifyService.cs

    public async Task<HttpResponseMessage> SendAsync(string token, LineSendInfo info)
    {
        HttpClient client = _httpClientFactory.CreateClient();
        client.Timeout = new TimeSpan(0, 0, 60);
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    
        var uri = $"{LineNotifyAPI}?message={info.Message}";
        if (info.StickerPackageId != null) uri = $"{uri}&stickerPackageId={info.StickerPackageId}"; 
        if (info.StickerId != null) uri = $"{uri}&stickerId={info.StickerId}";
    
        var content = new MultipartFormDataContent();
        if (info.ImageFile != null)
        {
            if(info.ImageFile.Length > 0)
            {
                using (var ms = new MemoryStream())
                {
                    await info.ImageFile.CopyToAsync(ms);
                    var baContent = new ByteArrayContent(ms.ToArray());
                    content.Add(baContent, "imageFile", info.ImageFile.FileName);
                }
            }
        }
        else
        {
            content = null;
        }
    
        return await client.PostAsync(uri, content);
    }
    
  • LineNotifyController.cs

    [HttpPost("Notify")]
    public async Task<IActionResult> PostMessageAsync(string token, [FromForm] LineSendInfo info)
    {
        try
        {
            var res = await _lineNotifyService.SendAsync(token, info);
            var msg = await res.Content.ReadAsStringAsync();
            return StatusCode((int)res.StatusCode, msg);
        }
        catch (Exception ex)
        {
            return StatusCode((int)HttpStatusCode.InternalServerError, ex.Message);
        }
    }
    
  • LineSendInfo.cs

    public class LineSendInfo
    {
        public string Message { get; set; } = null!;
        public IFormFile? ImageFile { get; set; }
        public string? StickerPackageId { get; set; }
        public string? StickerId { get; set; }
    }
    

5. 撤消AccessToken

  • LineNotifyService.cs

    public async Task<HttpResponseMessage> RevokeToken(string token)
    {
        HttpClient client = _httpClientFactory.CreateClient();
        client.Timeout = new TimeSpan(0, 0, 60);
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    
        return await client.PostAsync(LineRevokeAPI, null);
    }
    
  • LineNotifyController.cs

    [HttpPost("Revoke")]
    public async Task<IActionResult> RevokeAsync(string token)
    {
        try
        {
            var res = await _lineNotifyService.RevokeToken(token);
            var msg = await res.Content.ReadAsStringAsync();
            return StatusCode((int)res.StatusCode, msg);
        }
        catch (Exception ex)
        {
            return StatusCode((int)HttpStatusCode.InternalServerError, ex.Message);
        }
    }
    

參考資料