【问题标题】:How do I post simple JSON data with a file upload?如何通过文件上传发布简单的 JSON 数据?
【发布时间】:2019-05-21 20:22:31
【问题描述】:

我正在尝试在 ServiceStack TypeScript 客户端中设置文件上传请求,该客户端还包括与文件相关的月份。如何设置请求以使两者都通过服务器?

我尝试了各种更改,包括手动更改标头以尝试强制 Content-Type 为 application/json,但这不起作用(但我怀疑即使这样做也会破坏文件上传)。

客户端 API:

export const serviceApi = {
    importData: (month: string, file: File) => {
        var client = new JsonServiceClient("");
        var request = new DTOs.ImportData();

        // At this point, the month has a value
        request.month = month.replace('/', '-').trim();

        let formData = new FormData();
        formData.append('description', file.name);
        formData.append('type', 'file');
        formData.append('file', file);

        const promise = client.postBody(request, formData);
        return from(promise);
    },
};

DTO 定义:

[Route("/api/data/import/{Month}", "POST")]
public class ImportData : IReturn<ImportDataResponse>
{
    public string Month { get; set; }
}

public class ImportDataResponse : IHasResponseStatus
{
    public ResponseStatus ResponseStatus { get; set; }
}

服务器端 API:

[Authenticate]
public object Post(ImportData request)
{
    if (Request.Files == null || Request.Files.Length <= 0)
    {
        throw new Exception("No import file was received by the server");
    }

    // This is always coming through as null
    if (request.Month == null)
    {
        throw new Exception("No month was received by the server");
    }

    var file = (HttpFile)Request.Files[0];
    var month = request.Month.Replace('-', '/');

    ImportData(month, file);

    return new ImportDataResponse();
}

我可以看到文件在服务器端正确通过,并且我可以看到 HTTP 请求通过查询字符串参数中设置为“07-2019”的月份,但是当我中断服务器时-side API函数,请求的month属性为null。

更新,这里是 HTTP 请求/响应标头:

请求标头

POST /json/reply/ImportData?month=07-2019 HTTP/1.1
Host: localhost:40016
Connection: keep-alive
Content-Length: 7366169
Origin: http://localhost:40016
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryI8CWlbw4tP80PkpZ
Accept: */*
Referer: http://localhost:40016/data
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Cookie: _ga=GA1.1.673673009.1532913806; ASP.NET_SessionId=gtwdk3wsvdn0yulhxyblod3g; __utmc=111872281; __utmz=111872281.1533684260.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); ss-opt=perm; __utma=111872281.673673009.1532913806.1550789161.1550794391.20; _gid=GA1.1.893581387.1558389301; ss-id=kfq4G0GYb3WldSdCaRyJ; ss-pid=aZ400sqM4n3TQgNVnHS2

响应标头

HTTP/1.1 500 Exception
Cache-Control: private
Content-Type: application/json; charset=utf-8
Vary: Accept
Server: Microsoft-IIS/10.0
X-Powered-By: ServiceStack/5.10 NET45 Win32NT/.NET
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RTpcVEZTXFNvdXJjZVxNZWRpc2VuXFdlYnNpdGVzXE9OaWlDU1xNYWluXFNvdXJjZVxPbmlpY3NSZWFjdC1QYXltZW50c1xPbmlpY3NSZWFjdFxPbmlpY3NSZWFjdFxqc29uXHJlcGx5XEltcG9ydE1CU0NvZGVz?=
X-Powered-By: ASP.NET
Date: Tue, 21 May 2019 21:49:03 GMT
Content-Length: 605

查询字符串参数

month=07-2019

【问题讨论】:

    标签: c# typescript servicestack


    【解决方案1】:

    您可以直接使用JavaScript's fetch API 上传文件,例如:

    let formData = new FormData();
    formData.append('description', file.name);
    formData.append('type', 'file');
    formData.append('file', file);
    
    fetch('/api/data/import/07-2019', {
        method: 'POST',
        body: formData
    });
    

    否则,如果您想使用 ServiceStack 的 TypeScript JsonServiceClient,则需要使用 API 来发布带有单独请求正文的请求 DTO,例如:

    formData.append('month', '07-2019');
    client.postBody(new ImportData(), formData);
    

    【讨论】:

    • 我很高兴使用 fetch,但为了与我的其他 API 保持一致,我想尝试让 JsonServiceClient 工作。您的第二段代码看起来很像我的(请求只是在行内而不是在变量中声明)。你能详细说明一下有什么区别吗?
    • @Mortdestro 没有区别,v5.5 all TypeScript DTOs support partial constructors 只是一种更简洁的填充 DTO 的方式。
    • 如果没有区别,那么该方法似乎不起作用。我会尝试 fetch 看看是否可以解决它。谢谢!
    • Fetch 确实有效,我很乐意使用它,但您知道为什么 JsonServiceClient 方法无效吗?
    • 说得太早了!请求成功返回,但服务器端没有发生任何事情。它甚至不会命中 API 函数中的断点
    【解决方案2】:

    我认为月份不应该是请求标头的一部分,这有点不正统。它应该是表单数据的一部分。

    如果你这样做了:

      formData.append('Month', month.replace('/', '-').trim());
    

    客户端,然后 request.Month 或 request.content.Month 应该可以工作,具体取决于您的实例中处理请求对象的方式。

    【讨论】:

    • 感谢您的建议。如果这是获取数据的唯一方式,我很乐意这样做,但在我所有的服务 API 中,我一直在利用 ServiceStack 的自动生成类型来保持客户端和服务器端之间的请求一致。我相信这通常作为请求有效负载通过,但这次似乎将其放入查询字符串中,我觉得这很奇怪。
    • @Mortdestro 啊,我明白了。我认识的大多数 webdevs 都会说 html 标头仅用于传输信息,而不是语义。另外,请记住,自定义标头也可能导致基于客户端实现的问题,例如,一些代理和防火墙会检查和重写标头,移动服务提供商也有修改标头的历史,尤其是在英国跨度>
    • 很高兴知道这一点。我不相信我现在正在创建任何自定义标头(我曾经尝试过,但目前我依靠 ServiceStack 为我生成 HTTP 请求)。我只是觉得有点疯狂,看起来我设置正确,但服务器没有获取我的数据。
    • 哦,对不起,我读错了请求对象,虽然它是标头,而不是 URI。
    • 嗯,你的建议确实解决了我的问题。这不是我理想的解决方案,但看起来 JsonServiceClient 存在某种类型的错误以及这些类型的请求,这种请求不应该发出,或者我在代码中的其他地方遗漏了导致要求不可靠。另一个回答者只是提出了同样的建议,所以我认为这是最好的方法。谢谢!
    猜你喜欢
    • 2020-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-28
    • 2013-09-11
    • 2012-10-23
    • 1970-01-01
    相关资源
    最近更新 更多