C#向JAVA发送form-data文件问题处理方案

前言

和上一篇文章一样,.NET 和 JAVA之间的接口请求又遇到了新问题

我们有一个用来接收文件的接口,外部把文件流、文件名、目录,传进来,我们系统把生成的附件ID反回去,接口为POST-form-data格式

我用POSTMAN调用没有问题,就把文档写好发出去了,结果.NET开发的兄弟就又进行不下去了

问题现象

一模一样的参数,POSTMAN里请求正常,C#请求就返回空,翻了一下我们的日志,

NullPointerException 这。。。也不知道是哪抛出来的,本来就是原有的接口,逻辑一大堆,只看日志也看不出个所以然。

而且客户的环境我还没办法远程debug,我本地环境又没办法让对方开发调。

于是乎我和他们远程会议一点点猜他代码的问题,该加的参数都加了,找不出来哪有毛病。

处理过程

最后本着能让他们改,我自己就不动的原则,我开始研究起来了C#,之前基本没怎么接触过,从准备环境开始。。。

详细的环境准备过程请看:

//TODO C#环境准备

因为本来就不怎么会写么,况且POSTMAN能调通,于是准备直接用POSTMAN生成的代码来测,就得到了这个:

    var client = new HttpClient();
    var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:8080/api/doc/upload/uploadFile2Doc");
    
    request.Headers.Add("appid", "CRM");
    request.Headers.Add("token", "a3c6e10f-2269-4851-a3e4-3bb7bb1df183");
    request.Headers.Add("userid", "EGqrxYColwBVopfkbVn/jUI7n9fPkRvcwnMh5LbNLuCkykywI902H3F98wvwlY8CWeHKXuiwLR8DmsCmupjcD1x79kMsNVkJOiq5N4y4Dj+CaFcelvdL+Nypgho3Sj4dPc/hRkbdXnvf4iAh8LR7v22pQOKuHm8I7ZclLID87JGKlsEs+X64CtJEJtTGh6js4PDBR5zWFB8NwxmcJHq4M26RNWeZVp7Tz93W5qv3WvgROUuuK7cla5gu8GgwqSJAQlti2VuNJpLIgTRNsB7UQeYCrGYNOTAU0JzDhAi+A3t7hA4eoWHb0+na/SLgR6sQrGzLk4KpO6rkU7PVCrHkjg==");
    
    var content = new MultipartFormDataContent();
    content.Add(new StreamContent(File.OpenRead("/C:/Users/13064/工作/测试文件/测试表格.xlsx")), "file", "/C:/Users/13064/工作/测试文件/测试表格.xlsx");
    content.Add(new StringContent("测试表格.xlsx""), "name");
    request.Content = content;
    
    var response = await client.SendAsync(request);
    response.EnsureSuccessStatusCode();
    Console.WriteLine(await response.Content.ReadAsStringAsync());
    

不出所料,还是返回了个{}

不过好在是自己的环境,Debug,启动!

一层一层的找,发现这个NullPointerException 是因为拿到的file为null,于是我天真的揣测是不是header没有指定类型的问题

于是天真的加上了

    content.Headers.ContentType = new MediaTypeHeaderValue("multipart/form-data")

果不其然,报错变了

Separation boundary was not specified

这是个什么东西,之前没见过,简单搜了一下,表明 multipart/form-data 请求中缺少边界(boundary)信息

然后文档里还写 :MultipartFormDataContent确保你没有在请求中手动设置 Content-Type,因为 C# 会自动添加边界

好吧,画蛇添足了属于是

继续从JAVA侧入手吧,看看为什么file为null,继续深入,发现file不是没拿到,而是到file这个参数的时候出问题了,但是异常并没有抛出

直接在里面catch掉之后return了个null

java.lang.RuntimeException: >>>>Xss(NoPass),invalidChar in params:--->referer:null path:/api/doc/upload/uploadFile2Doc param:=?utf-8?B?5rWL6K+V6KGo5qC8Lnhsc3g=?= rule=(the value is too big!(1000000)) paramValue:PK

值超长?其实这会就该想到为什么了,但是没反应过来,新建了个TXT传,心想肯定不会超长了吧,结果倒是没超长,但是file还是没拿到

仔细看了下,发现file参数被当作了个普通param来处理的,怪不得文件大一点就超长了

既然这种请求方式JAVA侧拿不到正确的参数类型,那就换一种,于是就从POSTMAN又得到了这段代码

    var options = new RestClientOptions("http://localhost:8080")
    {
      MaxTimeout = -1,
    };
    
    var client = new RestClient(options);
    var request = new RestRequest("/api/doc/upload/uploadFile2Doc", Method.Post);
    request.AddHeader("appid", "CRM");
    request.AddHeader("token", "a3c6e10f-2269-4851-a3e4-3bb7bb1df183");
    request.AddHeader("userid", "EGqrxYColwBVopfkbVn/jUI7n9fPkRvcwnMh5LbNLuCkykywI902H3F98wvwlY8CWeHKXuiwLR8DmsCmupjcD1x79kMsNVkJOiq5N4y4Dj+CaFcelvdL+Nypgho3Sj4dPc/hRkbdXnvf4iAh8LR7v22pQOKuHm8I7ZclLID87JGKlsEs+X64CtJEJtTGh6js4PDBR5zWFB8NwxmcJHq4M26RNWeZVp7Tz93W5qv3WvgROUuuK7cla5gu8GgwqSJAQlti2VuNJpLIgTRNsB7UQeYCrGYNOTAU0JzDhAi+A3t7hA4eoWHb0+na/SLgR6sQrGzLk4KpO6rkU7PVCrHkjg==");
    request.AddHeader("Cookie", "ecology_JSessionid=aaah7nfm2JXcVfjSBTajz; languageidweaver=7; loginidweaver=1");
    request.AlwaysMultipartFormData = true;
    request.AddFile("file", "/C:/Users/13064/工作/测试文件/测试表格.xlsx");
    request.AddParameter("name", "测试表格.xlsx");
    RestResponse response = await client.ExecuteAsync(request);
    Console.WriteLine(response.Content);

调用一下,发现这次JAVA这边连debug都没进,而且C#这边也什么都没打出来

简单研究了一下RestResponse的用法

加上了如下代码

    if (response.IsSuccessful)
    {
        Console.WriteLine("请求成功:");
        Console.WriteLine(response.Content);
    }
    else
    {
        Console.WriteLine("请求失败:");
        Console.WriteLine($"状态码: {response.StatusCode}");
        Console.WriteLine($"错误信息: {response.ErrorMessage}");
        Console.WriteLine($"响应内容: {response.Content}");
    }

状态码: NotAcceptable

继续添加

    request.AddHeader("Accept", "*/*");

再看JAVA侧,终于file是个文件了,回看C#也已经正确拿到了返回数据

完整代码

为了方便扔给那边的兄弟做测试,简单优化了下

    using RestSharp;
    using Newtonsoft.Json.Linq;
    
    
    string uri = "http://127.0.0.1:8080";
    
    var json = await GetToken();
    var jsonObject = JObject.Parse(json);
    string token = (string)jsonObject["token"];
    var options = new RestClientOptions(uri)
    {
        MaxTimeout = -1,
    };
    var client = new RestClient(options);
    var request = new RestRequest("/api/doc/upload/uploadFile2Doc", Method.Post);
    request.AddHeader("appid", "CRM");
    request.AddHeader("token", token);
    request.AddHeader("userid", "zOumGpDpL6azXvv/DBS/V3wxmqRxkJg3G1iI20RKtks6PcPTEykqJBAgwze9xT4DSUnaMQdYev3BiAFEX1DpsFoDYKe5LYuXf8h0o/KD86HE4DqzlfrNhwr4bFrbT+YZrONxc+hzx8m1QsqdwZMosrPnSbM0xQEwwMr/TAg3JRQmgDEa+OggqYupEaVfVIJklJEdtX6g2Dfor+WX79aCnKphZxp/N62zi/w6iBusSbwtAADN8Ind61YBZq/+yoi9E/FlkENRTv3tTwNAA59CHYbYZnpvIEW7XddefPTx9JVWtPN40kueNBLTdbsz41HuHOIcMFdaEGTTnA6hYUWllg==");
    request.AddHeader("Accept", "*/*");
    
    request.AlwaysMultipartFormData = true;
    request.AddFile("file", "./t.txt");
    request.AddParameter("name", "t.txt");
    RestResponse response = await client.ExecuteAsync(request);
    
    if (response.IsSuccessful)
    {
    
        Console.WriteLine("请求成功:");
        Console.WriteLine(response.Content);
    }
    else
    {
        Console.WriteLine("请求失败:");
        Console.WriteLine($"状态码: {response.StatusCode}");
        Console.WriteLine($"错误信息: {response.ErrorMessage}");
        Console.WriteLine($"响应内容: {response.Content}");
    }
    
    
    
    async Task<string> GetToken()
    {
    
        var options = new RestClientOptions(uri)
        {
            MaxTimeout = -1,
        };
        var client = new RestClient(options);
        var request = new RestRequest("/api/ec/dev/auth/applytoken", Method.Post);
        request.AddHeader("appid", "CRM");
        request.AddHeader("secret", "hqHTV4Bf0/AN74M+FXxWs3v2GxxYrQwGoE+ioUOkFttFAFMGAR55JAzDeUkxN5MUwkHy+ovu2Fh2NtEoQh396dIAkdUdHiqzvrxHAIaHPfNrcUtAFVkcqRAKOsVLBVSAR7tMTxw5KkEqYI4mV/pMZeZB5tKK08nUFOi5u06xUwB8AS/jo8CYfuAFJ8xAJWld6bjGnNW6nJXWXam1lbMsD9RUZWJ/RHSzrISDCmNauG+ejpNuB8A4uyAaY7kEIx723ZU2HkczExfQXxwtEjCwS8elj8bQp39j+g8bYlL2O6fP2GDlWhYhzorGgzNc6MYBZFaQ2vDgP3KCP9NX9JlhSQ==");
        request.AddHeader("Accept", "*/*");
        RestResponse response = await client.ExecuteAsync(request);
        return response.Content;
    }

PS:C#语法确实比JAVA写起来方便,psvm都没有就能用,而且比较有意思的是编译之后直接就能生成exe,之前我那JAVA写的东西想给别人直接双击启动 还得自己写bat,或者用exe4j,后面可以好好研究研究。

posted @ 2024-12-06 14:02  地球上的张先生  阅读(7)  评论(0编辑  收藏  举报