How to post ordered items without timeout to controller

拜拜、爱过 提交于 2020-01-15 18:46:53

问题


HTML5 offline ASP.NET MVC4 application allows to enter order quantities and submit order for customer.

Product list rendered in browser contains about 4000 products. Only few products are ordered for particular order.

Using code below request times out. Browser posts all products. Request takes too much time and is probably terminated by httpruntime ExecutionTimeout

Server error log shows that all posted data is received. It looks like MVC model binder requires too much time to create product list from posted data.

How to fix this ? How to post only ordered items whose quantities are entered or other idea ? I can switch to ajax, jquery and MVC4 Web API if this is reasonable.

Controller:

public class OfflineOrderController : ControllerBase
{

    [HttpGet]
    public ActionResult Order(string customerId)
    {
        return View(new MobileOrderOrderViewModel(customerId));
    }

    [HttpPost]
    public ActionResult Order(string customerId, [Bind(Prefix = "Products")] IEnumerable<OrderedItems> Result)

    {
      ... save order to database
        return new ContentResult() { Content = "Order received" };
    }


    public AppCacheResult Manifest()
    {
        return new AppCacheResult(new[] { 
            Url.Content("~/Image/Icon/favicon")
        });
    }

}

View:

<!DOCTYPE html>
<html manifest="~/MobileOrder/Manifest">
<body>
@using (Html.BeginForm())
{
    <table>
        @for (int i = 0; i < Model.Products.Count; i++)
        {
                <tr>
                    <td>@Model.Products[i].Id</td>
                    <td>
                    @Html.HiddenFor(m => Model.Products[i].Id)
                    @Html.TextBoxFor(m => Model.Products[i].Quantity,
                    new { type = "number", min = 0 })
                    </td>
                </tr>
            }
    </table>
    <input type="submit" value="Send order">
@Html.HiddenFor(m => Model.CustomerId)
}
</body>
</html>

ViewModel:

public class MobileOrderOrderViewModel : ViewModelBase
{
    public string CustomerId { get; set; }

    public List<OrderedItems> Products { get; set; }

 public MobileOrderOrderViewModel( string customer ) {
    CustomerId = customer;
    ... populate Products property from database
    }

}

Model:

    public class OrderedItems
    {
        public string Id;
        public decimal Quantity;
    }

Update2

Using code from Imrans answer I created API controller to receive ordered products:

    public class OrderController :ApiController
    {

        public HttpResponseMessage Post(string customerid, [FromBody]List<OrderedItems> products) {
  ....
   }
}

Debugger shows that products are posted but products parameter is empty list. How to pass selected product list to Web API ?

Code used for posting is:

<script>
    $(function () {
        "use strict";
        var BASE_URL = '@Url.Content("~/")';
        $("form").submit(function (ev) {
            var elementsToSend = [];
            ev.preventDefault();
            var quantityElements = $("input.quantity").filter(function (index, element) {
                if ($(this).val() != 0) {
                    return true;
                }
                else {
                    return false;
                }
            });

            $.each(quantityElements, function (index, element) {
                var productIndex = $(element).prevAll()[1];
                var productIdElement = $(element).prevAll()[0];
                elementsToSend.push(productIndex);
                elementsToSend.push(productIdElement);
                elementsToSend.push(element);
            });
            var dataToPost = $(elementsToSend).serializeArray();
            $.post(BASE_URL + "api/Order?" + $.param({
                customerid: $("#CustomerId").val()
            }), dataToPost);
            return false;
        });
    })
</script>

回答1:


Ok, here is what I think your view should look like

<!DOCTYPE html>
<html>
<body>
    @using (Html.BeginForm())
    {
        <table>
            @for (int i = 0; i < Model.Products.Count; i++)
            {
                <tr>
                    <td>@Model.Products[i].Id</td>
                    <td>
                        <input type="hidden" name="Products.Index" value="@i"/>
                        @Html.HiddenFor(m => Model.Products[i].Id)
                        @Html.TextBoxFor(m => Model.Products[i].Quantity, new { @class="quantity", type = "number", min = 0 })
                    </td>
                </tr>
            }
        </table>
        <input type="submit" value="Send order">
        @Html.HiddenFor(m => Model.CustomerId)
    }

    <script>
        $(function() {
            $("form").submit(function () {
                var elementsToSend = [];
                var quantityElements = $("input.quantity").filter(function(index, element) {
                    if ($(this).val() != 0) { return true; }
                    else{ return false;}
                });
                $.each(quantityElements, function (index, element) {
                    var productIndex = $(element).prevAll()[1];
                    var productIdElement = $(element).prevAll()[0];
                    elementsToSend.push(productIndex);
                    elementsToSend.push(productIdElement);
                    elementsToSend.push(element);
                });
                elementsToSend.push($("#CustomerId")[0]);
                var dataToPost = $(elementsToSend).serializeArray();
                //send elementsToSend via ajax
                $.post("/OfflineOrder/Order", dataToPost);
                return false;
            });
        })
    </script>
</body>
</html>

And here is the controller method:

[HttpPost]
public ActionResult Order(string customerId, List<OrderedItems> products)
{
    .....
}

Hope this helps.




回答2:


You can try AJAX to reduce the load:

$('form').submit(function () {
    var data = $(this).serializeArray().filter(function (k, v) {
        // Filter data based on order rule
    });
    // Send AJAX!
    return false;
});

Bear in mind, this only hijacks the default HTML form behavior. It can be superseded by turning off JavaScript.

EDIT: Make sure you are using the Controller / C# naming conventions. Else, binding may not work.

orderedItems.push({ Id: items[i].Id, Quantity: items[i].Quantity });
var data = JSON.stringify({ customerId: $("customerId").val(), Result: orderedItems });

There are fancier ways to do list comprehensions using filter but what you have makes sense to me.

Finally, in your Order POST action, it is a good idea to return a JsonResult.



来源:https://stackoverflow.com/questions/27695779/how-to-post-ordered-items-without-timeout-to-controller

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!