为什么将文件上传到 Slack 的两种(几乎)相似的方法(使用 c#)会导致不同的结果?

Why do two (almost) similar methods(using c#) of uploading a file to Slack cause different outcomes?

所以这个问题让我很困惑。我将 post 相当多的代码来解释这一点。首先,我有 "old" 版本的代码 (c#),我用它来 post 消息和文件到 Slack。这段代码对我来说很好用!感兴趣的方法如下:

public class PostMessage
    {       
        private string _token = "xoxp-MyToken";
        public string token { get { return _token; } }

        public string channel { get; set; }

        public string text { get; set; }

        public MultipartFormDataContent UploadFile()
        {
            var requestContent = new MultipartFormDataContent();
            var fileContent = new StreamContent(GetFile.ReadFile());
            requestContent.Add(new StringContent(token), "token");
            requestContent.Add(new StringContent(channel), "channels");
            requestContent.Add(fileContent, "file", Path.GetFileName(GetFile.path));

            return requestContent;
        }

    public static class GetFile
    {
        public static string path = @"C:\Users\f.held\Desktop\Held-Docs\Download.jpg";

        public static FileStream ReadFile()
        {            
            FileInfo fileInfo = new FileInfo(path);
            FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
            return fs;
        }
    }

这是我的客户:

    public class SlackClient
    {
        public Uri _method { get; set; }
        private readonly HttpClient _httpClient = new HttpClient {};

        public SlackClient(Uri webhookUrl)
        {
            _method = webhookUrl;
        }

        public async Task<HttpResponseMessage> UploadFileAsync(MultipartFormDataContent requestContent)
        {           
            var response = await _httpClient.PostAsync(_method, requestContent);

            return response;
        }
}

我在这个 Main 中调用所有这些:

    public static void Main(string[] args)
            {
                Task.WaitAll(TalkToSlackAsync());                    

            private static async Task TalkToSlackAsync()
            {

                            var webhookUrl = new Uri("https://slack.com/api/files.upload");
                            var slackClient = new SlackClient(webhookUrl);

                            PostMessage PM = new PostMessage();
                            PM.channel = "DCW21NBHD";

                            var cont = PM.UploadFile();

                            var response = await slackClient.UploadFileAsync(cont);
                            string content = await response.Content.ReadAsStringAsync();
        }          
}

到目前为止,一切顺利!但现在它变得有趣了。我构建了一个类似的版本,其中我使用了 Newtonsoft 的 Json NuGet-package

现在,首先是代码:

客户:

    public async Task<HttpResponseMessage> SendFileAsync(MultipartFormDataContent requestContent)
    {
        _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "xoxp-MyToken");
        var response = await _httpClient.PostAsync(UriMethod, requestContent);

        return response;
    }

同样的Filestram-读取文件的方法:

public class Message
{
    public class GetFile // Just pass a path here as parameter!
    {
        public static string path = @"C:\Users\f.held\Desktop\Held-Docs\Download.jpg";

        public static FileStream ReadFile()
        {
            FileInfo fileInfo = new FileInfo(path);
            FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
            return fs;
        }
    }

我连载的Json-class:

public class JsonObject

    {
            [JsonProperty("file")]
            public string file { get; set; }
            [JsonProperty("channels")]
            public string channels { get; set; }
    }

和主要的:

class MainArea
{
    public static void Main( string[] args)
    {
        try
        {
            Task.WaitAll(SendMessage());
        }
        catch(Exception dudd)
        {
            Console.WriteLine(dudd);
            Console.ReadKey();
        }
    }
    private static async Task SendMessage()
    {
        var client = new BpsHttpClient("https://slack.com/api/files.upload");
        JsonObject JO = new JsonObject();
        JO.channels = "DCW21NBHD";

        var Json = JsonConvert.SerializeObject(JO, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });

        var StringJson = new StringContent(Json, Encoding.UTF8, "multipart/form-data");
        var DeSon = JsonConvert.DeserializeObject(Json);

        Console.WriteLine(DeSon);
        Console.ReadKey();

        var requestContent = new MultipartFormDataContent();
        var fileContent = new StreamContent(Message.GetFile.ReadFile());
        requestContent.Add(fileContent, "file", Path.GetFileName(Message.GetFile.path));
        requestContent.Add(StringJson);

        var ResponseFile = await client.SendFileAsync(requestContent);

        Console.WriteLine(ResponseFile);
        Console.ReadKey();
    }
}

所以,两个 SEEM 都可以工作。但是这些方法中的后者不会 post 文件到声明的频道 - 它只是将它上传到 Slack。这很好,因为我可以使用 'public_url' 在任何渠道上宣传它。但是 - BIG BUT - 使用第一种方法,它会立即将其加载到我的频道!它在我从 Slack 得到的回复中告诉我。两者的响应完全相同——显然除了时间戳和 file_id 等。但是结局不一样! 这里是旧版回复的结尾:

"shares":{"private":{"DCW21NBHD":[{"reply_users":[],"reply_users_count":0,"reply_count":0,"ts":"1544025773.001700"}]}},"channels":[],"groups":[],"ims":["DCW21NBHD"]}}

这是新版本的答案:

"shares":{},"channels":[],"groups":[],"ims":[]}}

现在好了,为什么在上帝的绿色地球上,一种方法可以做到而另一种方法却不能? :D

感谢任何对此有一定见解和知识的人 "issue" 并愿意分享!

files.upload 的文档所述:

Present arguments as parameters in application/x-www-form-urlencoded querystring or POST body. This method does not currently accept application/json.

所以这不起作用的原因是当此方法不支持 JSON 时,您试图提供 API 参数,例如 channels 作为 JSON .结果是忽略了那些属性,这就是为什么图片上传了,但没有分享到指定的频道。

要修复它,只需像在第一个示例中那样提供您的参数作为 application/x-www-form-urlencoded 查询字符串。

请注意,通常只有 Slack API 方法的一个子集支持使用 JSON 来提供所列参数 here。如果要使用 JSON,请仔细检查 API 方法是否支持它,或者坚持使用 x-www-form-urlencoded(这是 POST 的标准)安全的一面。