【问题标题】:Json response does not output in view, but prompts me to download the file insteadJson 响应没有在视图中输出,而是提示我下载文件
【发布时间】:2011-11-22 05:24:09
【问题描述】:

我正在使用 WebRequest 从 FCC 读取 JSON 数据,以便将其输出到视图。这是我持有 FCC 许可证的自定义课程:

     public class License
        {
            public string Name{ get; set; }
            public string Frn { get; set; }
            public string Callsign { get; set;}
            public string CategoryDesc { get; set; }
            public string ServiceDesc { get; set; }
            public string StatusDesc { get; set; }
            public DateTime ExpiredDate { get; set; }
            public string Id { get; set; }
            public string DetailUrl { get; set; }

        }

这是我用来读取 json 结果的控制器操作。
我现在将 Verizon Wireless 硬编码为搜索值:

public ActionResult GetLicenses()
        {
            var result = string.Empty;
            var url = "http://data.fcc.gov/api/license-view/basicSearch/getLicenses?searchValue=Verizon+Wireless&format=jsonp&jsonCallback=?";

            var webRequest = WebRequest.Create(url);

            webRequest.Timeout = 2000;

            using (var response = webRequest.GetResponse() as HttpWebResponse)
            {
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    var receiveStream = response.GetResponseStream();
                    if (receiveStream != null)
                    {
                        var stream = new StreamReader(receiveStream);
                        result = stream.ReadToEnd();
                    }
                }
            }
            return new ContentResult { Content = result, ContentType = "application/json" };

        }

这是视图。我正在尝试枚举所有许可证并将它们输出到表中,但是当我转到 /Home/GetLicenses 时,它会提示我下载文件:

@model IEnumerable<MvcApplication1.Models.License>

@{
    ViewBag.Title = "Licenses";
}

<h2>Licenses</h2>

<table>
    <tr>
        <th>
            Name
        </th>
        <th>
            Frn
        </th>
        <th>
            Callsign
        </th>
        <th>
            CategoryDesc
        </th>
        <th>
            ServiceDesc
        </th>
        <th>
            StatusDesc
        </th>
        <th>
            ExpiredDate
        </th>
        <th>
            DetailUrl
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Frn)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Callsign)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.CategoryDesc)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.ServiceDesc)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.StatusDesc)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.ExpiredDate)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.DetailUrl)
        </td>
    </tr>
}

</table>

如果我直接通过 jquery 的 getJSON 方法执行上述操作,我可以完成上述工作,但我想看看是否可以将结果从控制器获取到视图,然后将其呈现在视图中。

这是结果变量中返回的示例:

?({
    "status": "OK",
    "Licenses": {
        "page": "1",
        "rowPerPage": "100",
        "totalRows": "1995",
        "lastUpdate": "Sep 21, 2011",
        "License": [
            {
                "licName": "CELLCO PARTNERSHIP (\"VERIZON WIRELESS\")",
                "frn": "",
                "callsign": "",
                "categoryDesc": "Satellite Earth Station",
                "serviceDesc": "",
                "statusDesc": "Active",
                "expiredDate": "",
                "licenseID": "2300007967",
                "licDetailURL": "http://licensing.fcc.gov/cgi-bin/ws.exe/prod/ib/forms/reports/swr031b.hts?prepare=&column=V_SITE_ANTENNA_FREQ.file_numberC/File+Number&q_set=V_SITE_ANTENNA_FREQ.file_numberC/File+Number/=/FCNNEW2000060800036"
            },
            {
                "licName": "CELLO PARTNERSHIP (\"VERIZON WIRELESS\")",
                "frn": "",
                "callsign": "",
                "categoryDesc": "Satellite Earth Station",
                "serviceDesc": "",
                "statusDesc": "Active",
                "expiredDate": "",
                "licenseID": "2300010661",
                "licDetailURL": "http://licensing.fcc.gov/cgi-bin/ws.exe/prod/ib/forms/reports/swr031b.hts?prepare=&column=V_SITE_ANTENNA_FREQ.file_numberC/File+Number&q_set=V_SITE_ANTENNA_FREQ.file_numberC/File+Number/=/FCNNEW2000083100048"
            },
            {
                "licName": "Cellco Partnership d/b/a Verizon Wireless",
                "frn": "0003290673",
                "callsign": "KE2XMC",
                "categoryDesc": "Experimental",
                "serviceDesc": "Experimental Developmental",
                "statusDesc": "Unknown",
                "expiredDate": "12/14/2000",
                "licenseID": "3000020853",
                "licDetailURL": "https://fjallfoss.fcc.gov/oetcf/els/reports/ELSSearchResult.cfm?callsign=KE2XMC"
            },
            {
                "licName": "Cellco Partnership d/b/a Verizon Wireless",
                "frn": "0003290673",
                "callsign": "WA2XPS",
                "categoryDesc": "Experimental",
                "serviceDesc": "Experimental Developmental",
                "statusDesc": "Unknown",
                "expiredDate": "12/14/2000",
                "licenseID": "3000020851",
                "licDetailURL": "https://fjallfoss.fcc.gov/oetcf/els/reports/ELSSearchResult.cfm?callsign=WA2XPS"
            },
            {
                "licName": "Cellco Partnership dba Verizon Wireless",
                "frn": "0003290673",
                "callsign": "KNKP866",
                "categoryDesc": "Mobile/Fixed Broadband",
                "serviceDesc": "Cellular",
                "statusDesc": "Cancelled",
                "expiredDate": "10/01/2005",
                "licenseID": "13328",
                "licDetailURL": "http://wireless2.fcc.gov/UlsApp/UlsSearch/license.jsp?__newWindow=false&licKey=13328"
            }
        ]
    }
})

我添加了这个类:

 public class FCC
    {
        public string status { get; set; }
        public Licenses Licenses { get; set; }

    }

但我仍然得到无效的 JSON 原语。

 public ActionResult GetLicenses()
        {
            var result = string.Empty;
            var url =
                "http://data.fcc.gov/api/license-view/basicSearch/getLicenses?searchValue=Verizon+Wireless&format=jsonp&jsonCallback=?";

            var webRequest = WebRequest.Create(url);

            webRequest.Timeout = 2000;
            webRequest.ContentType = "application/json";

            using (var response = webRequest.GetResponse() as HttpWebResponse)
            {
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    var receiveStream = response.GetResponseStream();
                    if (receiveStream != null)
                    {
                        var stream = new StreamReader(receiveStream);
                        result = stream.ReadToEnd();
                    }
                }
            }

            FCC fcc = new FCC();

            if (result.StartsWith(@"?("))
            {
                result = result.Substring(2);
            }

            if (result.EndsWith(@")"))
            {
                result = result.Remove(result.Length - 1);
            }


            if (result != null)
            {
                JavaScriptSerializer serializer = new JavaScriptSerializer();
                fcc = serializer.Deserialize<FCC>(result);
            }
            return View(fcc.Licenses.License);


        }

【问题讨论】:

    标签: c# json asp.net-mvc-3 c#-4.0 jsonp


    【解决方案1】:

    通过从您的 ActionMethod 返回 ContentResult,您的浏览器将根据内容以适当的操作进行响应。在这种情况下,将像文件一样下载 JSON 字符串,因为它不是 HTML 文档。

    如果您想在视图中而不是通过 AJAX 呈现结果,那么您需要创建一个表示您的 WebRequest 响应数据的 C# 模型类,然后返回一个 ViewResult 并传递模型(或模型集合) 到视图。

    我建议更改您的ActionMethod 以执行以下操作,并创建一个名为“许可证”的视图

    另外,您的示例响应有点诡计。它比原始许可证对象的数组更复杂,并且用?() 包装。 JavaScriptSerializer 只会反序列化它可以根据属性名称匹配的属性(它也区分大小写)。由于?() 包装,我们需要删除它,这样反序列化就不会中断。

    因此您需要相应地修改您的许可证对象:

    public class FCC
    {
        public string status {get;set;}
        public Licenses Licenses {get; set;}
    }
    public class License
    {
        public string licName{ get; set; }
        public string frn { get; set; }
        public string callsign { get; set;}
        public string categoryDesc { get; set; }
        public string serviceDesc { get; set; }
        public string statusDesc { get; set; }
        public string expiredDate { get; set; } //JSON dates and C# Dates are very finicky
        public string licenseID { get; set; }
        public string licDetailURL { get; set; }
    }
    
    public class Licenses
    {
        public int page {get; set;}
        public int rowPerPage {get; set;}
        public int totalRows {get; set;}
        public string lastUpdate {get; set;}
        public List<License> License {get; set;}
    }
    
    
    public ActionResult Licenses()
    {
            var result = string.Empty;
            var url = "http://data.fcc.gov/api/license-view/basicSearch/getLicenses?searchValue=Verizon+Wireless&format=jsonp&jsonCallback=?";
    
            var webRequest = WebRequest.Create(url);
    
            webRequest.Timeout = 2000;
    
            using (var response = webRequest.GetResponse() as HttpWebResponse)
            {
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    var receiveStream = response.GetResponseStream();
                    if (receiveStream != null)
                    {
                        var stream = new StreamReader(receiveStream);
                        result = stream.ReadToEnd();
                    }
                }
            }
    
            FCC fcc = new FCC();
    
            if (result.StartsWith(@"?("))
            {
                result = result.Substring(2);
            }
    
            if (result.EndsWith(@")"))
            {
                result = result.Remove(result.Length - 1);
            }
    
            if(result != null)
            {
                 JavaScriptSerializer serializer = new JavaScriptSerializer();
                 fcc = serializer.Deserialize<FCC>(result);
            }
            return View(fcc.Licenses.License); //pass the data that your view needs
    
    }
    

    最后,您需要更改 CSHTML 文件中的属性名称,因为新的 License 对象不再具有相同的属性名称。

    AJAX 可能是另一个问题,但如果我遇到一个很好的例子,我会在这里发帖

    【讨论】:

    • 我想我明白你在说什么。我需要将我的响应转换为 c# 对象的集合,然后将该模型传递给要呈现的视图。我该怎么做?
    • 另外,我将如何通过 AJAX 来实现?
    • 我尝试了上述方法,但收到错误消息:无效 JSON 原语:.
    • 您能否发布来自您的 WebRequest 的示例响应?调试时结果变量是什么样的?
    • 我用返回的示例数据更新了我的帖子。我省略了许可证,所以它不会那么长。
    【解决方案2】:

    这是我所做的,它向我展示了 DIV 中的 Json 数据...

    你的控制器...

    public JsonResult GetLicenses()
            {
                var result = string.Empty;
                const string url = "http://data.fcc.gov/api/license-view/basicSearch/getLicenses?searchValue=Verizon+Wireless&format=jsonp&jsonCallback=?";
    
                var webRequest = WebRequest.Create(url);
    
                webRequest.Timeout = 2000;
    
                using (var response = webRequest.GetResponse() as HttpWebResponse)
                {
                    if (response != null && response.StatusCode == HttpStatusCode.OK)
                    {
                        var receiveStream = response.GetResponseStream();
                        if (receiveStream != null)
                        {
                            var stream = new StreamReader(receiveStream);
                            result = stream.ReadToEnd();
    
                        }
                    }
                }
                return Json(result,JsonRequestBehavior.AllowGet);
    

    将返回类型设为 Json 并在 Action 中键入 JsonResult

    如果你想通过 AJAX 调用它,那么这里是

    $.ajax

    <script type="text/javascript">
        $(document).ready(function () {
            $.ajax({
                type: 'GET',
                url: '@Url.Action("GetLicenses","Home")',
                success: function (data) {
                    $('#content').html(data);
                },
                error: function (data) {
                    $('#content').append(data);
                }
            });
        });
    

    并确保您引用了 _Layout.cshtml..中的 Jquery 文件。

    【讨论】:

    • 我已经试过了,它仍然提示下载文件。
    • 在您看来,您必须通过 $.ajax 调用控制器操作并在成功回调中解析 json 数据...
    • 如果我不想使用 ajax,而是想将模型传递给视图并通过 razor 语法将其渲染到表中。
    • 我也试过你给的ajax例子,它仍然要求我下载文件。
    • 好吧,你一定是做错了什么......正如我所尝试的那样,我的视图中呈现了一个完美的 Json 结果......你能再次发布你的控制器和 jquery...
    【解决方案3】:

    我将尝试解决您的代码的几个问题:

    1. 您有一个控制器操作,它返回一个 JSON 字符串,并且您定义了一些 Razor 视图,但从未从该操作中调用此 Razor 视图。
    2. 您正在查询返回 JSONP 的远程服务,而不是使用似乎更适合的 API 的 JSON 可能性。
    3. 在获取远程资源期间,您正在危及工作线程。

    让我们从我们的视图模型开始:

    public class License
    {
        public string Name { get; set; }
        public string Frn { get; set; }
        public string Callsign { get; set; }
        public string CategoryDesc { get; set; }
        public string ServiceDesc { get; set; }
        public string StatusDesc { get; set; }
        public DateTime ExpiredDate { get; set; }
        public string Id { get; set; }
        public string DetailUrl { get; set; }
    }
    
    public class Licenses
    {
        public License[] License { get; set; }
    }
    
    public class FCC
    {
        public string status { get; set; }
        public Licenses Licenses { get; set; }
    }
    

    那么我们将有以下控制器:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            using (var client = new WebClient())
            {
                var json = client.DownloadString("http://data.fcc.gov/api/license-view/basicSearch/getLicenses?searchValue=Verizon+Wireless&format=json");
                var serializer = new JavaScriptSerializer();
                var model = serializer.Deserialize<FCC>(json);
                return View(model.Licenses.License);
            }
        }
    }
    

    请注意,我如何在 url 中不再指定 jsonCallback 查询字符串参数,该参数旨在与 JSONP 一起使用,我不想要 JSONP,我想要 JSON。为此,我还设置了format=json 参数。

    最后我们可以看到以下~/Views/Home/Index.cshtml 视图:

    @model IEnumerable<License>
    
    <table>
        <thead>
            <tr>
                <th>
                    Name
                </th>
                <th>
                    Frn
                </th>
                <th>
                    Callsign
                </th>
                <th>
                    CategoryDesc
                </th>
                <th>
                    ServiceDesc
                </th>
                <th>
                    StatusDesc
                </th>
                <th>
                    ExpiredDate
                </th>
                <th>
                    DetailUrl
                </th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            @Html.DisplayForModel()
        </tbody>
    </table>
    

    以及将为许可证集合的每个元素呈现的相应显示模板 (~/Views/Home/DisplayTemplates/License.cshtml):

    @model License
    
    <tr>
        <td>
            @Html.DisplayFor(x => x.Name)
        </td>
        <td>
            @Html.DisplayFor(x => x.Frn)
        </td>
        <td>
            @Html.DisplayFor(x => x.Callsign)
        </td>
        <td>
            @Html.DisplayFor(x => x.CategoryDesc)
        </td>
        <td>
            @Html.DisplayFor(x => x.ServiceDesc)
        </td>
        <td>
            @Html.DisplayFor(x => x.StatusDesc)
        </td>
        <td>
            @Html.DisplayFor(x => x.ExpiredDate)
        </td>
        <td>
            @Html.DisplayFor(x => x.DetailUrl)
        </td>
    </tr>
    

    好的,到目前为止,我们已经有了地址点 1. 和 2。

    现在是第三个。此同步调用的问题在于以下行:client.DownloadString。这是一个阻塞调用。在 ASP.NET 应用程序中阻塞对远程资源的调用是非常糟糕的。在这里,您正在获取一些可能需要时间的远程资源 => 您将穿越网络边界、互联网防火墙,...直到您访问远程 Web 服务器,该服务器本身为了服务请求将查询数据库,...你明白了:它很慢。在这段时间内,您的 Web 应用程序一直处于等待状态,并且独占了一个线程。请记住,您可以使用的工作线程数量有限,因此不要浪费它们。

    解决这个very serious issue 的方法是使用asynchronous controllers 和I/O 完成端口。它们直接内置在 Windows 内核中,允许您执行 IO 密集型操作,而不会阻塞和独占服务器上的线程。

    你的 HomeController 会变成这样:

    public class HomeController : AsyncController
    {
        public void IndexAsync()
        {
            var client = new WebClient();
            AsyncManager.OutstandingOperations.Increment();
            client.DownloadStringCompleted += (s, e) => 
            {
                AsyncManager.OutstandingOperations.Decrement();
                if (e.Error != null)
                {
                    AsyncManager.Parameters["error"] = e.Error.Message;
                }
                else
                {
                    var serializer = new JavaScriptSerializer();
                    var model = serializer.Deserialize<FCC>(e.Result);
                    AsyncManager.Parameters["licenses"] = model.Licenses.License;
                }
            };
            client.DownloadStringAsync(new Uri("http://data.fcc.gov/api/license-view/basicSearch/getLicenses?searchValue=Verizon+Wireless&format=json"));
        }
    
        public ActionResult IndexCompleted(License[] licenses, string error)
        {
            if (!string.IsNullOrEmpty(error))
            {
                ModelState.AddModelError("licenses", error);
            }
            return View(licenses ?? Enumerable.Empty<License>());
        }
    }
    

    【讨论】:

    • 有机会我会回顾一下。感谢 Darin 的洞察力。
    • 达林,我看过你提出的观点。我右键单击控制器并选择添加视图并创建了一个名为 GetLicenses.cshtml 的视图,它是在 /Home 下创建的,所以当你说控制器没有调用视图时我有点困惑。关于阻塞调用,在这种情况下,最好只检索 json 数据并通过 javascript 解析它而不是通过控制器操作?
    • @Xaisoft,您在磁盘上创建了一个视图,但从未在您的许可证控制器操作中调用此视图。你返回了 Json,而不是 View。关于阻塞呼叫,没有单一的答案。如果您打算使用控制器操作来获取远程资源,异步控制器是最好的。如果远程资源支持 JSONP(在您的情况下支持),您也可以纯粹在客户端执行相同的操作,但如果您想以一种很好的方式呈现结果,则必须从结果中手动构建 DOM 或使用客户端模板引擎,例如 jquery.templates。
    • 我认为这一行:return View(fcc.Licenses.License) 正在将一个带有我的 JSON 模型的视图返回到我添加一个时渲染的视图。
    • @Xaisoft,没错,只是我在您的GetLicenses 操作中看不到这一行。您正在返回 new ContentResult 而不是查看。
    猜你喜欢
    • 1970-01-01
    • 2013-05-18
    • 2018-04-02
    • 1970-01-01
    • 1970-01-01
    • 2017-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多