简体   繁体   中英

Asp.net MVC - Model Binding Only Working with Primitive Values

I am building a web application that will be storing data from a form in a database. My initial approach was to create a custom model/class, and have the POST data bind to it. This was an approach I had used before successfully. However, this time it simply would not work. No matter I did, I could not get the values to bind to the property of the object (ie model.Title), but only primitive values (ie String title). In frustration, I eventually just decided to bind the data to primitives.

However, while most of the data is now correctly bound, I have ran into another difficulty - the data is not being bound to the HttpPostedFileBase object. Initially, I had assumed there might have been something wrong with my custom class. This no longer appears to be the case, instead the Model Binder no longer seems able to bind correctly to any object, only primitives.

My View

@model StoryWall.ViewModels.AddStoryViewModel
@{
    ViewBag.Title = "Add New Story";
}



<form  method="post" enctype="multipart/form-data" name="addStoryForm" action="Add/SubmitStory" novalidate class="form-horizontal">
    @Html.AntiForgeryToken()


    <div class="form-group"><label class="control-label col-sm-2">Story Title</label><div class="col-sm-10"><input type='text' class="form-control" ng-model="Title" name="PostTitle" required /> <span class="text-danger" ng-show="addStoryForm.model.PostTitle.$touched && addStoryForm.model.PostTitle.$invalid">Title is required.</span></div></div>

    <div class="form-group"><label class="control-label col-sm-2">Story</label><div class="col-sm-10"><textarea class="form-control" ng-model="Body" name="PostBody" required> </textarea> <span class="text-danger" ng-show="addStoryForm.PostBody.$touched && addStoryForm.PostBody.$invalid">Field is required.</span></div></div>



    <div class="form-group"><label class="control-label col-sm-2">Store / Location </label><div class="col-sm-10"><select name="StoreID" class="form-control" ng-model="Store" required > <option value="">Select...</option> 
        @foreach (var store in @Model.stores)
        {
            <option value="@store.StoreID">@store.StoreName</option>
        }
        </select>
         <span class="text-danger" ng-show="addStoryForm.StoreID.$touched && addStoryForm.StoreID.$invalid">Please select a store.</span></div></div>

    <div class="form-group"><label class="control-label col-sm-2">Add a picture (optional)</label><div class="col-sm-10"><input type="file" class="form-control" name="StoryImg"></div></div>

    <div class="form-group"><label class="control-label col-sm-2">Your name</label><div class="col-sm-10"><input type='text' class="form-control" ng-model="Author" name="AuthorName" required /> <span class="text-danger" ng-show="addStoryForm.AuthorName.$touched && addStoryForm.AuthorName.$invalid">Please enter your name.</span></div></div>

    <div class="form-group"><label class="control-label col-sm-2">Your email</label><div class="col-sm-10"><input type='email' class="form-control" ng-model="Email" name="AuthorEmail" required /> <span class="text-danger" ng-show="addStoryForm.AuthorEmail.$dirty && addStoryForm.AuthorEmail.$invalid">Please enter your email.</span></div></div>


    <div ng-controller="TagsCtrl" class="form-group">
        <label class="control-label col-sm-2">Tags (separate with a comma) {{tags.text}}</label>
        <tags-input on-tag-added="updateInput()" ng-model="tags"></tags-input>

            <div class="col-sm-10"><input type='text' class="form-control" ng-model="input.currText" id="tags" name="TagText" /> </div>

    </div>

    <button type="submit" class="btn btn-primary" ng-disabled="addStoryForm.$invalid">Submit</button>
</form>

The Controller responsible for generating the view above and accepting the POST data:

  public class AddController : Controller
{

    StoryModel dbContext = new StoryModel();

    public ActionResult Index()
    {
      AddStoryViewModel vm = new AddStoryViewModel();
        vm.stores = dbContext.Stores.OrderBy(s => s.StoreName).ToList();

        return View("Index", vm);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult SubmitStory(String TagText, String PostBody, String PostTitle, int StoreID, String AuthorEmail, String AuthorName, HttpPostedFile StoryImg) 
    {

            Story newStory = new Story();

            // Create new tag if necessary
            String[] tags = TagText.Split(',');

            if (tags.Length > 0) {
                for (int i = 0; i < tags.Length; i++)
                {
                    String tagText = tags[i].ToLower();
                    Tag tag = dbContext.Tags.FirstOrDefault(t => t.TagName.ToLower() == tagText);
                    if (tag != null)
                    {
                        newStory.Tags.Add(tag);
                    }
                    else
                    {
                        Tag newTag = new Tag();
                        newTag.TagName = tags[i].ToLower();
                        dbContext.Tags.Add(newTag);
                        dbContext.SaveChanges();
                        newStory.Tags.Add(tag);

                    }
                }
            }

            newStory.StoryBody = PostBody;
            newStory.DatePosted = DateTime.Now;
            newStory.PosterEmail = AuthorEmail;
            newStory.PosterName = AuthorName;
            newStory.Title = PostTitle;
            newStory.StoreID = StoreID;

            // upload image if uploaded

            if (StoryImg != null)
            {
                String fileName = String.Format("{0}.jpg", new Guid());
                StoryImg.SaveAs(Server.MapPath("~/img/") + fileName);
                newStory.StoryImage = fileName;         
            }
            dbContext.Stories.Add(newStory);
            dbContext.SaveChanges();
            return RedirectToAction("Success", new { storyID = newStory.StoryID });

    }

    public ActionResult Success(Int32 storyID)
    {
        SuccessViewModel vm = new SuccessViewModel();
        vm.newStoryID = storyID;

        return View(vm);

    }
}

}

The only non-primitive, StoryImg, will not bind - it is always null.

Thanks so much for any help.

您是否尝试使用HttpPostedFileBase而不是HttpPostedFile?

Try using HttpPostedFileBase as controller input argument in HttpPost request combined with EditorFor as file container inside form as shown in example below:

Controller

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SubmitStory(AddStoryViewModel model, HttpPostedFileBase StoryImg) 
{
     // other stuff

     if (StoryImg != null)
     {
         String fileName = String.Format("{0}.jpg", new Guid());
         StoryImg.SaveAs(Server.MapPath("~/img/") + fileName);
         newStory.StoryImage = fileName;         
     }

     // other code to add story data into DB

     return RedirectToAction("Success", new { storyID = newStory.StoryID });
}

View

@model StoryWall.ViewModels.AddStoryViewModel

@using (Html.BeginForm("SubmitStory", "Add", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()

    <!-- other stuff -->

    <div class="form-group">
       <label class="control-label col-sm-2">Add a picture (optional)</label>
       <div class="col-sm-10">
       @Html.EditorFor(model => model.StoryImage, new { htmlAttributes = new { @class = "form-control", @type="file" }})
       </div>
    </div>

    <!-- other stuff -->
}

If the uploaded file still not available in StoryImg , try using Request.Files to retrieve file names from POST request:

foreach (String name in Request.Files)
{
    StoryImg = this.Request.Files[name];
}

NB: HttpPostedFile is a sealed class which treated differently compared with the abstract class HttpPostedFileBase , even the property and method names inside them have somewhat similar.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM