Testing 如何使用导水管线束测试文件上载?

Testing 如何使用导水管线束测试文件上载?,testing,dart,aqueduct,Testing,Dart,Aqueduct,我按照导水管教程创建测试,但缺少一个我急需的例子;我无法使用控制器测试文件上载终结点 我已经实现了这样一个控制器: class FileController extends ResourceController { FileController() { acceptedContentTypes = [ContentType("multipart", "form-data")]; } @Operation.post() Future<Response> po

我按照导水管教程创建测试,但缺少一个我急需的例子;我无法使用控制器测试文件上载终结点

我已经实现了这样一个控制器:

class FileController extends ResourceController {

  FileController() {
    acceptedContentTypes = [ContentType("multipart", "form-data")];
  }

  @Operation.post()
  Future<Response> postForm() async {

    final transformer = MimeMultipartTransformer(request.raw.headers.contentType.parameters["boundary"]);
    final bodyStream = Stream.fromIterable([await request.body.decode<List<int>>()]);
    final parts = await transformer.bind(bodyStream).toList();

    for (var part in parts) {
      final headers = part.headers;

      HttpMultipartFormData multipart = HttpMultipartFormData.parse(part);
      final content = multipart.cast<List<int>>();

      final filePath = "uploads/test.txt";

      await new File(filePath).create(recursive: true);

      IOSink sink = File(filePath).openWrite();
      await content.forEach(sink.add);

      await sink.flush();
      await sink.close();
    }

    return Response.ok({});   
  }
}
并在vscode调试器中获取以下信息:

Expected: --- HTTP Response ---
          - Status code must be 200
          - Headers can be anything
          - Body can be anything
          ---------------------
  Actual: TestResponse:<-----------
          - Status code is 415
          - Headers are the following:
            - x-frame-options: SAMEORIGIN
            - x-xss-protection: 1; mode=block
            - x-content-type-options: nosniff
            - server: aqueduct/1
            - content-length: 0
          - Body is empty
          -------------------------
          >
   Which: Status codes are different. Expected: 200. Actual: 415
应为:---HTTP响应---
-状态代码必须为200
-标题可以是任何内容
-身体可以是任何东西
---------------------
实际:测试响应:
其中:状态代码不同。预计:200人。实际:415

415状态代码响应将指示ResourceController已拒绝请求的内容类型。您已经正确设置了
acceptedContentTypes
,但是,
代理的文档中隐藏的测试代理有一个细微差别(无可否认是令人困惑的)。头文件

Default headers to be added to requests made by this agent.

By default, this value is the empty map.

Do not provide a 'content-type' key. If the key 'content-type' is present, it will be removed prior to sending the request. It is replaced by the value of TestRequest.contentType, which also controls body encoding.

See also setBasicAuthorization, bearerAuthorization, accept, contentType for setting common headers.

请参阅API参考。至于为什么会这样:与您的响应一样,TestRequest的内容类型(即使用代理发出请求时创建和执行的对象)决定了CodeRegistry中的哪个编解码器用作编码器。这使您能够始终处理“Dart对象”,并让Aqueduct处理编码/解码。

我编写了一系列类来简化和澄清多部分请求测试。因此,如果有人仍在与此抗争,欢迎尝试我的解决方案:

测试

import'multipart\u body\u parser.dart';
//[...]
test('POST/upload file将文件上载到服务器',()异步{
最终边界='7d82a244f2ea5xd0s046';
最终文件=文件('test.txt');
var encodedBody=MultipartBodyParser(boundary).parse([
文件正文部分(
“文件”,
“test.txt”,
文件('test.txt'),
),
]);
最终响应=等待线束.agent.post(
“/upload file”,
body:encodedBody,
);
预期反应(反应,200);
});
multipart\u body\u parser.dart

导入'dart:convert';
导入“dart:io”;
类MultipartBodyParser{
最终串边界;
MultipartBodyParser(this.boundary)
:断言(
边界!=null,
'边界为空。请设置它'+
'请记住,它不得出现在任何+
'封装零件。示例:“sampleBoundary7da24f2e50046”。',
);
列表获取encodedNonLastBoundary=>
ascii.encode('\r\n--'+boundary+'\r\n');
列表获取encodedLastBoundary=>
ascii.encode('\r\n--'+boundary+'-\r\n\r\n');
列表解析(列表部分){
if(parts==null | | parts.isEmpty){
抛出MultipartBodyParserException(
“零件不能为空。请至少设置车身的一个零件。”,
);
}
var body=encodedNonLastBoundary;
零件。forEach((零件){
body+=part.parse();
如果(parts.last!=零件){
body+=编码的dnonLastBoundary;
}
});
body+=编码的LastBoundary;
返回体;
}
}
类TextBodyPart扩展了\u BodyPart{
最终字符串内容;
TextBodyPart(formFieldName,_内容)
:content=_content??“”,
超级(
_内容配置(
formFieldName,
“表单数据”,
),
_ContentType(),
);
@凌驾
List get encodedContent=>ascii.encode(内容);
}
类FileBodyPart扩展了_BodyPart{
最终文件;
最终字符串文件名;
FileBodyPart(formFieldName、this.fileName、this.file)
:超级(
_内容配置(
formFieldName,
“表单数据”,
“;filename=“$filename””,
),
_ContentType(“应用程序/八位字节流”),
);
@凌驾
List get encodedContent=>file.readAsBytesSync();
}
抽象类_BodyPart{
最终处置内容处置;
最终_contenttypecontenttype;
_BodyPart(this.contentDisposition,this.contentType)
:assert(contentDisposition!=null),
断言(contentType!=null);
字符串get partHeader=>
contentDisposition.toString()+contentType.toString();
列表获取编码内容;
List parse()=>ascii.encode(partHeader)+encodedContent;
}
类_ContentDisposition{
最后一个字符串formFieldName;
最终字符串formFieldType;
最终字符串附加参数;
_ContentDisposition(this.formFieldName、[\u formFieldType、\u additionalParams])
:formFieldType=\u formFieldType???“表单数据”,
additionalParams=_additionalParams??“”,
断言(formFieldName!=null);
@凌驾
字符串toString()=>
'内容处置:$formFieldType;name=“$formFieldName”$additionalParams\r\n';
}
类_ContentType{
最终字符串类型;
_ContentType([this.type='text/plain']):断言(type!=null);
@凌驾
字符串toString()=>'内容类型:$type\r\n\r\n';
}
类MultipartBodyParserException实现异常{
最终字符串消息;
const MultipartBodyParserException([this.message]);
}

谢谢您的回答。我一直在努力解决这个问题已经有一段时间了。似乎
ContentType
类没有为
多部分/表单数据提供属性。那么我应该使用什么属性呢?好的,我可以在测试中做到这一点:
harnese.agent.contentType=contentType(“multipart”,“formdata”)
这消除了我不支持的媒体类型问题,但现在实现失败,并显示另一条错误消息,这可能超出了此问题的范围。我的方法正确吗?
Default headers to be added to requests made by this agent.

By default, this value is the empty map.

Do not provide a 'content-type' key. If the key 'content-type' is present, it will be removed prior to sending the request. It is replaced by the value of TestRequest.contentType, which also controls body encoding.

See also setBasicAuthorization, bearerAuthorization, accept, contentType for setting common headers.