代码之家  ›  专栏  ›  技术社区  ›  0xburned

从ASP.NET Razor视图调用Ajax请求

  •  1
  • 0xburned  · 技术社区  · 5 年前

    如何从Razor视图启动一个Ajax请求(调用控制器操作),该视图以JSON格式返回数据?

    在单击我的Razor视图中的操作链接后,页面会执行post请求,该请求会将页面重定向到/actionname,当然这是不存在的。

    我也在使用jquery,但不确定如何从Razor视图中获取数据,如果我使用jqueryAjax方法,则需要传递这些数据。

    显示事件日志.cshtml

    @{ ViewBag.Title = "Event Logs"; }
    @model IEnumerable
    <Application.Models.EventLogs>
    <table id="data-table" class="table display responsive" style="width:100%">
       <thead class="thead-colored thead-light">
          <tr>
             <th>Time</th>
             <th>Scheme</th>
             <th>Serial Number</th>
             <th>Batch</th>
             <th>Exp Date</th>
             <th>Product Code</th>
             <th>Http Code</th>
             <th>Is Confirmed?</th>
             <th>Confirmation Date</th>
             <th>Verify Pack</th>
          </tr>
       </thead>
       <tbody>
          @foreach (var item in Model)
          {
          <tr>
             <td>@item.Timestamp</td>
             <td>@item.ProductCodeScheme</td>
             <td>@item.SerialNumber</td>
             <td>@item.Batch</td>
             <td>@item.ExpirationDate</td>
             <td>@item.ProductCode</td>
             <td>@item.HttpResponseCode</td>
             <td>@item.ConfirmedParsed</td>
             <td>@item.ConfirmedDate</td>
             if (@item.HttpResponseCode == "202")
             {
             <td class="text-secondary">@Html.ActionLink("Verify Pack", "VerifyPack", "Home", new { ProductCodeScheme = @item.ProductCodeScheme, ProductCode = @item.ProductCode, SerialNumber = @item.SerialNumber, Batch = @item.Batch, ExpirationDate = @item.ExpirationDate, CommandStatusCode = 0 }, new { @class = "text-info" })</td>
             }
             else
             {
             <td class="text-secondary">Not Available</td>
             }
          </tr>
          }
       </tbody>
    </table>
    }
    

    控制器动作

    [HttpPost]
    public ActionResult VerifyPack(string productCodeScheme, string productCode, string serialNumber, string batch, string expirationDate, int commandStatusCode, string orderTrackingNo = null) {
        string TextAreaResult = string.Empty;
        string TextAreaResultException = string.Empty;
        string TextAreaResultHttpOperationCode = string.Empty;
        string TextAreaResultHttpResponseCode = string.Empty;
        string TextAreaResultHttpInformation = string.Empty;
        string TextAreaResultHttpWarning = string.Empty;
        string TextAreaResultState = string.Empty;
        string RemoteIpAddress = string.Format("{0}", Request.UserHostAddress);
    
        try {
            using(SecureMediDatabase database = new SecureMediDatabase(this)) {
                DatabaseFactory.setDatabase(database);
                Request baseRequest = (Request) database.createRequest(Country);
                ServiceThread serviceThread = new ServiceThread(0, null, Country);
                serviceThread.attach(this);
    
                baseRequest.setId(0);
                baseRequest.setProductCodeScheme(productCodeScheme);
                baseRequest.setRequestType(1); //single pack
                baseRequest.setProductCode(productCode);
                baseRequest.setSerialNumber(serialNumber);
                baseRequest.setBatch(batch);
                baseRequest.setExpirationDate(expirationDate);
                baseRequest.setWorkstation(RemoteIpAddress);
                baseRequest.setManualEntry(string.IsNullOrEmpty(expirationDate) || string.IsNullOrEmpty(batch));
    
                if (baseRequest.isManualEntry()) {
                    switch (commandStatusCode) {
                        case 2:
                        case 3:
                            break;
    
                        default:
                            throw new NotSupportedException("This operation does not support manual entries!");
                    }
                }
    
                switch (Country) {
                    case "SE":
                        SecureMediRequestSE requestSE = (SecureMediRequestSE) baseRequest;
                        requestSE.setUserId(@User.Identity.Name);
                        requestSE.setCommandStatusCode(commandStatusCode);
                        requestSE.OrderTrackingNumber = orderTrackingNo;
                        break;
    
                    case "FI":
                        SecureMediRequestFI requestFI = (SecureMediRequestFI) baseRequest;
                        requestFI.setSubUserId(@User.Identity.Name);
                        break;
                }
    
                serviceThread.RunRequest(control, baseRequest, apteekki);
    
                TextAreaResult = string.Format("{0} {1} {2} {3} {4}", baseRequest.getResponseOperationCode(), baseRequest.getHttpResponseCode(), baseRequest.getHttpInformation(), baseRequest.getHttpWarning(), baseRequest.getResponseStatusCode());
    
                TextAreaResultHttpOperationCode = string.Format("{0}", baseRequest.getResponseOperationCode());
    
                TextAreaResultHttpResponseCode = string.Format("{0}", baseRequest.getHttpResponseCode());
    
                TextAreaResultHttpInformation = string.Format("{0}", baseRequest.getHttpInformation());
    
                TextAreaResultHttpWarning = string.Format("{0}", baseRequest.getHttpWarning());
    
                TextAreaResultState = string.Format("{0}", baseRequest.getResponseStatusCode());
            }
        } catch (Exception exc) {
            TextAreaResultException = "Exception: " + exc.Message;
        }
    
        return Json(new {
            result = TextAreaResult,
            httpOperationCode = TextAreaResultHttpOperationCode,
            httpResponseCode = TextAreaResultHttpResponseCode,
            httpInformation = TextAreaResultHttpInformation,
            httpWarning = TextAreaResultHttpWarning,
            state = TextAreaResultState,
            exception = TextAreaResultException,
            isSuccess = TextAreaResultHttpResponseCode == "200" || TextAreaResultHttpResponseCode == "202"
        });
    }
    

    基于答案的错误:

    enter image description here enter image description here

    2 回复  |  直到 5 年前
        1
  •  1
  •   Nathan Champion    5 年前

    像这样的事情应该能让你开始。向需要从中提取信息的项添加类。然后,不使用actionlink,只创建一个普通的元素,该元素还具有一个唯一的类。让jquery处理这些链接上的单击事件,并通过Ajax调用将同一行的其他td项传递给控制器。

    $(".button").click( function() {
    	var tr = $(this).closest("tr");
      var ProductCodeScheme = tr.find(".ProductCodeScheme").html();
      var SerialNumber = tr.find(".SerialNumber").html();
      var Batch = tr.find(".Batch").html();
      var ExpirationDate = tr.find(".ExpirationDate").html();
      var ProductCode = tr.find(".ProductCode").html();
      
      $.ajax({
        url: "/Verify Pack/VerifyPack", 
        type: "POST",      
        data: ({
        	ProductCodeScheme: ProductCodeScheme,
          SerialNumber: SerialNumber,
          Batch: Batch,
          ExpirationDate: ExpirationDate,
          ProductCode: ProductCode
        }),     
        cache: false,
        success: function(data){                          
          //Do something here for a successful POST                   
        }           
      });    
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <table id="data-table" class="table display responsive" style="width:100%">
       <thead class="thead-colored thead-light">
          <tr>
             <th>Time</th>
             <th>Scheme</th>
             <th>Serial Number</th>
             <th>Batch</th>
             <th>Exp Date</th>
             <th>Product Code</th>
             <th>Http Code</th>
             <th>Is Confirmed?</th>
             <th>Confirmation Date</th>
             <th>Verify Pack</th>
          </tr>
       </thead>
       <tbody>
          <tr>
             <td>Timestamp 1</td>
             <td class="ProductCodeScheme">ProductCodeScheme 1</td>
             <td class="SerialNumber">SerialNumber 1</td>
             <td class="Batch">Batch 1</td>
             <td class="ExpirationDate">ExpirationDate 1</td>
             <td class="ProductCode">ProductCode 1</td>
             <td>HttpResponseCode 1</td>
             <td>ConfirmedParsed 1</td>
             <td>ConfirmedDate 1</td>
             <td class="text-secondary"><a href="#!" class="button">Item 1</a></td>
          </tr>
         <tr>
             <td>Timestamp 2</td>
             <td class="ProductCodeScheme">ProductCodeScheme 2</td>
             <td class="SerialNumber">SerialNumber 2</td>
             <td class="Batch">Batch 2</td>
             <td class="ExpirationDate">ExpirationDate2</td>
             <td class="ProductCode">ProductCode 2</td>
             <td>HttpResponseCode 2</td>
             <td>ConfirmedParsed 2</td>
             <td>ConfirmedDate 2</td>
             <td class="text-secondary"><a href="#!" class="button">Item 2</a></td>
          </tr>
       </tbody>
    </table>
        2
  •  2
  •   Tetsuya Yamamoto    5 年前

    基本上 @Html.ActionLink() 帮助器呈现定位标记( <a> )使用属性并默认为通过刷新整个页面来使用get请求,因此需要添加 preventDefault() 以便使用该元素的Ajax回调。如果action方法使用http get方法,则可以对锚链接的公共类执行简单的Ajax调用,如下所示:

    $('.text-info').on('click', function (e) {
        e.preventDefault();
    
        var url = $(this).attr('href');
        $.get(url, function (response) {
            // do something with AJAX response
        });
    });
    

    但是,由于目标控制器操作标记为 [HttpPost] ,需要从中提取查询字符串参数 href 带有附加函数的属性,并在Ajax调用中使用它们 type: 'POST' 设置或使用 $.post() :

    $('.text-info').on('click', function (e) {
        e.preventDefault(); // mandatory to prevent GET request
    
        var url = $(this).attr('href');
    
        var pcs = getQueryStringParams(url, 'ProductCodeScheme');
        var pc = getQueryStringParams(url, 'ProductCode');
        var sn = getQueryStringParams(url, 'SerialNumber');
        var batch = getQueryStringParams(url, 'Batch');
        var expDate = getQueryStringParams(url, 'ExpirationDate');
        var csc = getQueryStringParams(url, 'CommandStatusCode');
    
        // create key-value pair for action method parameters
        var obj = { ProductCodeScheme: pcs, ProductCode: pc, SerialNumber: sn, ... }
    
        $.ajax({
            type: 'POST',
            url: url.split('?')[0], // URL without query string, or use '@Url.Action("VerifyPack", "Home")'
            data: obj,
            dataType: 'json', // expects response as JSON
            success: function (response) {
                // do something with AJAX response
            },
            error: function (xhr, status, err) {
                // error handling
            }
        });
    
        // just make sure that the link is not redirecting
        return false;
    });
    
    function getQueryStringParams(url, name) {
         return (RegExp(name + '=' + '(.+?)(&|$)').exec(url)||[,null])[1];
    }
    

    实际上,还有另一种方法可以从锚标记调用Ajax,比如 @Ajax.ActionLink() ,取决于您的选择:

    @Ajax.ActionLink("Verify Pack", "VerifyPack", "Home", new { ProductCodeScheme = @item.ProductCodeScheme, ProductCode = @item.ProductCode, SerialNumber = @item.SerialNumber, Batch = @item.Batch, ExpirationDate = @item.ExpirationDate, CommandStatusCode = 0 }, 
                     new AjaxOptions { HttpMethod = "POST", 
                                       InsertionMode = InsertionMode.Replace, 
                                       UpdateTargetId = "targetElementId", 
                                       OnComplete = "onComplete();" 
                                     },
                     new { @class = "text-info" })
    

    注:

    如果需要处理来自同一个控制器的Ajax请求和普通请求,可以使用 Request.IsAjaxRequest() (或) Context.Request.Headers["X-Requested-With"] == "XMLHttpRequest" 在核心MVC中。