ASP.NET Web API のテスト用HTML において、enctype="multipart/form-data" なフォームからpostしたところ、「メディアの型 'multipart/form-data' のコンテンツから型 'RequestData' のオブジェクトを読み取るために使用可能な MediaTypeFormatter がありません。」というエラーが出た。

RequestDataは Controller の API の引数として定義しているクラス。

調べてみると、enctype="multipart/form-data" のときは、自分で MediaTypeFormatter を定義し、バインドできるようにする必要があった。

まず、RequestData の定義。
public class RequestDataFile
{
	public byte[] Buffer { get; set; }
	public string FileName { get; set; }
	public string MediaType { get; set; }
}

public class RequestData
{
	public string textdata { get; set; }
	public RequestDataFile filedata { get; set; }
}

そして、以下のような MediaTypeFormatter を継承したクラスを作る。
public class CustomMediaFormatter : MediaTypeFormatter
{
	public CustomMediaFormatter()
	{
		SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/octet-stream"));
		SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data"));
	}

	public override bool CanReadType(Type type)
	{
		return type == typeof(RequestData);
	}

	public override bool CanWriteType(Type type)
	{
		return false;
	}

	public async override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
	{
		if (!content.IsMimeMultipartContent())
		{
			throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
		}

		var ret = new RequestData();

		var Parts = await content.ReadAsMultipartAsync();

		// textdata という名前のパートを探す
		foreach (var Part in Parts.Contents.Where(x => x.Headers.ContentDisposition.DispositionType == "form-data"
			&& x.Headers.ContentDisposition.Name.ToLower() == "\"textdata\""))
		{
			var Data = await Part.ReadAsStringAsync();
			ret.textdata = Data;
			break;
		}

		// filedataという名前のパートを探す
		foreach (var Part in Parts.Contents.Where(x => x.Headers.ContentDisposition.DispositionType == "form-data"
			&& x.Headers.ContentDisposition.Name.ToLower() == "\"filedata\""))
		{
			HttpContent FileContent = Part;
			if (FileContent.Headers.ContentLength > 0)
			{
				var fileInfo = new RequestDataFile();
				fileInfo.FileName = FileContent.Headers.ContentDisposition.FileName;
				fileInfo.MediaType = FileContent.Headers.ContentType.MediaType;
				using (var Imgstream = await FileContent.ReadAsStreamAsync())
				{
					fileInfo.Buffer = new byte[16 * 1024];
					using (var Ms = new MemoryStream())
					{
						int Read;
						while ((Read = Ms.Read(fileInfo.Buffer, 0, fileInfo.Buffer.Length)) > 0)
						{
							Ms.Write(fileInfo.Buffer, 0, Read);
						}
					}
				}
				ret.filedata= fileInfo;
			}
			break;
		}
		
		return ret;
	}
}

最後に App_Start の下にある WebApiConfig クラスの Register メソッドで、上のクラスを登録する。
public static class WebApiConfig
{
	public static void Register(HttpConfiguration config)
	{
		// multipart/form-data のリクエストをバインディングする
		config.Formatters.Add(new CustomMediaFormatter());

		config.Routes.MapHttpRoute(
			name: "DefaultApi",
			routeTemplate: "api/{controller}/{id}",
			defaults: new { id = RouteParameter.Optional }
		);
	}
}

以上です。