Simulating a file upload in form data using MvcIntegrationTestingFramework - asp.net-mvc

I am using Steve Sanderson's MvcIntegrationTestingFramework and trying to simulate a post to an upload action. The second parameter is an object that is then converted into the name value pairs of the form data to be posted.
My test code is:
[Test]
[Category("integration")]
public void UploadRunsCorrectlyWhenConfiguredFromApplicationStart()
{
var appHost = AppHost.Simulate("ingester");
appHost.Start(session =>
{
var result = session.Post("upload/upload",
new
{
id = Guid.NewGuid().ToString(),
file = ***Help here ***
});
Assert.That(result.Response.StatusCode == 202);
} );
}
The action is:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Upload(string id, HttpPostedFileBase file)
{
//Need file to not be null here
}
My question is what should I put in Help here to have the file in action not appear as null?
Thanks :)

Related

Troubleshoot MVC model binding failure - argument is null in controller

I am trying to POST an object from a WebJob to an MVC 4 controller. I am using Entity Framework. In the controller, I cannot get the object to bind properly (the argument is null). I have looked at many tutorials and it seems like my code should work.
Model (does this need to be in a specific namespace for EF to find it?):
public class CreateListingObject
{
public Listing listing;
public List<GalleryImage> images;
public CreateListingObject()
{
listing = new Listing();
images = new List<GalleryImage>();
}
}
public struct GalleryImage
{
public string picURL;
public string caption;
}
POST:
public void PostListing(CreateListingObject o)
{
Console.WriteLine("Posting listing: {0}", o.listing.Title);
HttpClient _httpClient = new HttpClient();
Uri uri = new Uri(_serviceUri, "/Automaton/CreateTestListing");
string json = BizbotHelper.SerializeJson(o);
HttpResponseMessage response = BizbotHelper.SendRequest(_httpClient, HttpMethod.Post, uri, json);
string r = response.Content.ReadAsStringAsync().Result;
response.EnsureSuccessStatusCode();
}
SendRequest (thank you Azure search samples):
public static HttpResponseMessage SendRequest(HttpClient client, HttpMethod method, Uri uri, string json = null)
{
UriBuilder builder = new UriBuilder(uri);
//string separator = string.IsNullOrWhiteSpace(builder.Query) ? string.Empty : "&";
//builder.Query = builder.Query.TrimStart('?') + separator + ApiVersionString;
var request = new HttpRequestMessage(method, builder.Uri);
if (json != null)
{
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
}
return client.SendAsync(request).Result;
}
Controller Action fragment (o is an empty object here):
[HttpPost]
public ActionResult CreateTestListing(CreateListingObject o)
{
Listing li = o.listing;
I have confirmed that if I post a simple object using the same code, everything works as expected.
Instead of sending a CreateListingObject in PostListing, I send this instead:
var test = new
{
data = "hi mom"
};
And change my action to, then the argument gets bound and I get valid data:
[HttpPost]
public ActionResult CreateTestListing(string data)
{
I have also checked the serialization of my CreateListingObject in the WebJob, and it is fully populated as I expect. This leads me to suspect that I am falling afoul of the default ModelBinder.

Does not contain a definition for 'AddToPosts' and no extension method

I am still learning ASP.NET/MVC and following along a tutorial by Matt Blagden (You Tube) on building a Blog..
After making the Post Controller I am getting an error in a snippet of code that handles adding a Tag to a Post if it is a new Post. My thought is the method AddToPosts should have been generated when I added a new Entity Data Model.
The error is:
blog.Models.BlogModel does not contain a definition for 'AddToPosts' and no extension method 'AddToPosts'. PostsController.cs accepting a first argument of type 'blog.Models.BlogModel' could be found (are you missing a using directive or an assembly reference?)
The portion of code that is giving me the error is:
if (!id.HasValue)
{
model.AddToPosts(post);
}
Here is the entire Post Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using blog.Models;
using System.Data.EntityModel;
using System.Text;
namespace blog.Controllers
{
public class PostsController : Controller
{
// Access Model
private BlogModel model = new BlogModel();
public ActionResult Index()
{
return View();
}
// A way to input sample data into database
[ValidateInput(false)]
public ActionResult Update(int? id, string title, string body, etc.....)
{
//IF not an admin redirect back to index
if (!IsAdmin)
{
return RedirectToAction("Index");
}
// Get Post
Post post = GetPost(id);
// Populate simple properties
post.Title = title;
post.Body = body;
post.DateTime = dateTime;
post.Tags.Clear(); // first clear tag list
//ensure input sequence of tag names is not null
tags = tags ?? string.Empty;
//Split the sequence into a list of tag names
string[] tagNames = tags.Split(new char[] { ' ' }, etc...
//Get or create each tag that was named
foreach (string tagName in tagNames)
{
//Add the tag
post.Tags.Add(GetTag(tagName));
}
if (!id.HasValue)
{
model.AddToPosts(post);
}
model.SaveChanges();
return RedirectToAction("Details", new { id = post.ID });
}
// PROMPTS USER TO INPUT DATA FOR DATABASE
public ActionResult Edit(int? id)
{
Post post = GetPost(id);
//ACCUMULATES LIST OF CURRENT TAGNAMES
StringBuilder tagList = new StringBuilder();
foreach (Tag tag in post.Tags)
{
//Append each tagname
tagList.AppendFormat("{0} ", tag.Name);
}
//Gives the tagList to the view
ViewBag.Tags = tagList.ToString();
return View(post);
}
private Tag GetTag(string tagName)
{ // if tag is set then Get the Tag, if not create a new one (Just like GetPost)
return model.Tags.Where(x => x.Name == tagName).FirstOrDefault() ?? etc....
}
private Post GetPost(int? id)
{
// IF id is set then Get the Post, if not make a new one..
return id.HasValue ? model.Posts.Where(x => x.ID == id).First() : etc.....
// TODO: don't just return true
public bool IsAdmin
/* READS SESSION STATE */
{
get { return true; /*{ return Session["IsAdmin"] != null && etc...
}
}
}
If "AddToPosts" coming from static class, try adding using statement of namespace where the Add post class exists.

Rotativa Download with SaveAs dialog

I am using Rotativa tool to display pdf. It works fine with the following code:
public ActionResult PreviewDocument()
{
var htmlContent = Session["html"].ToString();
var model = new PdfInfo { Content = htmlContent, Name = "PDF Doc" };
return new ViewAsPdf(model);
}
I wanted to know the way to download the pdf via browser's "save as" dialog on clicking on a button and not to display in some iframe. "new ViewAsPdf(model)" just returns the pdf data.
Thanks in advance.
You can add additional attributes to the Rotativa call like this:
return new PartialViewAsPdf("PreviewDocument", pdfModel)
{
PageSize = Size.A4,
FileName = "PDF Doc.pdf"
};
And it'll create the file for you. :)
I finally got a way to do this.
Actually rotativa's method "return new ViewAsPdf(model)" returns HttpResponseStream. Where we can hardly do something. But we can modify/alter the response once the action get executed by using custom attribute. We can override OnResultExecuted() method of action filter.
Controller's action
[HttpGet]
[ActionDownload] //here a custom action filter added
public ActionResult DownloadDocument()
{
var htmlContent = "<h1>sachin Kumar</hi>";
var model = new PdfInfo {FtContent = htmlContent, FtName = "Populate Form"};
return new ViewAsPdf(model);
}
Custom Action filter:
public class ActionDownloadAttribute : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
//Add content-disposition header to response so that browser understand to download as an attachment.
filterContext.HttpContext.Response.AddHeader("content-disposition", "attachment; filename=" + "Report.pdf");
base.OnResultExecuted(filterContext);
}
}
You can use return new ActionAsPdf. No custom attributes or anything else required.
Example: https://github.com/webgio/Rotativa/
public ActionResult PrintPreviewDocument()
{
return new ActionAsPdf("PreviewDocument") { FileName = "PDF Doc.pdf" };
}
public ActionResult PreviewDocument()
{
var htmlContent = Session["html"].ToString();
var model = new PdfInfo { Content = htmlContent, Name = "PDF Doc" };
return View(model);
}

ASP.NET MVC Form Submission

I'm having a bit of a brain fart; it must be Monday...
I have an MVC form, which allow the user to submit an image.
The image is saved to a folder, then I want to redirect to another Controller and Action to display the image.
What are my options for passing the image name and path back to the controller action to display the graphic?
// Handles the updload, contains a control (ascx)
// and the control's action method is in another controller
public ActionResult Index()
{
return View();
}
// I want this page to display the image uploaded in the Upload.ascx control
// that is in the index method above:
public ActionResult Result()
{
ViewData["MyImage"] = ???
}
Thanks much.
Where is the image being stored? In your content area or in a database? If it's in a database, then I'd construct the controller/action url to display that image form the db. If it's in your content area, then you can construct the url based on the name of the uploaded file. I'd probably create a model rather than passing the url in view data, but view data is a valid (i.e., it works) alternative.
public ActionResult Result( int id ) // db storage
{
return View( new UploadModel
{
ImageUrl = Url.Action( "display", "image", new { id = id }
} );
}
public ActionResult Result() // content area
{
var imageName = ... get image name from ??? ...
return View( new UploadModel
{
ImageUrl = Url.Content( "~/content/images/uploads/" + iamgeName );
});
}

ASP.NET MVC: Server Validation & Keeping URL paramters when returning the view

I currently have the following code for the POST to edit a customer note.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditNote(Note note)
{
if (ValidateNote(note))
{
_customerRepository.Save(note);
return RedirectToAction("Notes", "Customers", new { id = note.CustomerID.ToString() });
}
else
{
var _customer = _customerRepository.GetCustomer(new Customer() { CustomerID = Convert.ToInt32(note.CustomerID) });
var _notePriorities = _customerRepository.GetNotePriorities(new Paging(), new NotePriority() { NotePriorityActive = true });
IEnumerable<SelectListItem> _selectNotePriorities = from c in _notePriorities
select new SelectListItem
{
Text = c.NotePriorityName,
Value = c.NotePriorityID.ToString()
};
var viewState = new GenericViewState
{
Customer = _customer,
SelectNotePriorities = _selectNotePriorities
};
return View(viewState);
}
}
If Validation fails, I want it to render the EditNote view again but preserve the url parameters (NoteID and CustomerID) for something like this: "http://localhost:63137/Customers/EditNote/?NoteID=7&CustomerID=28"
Any ideas on how to accomplish this?
Thanks!
This action is hit by using a post. Wouldn't you want the params to come through as part of the form rather than in the url?
If you do want it, I suppose you could do a RedirectToAction to the edit GET action which contains the noteId and customerId. This would effectively make your action look like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditNote(Note note)
{
if (ValidateNote(note))
{
_customerRepository.Save(note);
return RedirectToAction("Notes", "Customers", new { id = note.CustomerID.ToString() });
}
//It's failed, so do a redirect to action. The EditNote action here would point to the original edit note url.
return RedirectToAction("EditNote", "Customers", new { id = note.CustomerID.ToString() });
}
The benefit of this is that you've removed the need to duplicate your code that gets the customer, notes and wotnot. The downside (although I can't see where it does it here) is that you're not returning validation failures.

Resources