2015-04-23 4 views
5

मेरे वेब एपीआई में, POST एक्शन विधि सर्वर पर एक फ़ाइल अपलोड करता है।HttpContext.Current.Request.Files को पॉप्युलेट करने के लिए फ़ाइल कैसे बनाएं?

HttpRequest request = new HttpRequest("", "http://localhost/", ""); 
    HttpResponse response = new HttpResponse(new StringWriter()); 
    HttpContext.Current = new HttpContext(request, response); 
:

HttpContext.Current.Request.Files 

अब तक मैं इस संहिता जो पूरी तरह से काम करता है के साथ HttpContext faking हूँ:

की इकाई के लिए इस विधि का परीक्षण, मैं बनाने के एक HttpContext और उसके अनुरोध के अंदर एक फ़ाइल लगाने की जरूरत है

ध्यान दें कि मैं Moq या किसी अन्य मॉकिंग लाइब्रेरी का उपयोग नहीं करना चाहता हूं।

मैं इसे कैसे पूरा कर सकता हूं? (MultipartContent शायद?)

धन्यवाद

+0

मैं बदल कर एक ही कोड की कोशिश की फ़ाइल भौतिक पथ के लिए HttpRequest का पहला पैरामीटर, लेकिन नियंत्रक में फ़ाइल नहीं मिल सका। क्या आप समझा सकते हैं कि ऐसा कैसे करें? – Srini

उत्तर

3

आमतौर पर यह एक बुरा व्यवहार वस्तुओं है कि नियंत्रकों में उपहास करने के लिए मुश्किल (HttpContext, HttpRequest, HttpResponse आदि जैसे वस्तुओं) का उपयोग करने के लिए है। उदाहरण के लिए एमवीसी अनुप्रयोगों में हमारे पास ModelBinder और HttpPostedFileBase ऑब्जेक्ट है जिसे हम HttpContext (Web Api एप्लिकेशन के लिए काम करने से बचने के लिए नियंत्रक में उपयोग कर सकते हैं, हमें अपना तर्क लिखने की आवश्यकता है)।

public ActionResult SaveUser(RegisteredUser data, HttpPostedFileBase file) 
{ 
    // some code here 
} 

तो तुम HttpContext.Current.Request.Files के साथ काम करने की जरूरत नहीं है। परीक्षण करना मुश्किल है। उस प्रकार का काम आपके आवेदन के दूसरे स्तर (नियंत्रक में नहीं) में किया जाना चाहिए। Web Api में हम उन उद्देश्यों के लिए MediaTypeFormatter लिख सकते हैं।

public class FileFormatter : MediaTypeFormatter 
{ 
    public FileFormatter() 
    { 
     SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data")); 
    } 

    public override bool CanReadType(Type type) 
    { 
     return typeof(ImageContentList).IsAssignableFrom(type); 
    } 

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

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

     var provider = new MultipartMemoryStreamProvider(); 
     var formData = await content.ReadAsMultipartAsync(provider); 

     var imageContent = formData.Contents 
      .Where(c => SupportedMediaTypes.Contains(c.Headers.ContentType)) 
      .Select(i => ReadContent(i).Result) 
      .ToList(); 

     var jsonContent = formData.Contents 
      .Where(c => !SupportedMediaTypes.Contains(c.Headers.ContentType)) 
      .Select(j => ReadJson(j).Result) 
      .ToDictionary(x => x.Key, x => x.Value); 

     var json = JsonConvert.SerializeObject(jsonContent); 
     var model = JsonConvert.DeserializeObject(json, type) as ImageContentList; 

     if (model == null) 
     { 
      throw new HttpResponseException(HttpStatusCode.NoContent); 
     } 

     model.Images = imageContent; 
     return model; 
    } 

    private async Task<ImageContent> ReadContent(HttpContent content) 
    { 
     var data = await content.ReadAsByteArrayAsync(); 
     return new ImageContent 
     { 
      Content = data, 
      ContentType = content.Headers.ContentType.MediaType, 
      Name = content.Headers.ContentDisposition.FileName 
     }; 
    } 

    private async Task<KeyValuePair<string, object>> ReadJson(HttpContent content) 
    { 
     var name = content.Headers.ContentDisposition.Name.Replace("\"", string.Empty); 
     var value = await content.ReadAsStringAsync(); 

     if (value.ToLower() == "null") 
      value = null; 

     return new KeyValuePair<string, object>(name, value); 
    } 
} 

तो कोई भी सामग्री जो multipart/form-data सामग्री प्रकार के साथ तैनात किया जाएगा (और फ़ाइलों कि सामग्री प्रकार के साथ तैनात किया जाना चाहिए) ImageContentList के बच्चे कक्षा में पार्स किया जाएगा (फाइलों के साथ ताकि आप किसी भी अन्य जानकारी पोस्ट कर सकते हैं) । यदि आप 2 या 3 फाइल पोस्ट करना चाहते हैं - यह भी काम करेगा।

public class ImageContent: IModel 
{ 
    public byte[] Content { get; set; } 
    public string ContentType { get; set; } 
    public string Name { get; set; } 
} 

public class ImageContentList 
{ 
    public ImageContentList() 
    { 
     Images = new List<ImageContent>(); 
    } 
    public List<ImageContent> Images { get; set; } 
} 

public class CategoryPostModel : ImageContentList 
{ 
    public int? ParentId { get; set; } 
    public string Name { get; set; } 
    public string Description { get; set; } 
} 

फिर आप इसे अपने आवेदन में किसी भी नियंत्रक में उपयोग कर सकते हैं। और परीक्षण करना आसान है क्योंकि आपके नियंत्रक का कोड अब HttpContext पर निर्भर नहीं है।

public ImagePostResultModel Post(CategoryPostModel model) 
{ 
    // some code here 
} 

इसके अलावा, आप Web Api विन्यास

configuration.Formatters.Add(new ImageFormatter()); 
6

के लिए MediaTypeFormatter रजिस्टर करने की आवश्यकता मैं अंत में reflection का भारी इस्तेमाल करके WebAPI इकाई परीक्षण के लिए HttpContext को नकली फ़ाइलें जोड़ने के लिए कर रहा था, यह देखते हुए Request.Files के सबसे कि बुनियादी ढांचे को सीलबंद या आंतरिक वर्गों में छुपाया जाता है।

एक बार जब आप नीचे दिए गए कोड जोड़ दिया है, फ़ाइलों HttpContext.Current को अपेक्षाकृत आसानी से जोड़ा जा सकता है:

var request = new HttpRequest(null, "http://tempuri.org", null); 
AddFileToRequest(request, "File", "img/jpg", new byte[] {1,2,3,4,5}); 

HttpContext.Current = new HttpContext(
    request, 
    new HttpResponse(new StringWriter()); 
बड़े कार्य करने के साथ

द्वारा किया:

static void AddFileToRequest(
    HttpRequest request, string fileName, string contentType, byte[] bytes) 
{ 
    var fileSize = bytes.Length; 

    // Because these are internal classes, we can't even reference their types here 
    var uploadedContent = ReflectionHelpers.Construct(typeof (HttpPostedFile).Assembly, 
     "System.Web.HttpRawUploadedContent", fileSize, fileSize); 
    uploadedContent.InvokeMethod("AddBytes", bytes, 0, fileSize); 
    uploadedContent.InvokeMethod("DoneAddingBytes"); 

    var inputStream = Construct(typeof (HttpPostedFile).Assembly, 
     "System.Web.HttpInputStream", uploadedContent, 0, fileSize); 

    var postedFile = Construct<HttpPostedFile>(fileName, 
      contentType, inputStream); 
    // Accessing request.Files creates an empty collection 
    request.Files.InvokeMethod("AddFile", fileName, postedFile); 
} 

public static object Construct(Assembly assembly, string typeFqn, params object[] args) 
{ 
    var theType = assembly.GetType(typeFqn); 
    return theType 
     .GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, 
      args.Select(a => a.GetType()).ToArray(), null) 
     .Invoke(args); 
} 

public static T Construct<T>(params object[] args) where T : class 
{ 
    return Activator.CreateInstance(
     typeof(T), 
     BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, 
     null, args, null) as T; 
} 

public static object InvokeMethod(this object o, string methodName, 
    params object[] args) 
{ 
    var mi = o.GetType().GetMethod(methodName, 
      BindingFlags.NonPublic | BindingFlags.Instance); 
    if (mi == null) throw new ArgumentOutOfRangeException("methodName", 
     string.Format("Method {0} not found", methodName)); 
    return mi.Invoke(o, args); 
} 
+0

मुझे ReflectionHelpers क्लास के लिए नेमस्पेस नहीं मिला। क्या मुझे पता चलेगा कि इस नाम के लिए नामस्थान या तृतीय पक्ष लाइब्रेरी का उपयोग करना चाहिए? – Srini

+0

अपोल - नीचे स्थैतिक विधियां प्रतिबिंब हेल्पर नामक स्थिर वर्ग में थीं। यदि आप एक ही कक्षा में सभी विधियों को डालते हैं तो आप प्रतिबिंब हेल्पर नेमस्पेस को पूरी तरह से छोड़ सकते हैं, या आप उन्हें अपनी कक्षा में दोबारा कर सकते हैं। – StuartLC

संबंधित मुद्दे