Ответ Json не выводится в поле зрения, но предлагает загрузить файл вместо - PullRequest
0 голосов
/ 21 сентября 2011

Я использую WebRequest для чтения данных JSON из FCC, чтобы я мог вывести их в представление.Вот мой пользовательский класс для хранения лицензии 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; }

        }

Вот действие Controller, которое я использую для чтения результатов 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>

Я получу вышеуказанное, работающее, если ясделать это напрямую через метод getJSON jquery, но я хотел посмотреть, смогу ли я получить результаты из контроллера в представление и затем отобразить его в представлении.

Это пример того, что возвращается впеременная результатов:

?({
    "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 Invalid.

 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);


        }

Ответы [ 3 ]

1 голос
/ 22 сентября 2011

Есть несколько проблем с вашим кодом, которые я попытаюсь решить:

  1. У вас есть действие контроллера, которое возвращает строку JSON, и вы определили некоторое представление Razor, но это представление бритвыникогда не вызывается из действия.
  2. Вы запрашиваете удаленный сервис, который возвращает JSONP вместо использования возможности JSON API, который кажется более адаптированным.
  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.Здесь вы выбираете некоторый удаленный ресурс, который может занять некоторое время => вы будете пересекать границы сети, брандмауэры в Интернете, ... пока не попадете на удаленный веб-сервер, который сам, для обслуживания запроса, запросит базу данных,Вы получаете суть: это медленно.И в течение всего этого времени ваше веб-приложение сидит и ждет, а поток был монополизирован.Помните, что в вашем распоряжении ограниченное количество рабочих потоков, поэтому не тратьте их впустую.

Способ решения этой очень серьезной проблемы состоит в использовании асинхронных контроллеров и порты завершения ввода / вывода.Они встроены непосредственно в ядро ​​Windows и позволяют выполнять интенсивные операции ввода-вывода без блокировки и монополизации потоков на вашем сервере.

Вот как ваш 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>());
    }
}
1 голос
/ 21 сентября 2011

Возвращая ContentResult из вашего ActionMethod, ваш браузер ответит соответствующим действием на основе содержимого.В этом случае строка JSON будет загружена аналогично файлу, поскольку она не является документом HTML.

Если вы хотите отобразить результаты в представлении, а не через AJAX, вам нужно будет создать C #Класс модели, представляющий ваши данные ответа WebRequest, а затем возвращает ViewResult и передает модель (или коллекцию моделей) в View.

Я бы предложил изменить ваш ActionMethod так, как показано ниже, итакже создайте представление под названием «Licenses»

Кроме того, ваш пример ответа - это немного хитрости.Это сложнее, чем массив вашего исходного объекта лицензии, и он обернут ?().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, вероятно, другой вопрос, но я опубликую здесь, если я выполнючерез хороший пример

0 голосов
/ 21 сентября 2011

Вот что я сделал, и он показывает мне данные Json в DIV ...

Ваш контроллер ...

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 и вводит 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);
            }
        });
    });

и убедитесь, что вы ссылаетесь на файлы Jquery в _Layout.cshtml..

...