Retrieving and caching HTML from website using ASP.NET MVC 3 - asp.net-mvc

I want a partial view that display some stuff from a website that is not under my control.
The data on the website is only available through HTML, and thus I can only retrieve it by querying the web site and parsing the HTML. (The website holds a list of 50 elements, and I only want the top 10.)
Now, the data from the website is not changing very frequently, so I imagine that I can retrieve the HTML on an hourly basis, and displaying a cached version on my web site.
How can I accomplish this in ASP.NET MVC 3?

Ignoring the MVC3 requirement for now, you should look to using WebClient to grab the html from the website. You can do something like:
var client = new WebClient();
var html = Encoding.UTF8.GetString(client.DownloadData("http://www.somedomain.com"));
If you need to tailor your request, I'd recommend looking at HttpWebRequest, HttpWebResponse. Now that you can grab the html, you need to consider your caching mechanism, possibly in the ASP.NET runtime?
public ActionResult GetHtml()
{
if (HttpRuntime.Cache["html"] == null)
GetHtmlInternal();
return Content((string)HttpRuntime.Cache["html"], "text/html");
}
private void GetHtmlInternal()
{
var html = // get html here.
HttpRuntime.Cache.Insert("html", html, null, DateTime.Now.AddMinutes(60), Cache.NoSlidingExpiration);
}

The first solution that comes to mind is to create an action in a controller that makes an Http request to the remote web page and parses the html you want to return to your own page and then set output caching on your action.
Edit:
What controller to put the action in would depend on the structure of your web site and whether the partial view would be visible on all views or just a specific view. If the partial is visible in all views I'd either place it in the Home controller or create a "General" controller (if I anticipated more actions would go in such a controller).
If you want to manipulate the result I would probably make a model and partial view for the list. If you want to take a part of the returned html and output it as it is I would use the same method as in the answer by Matthew Abbott:
return Content(yourHtmlString);
The end would look something like this:
[OutputCache(Duration = 3600)]
public ActionResult RemoteList()
{
var client = new WebClient();
var html = Encoding.UTF8.GetString(client.DownloadData("http://www.somedomain.com"));
// Do your manipulation here...
return Content(html);
}
(Some of the above code was borrowed from the post by Matthew Abbott.)

You could just add OutputCache attribute on your action and set OutputCache.Duration Property to 3600 seconds (1 hour)

Related

MVC: Displaying dynamic forms based on URL arguments

I am trying to generate content within a view based on a URL parameter.
For example:
10 companies, 30 users each
A user from company Google clicks a distributed icon that opens
www.****tickets.com/ticket/?c=Google
A custom ticketing page with textboxes, dropdownlists with
'Google-specific' categories and the Google logo is displayed.
All of this data will be handled by a common controller
I have lots of plans for this site, but this is step #1. I am pretty new to MVC, but I have a page propped up that has Users/Groups/Roles. I'm unsure if there would be a better way to implement this, such as Javascript.
EDIT: It looks like MVC Dynamic Forms could be useful for this application. Researching it now.
Your controller method for handling the URL /ticket should accept an optional parameter c. This will then automatically be populated by the value in the querystring. You can then hold this in a model and pass it into the view.
The code will be something like
[HttpGet]
public ActionResult Ticket(string c = null)
{
//Get some data based on c
var model = new TicketModel
{
Company = c,
SomeDropdownValues = StuffYouReadFromTheDbUsingParameterC,
LogoPath = PathYouHaveCalculatedUsingParameterC
};
return View(model);
}
If you don't know how to use a model within a view look up the term "model binding". This article explains it clearly.

ServiceStack Razor - Html.RenderAction equivalent

I have the requirement to use Html.RenderAction like you would in ASP.NET MVC.
For instance I have a Home Page with News and Products on.
I would like to do for instance
#Html.RenderAction("/api/products/featured")
Which would start a new service call and output the template to the html stream.
Is this possible using ServiceStack Razor and if so how do I accomplish it?
The PartialExamples.cshtml test page shows different examples of rendering a razor view inside a page, e.g:
Using the new RenderToAction() method which lets you execute a Service and it's rendered partial view with a route and QueryString, e.g:
#Html.RenderAction("/products/1")
This also takes an optional view name if you want a different view than the default:
#Html.RenderAction("/products/1", "CustomProductView")
There's also the normal Html.Partial() to specify which view and model you want to render in the page, e.g:
#Html.Partial("GetProduct",
base.ExecuteService<ProductService>(s => s.Any(new GetProduct { Id = 1 })))
ExecuteService is simply a wrapper around the equivalent ResolveService in a using statement, i.e:
#{
Response response = null;
using (var service = base.ResolveService<ProductService>())
{
response = service.Any(new GetProduct { Id = 1 });
}
}
#Html.Partial("GetProduct", response)
The new RenderToAction() method in Razor Views was added in v4.0.34+ which is now available on MyGet.
*I may be duplicating my answer or I lost it somehow
Looking at the ServiceStack.Razor.ViewPage class there is an Html property of type ServiceStack.Html.HtmlHelper. I don't see 'RenderAction' as a method (or extension method) on this class so it doesn't appear to be available. There is a 'Partial' method that takes the ViewName and an overload that takes a ViewName and an object. Based on your above comment this doesn't appear to be a useful solution.
If I'm correct about the above, I think you'd need your 'Featured View Template' to pull in the data. Could add soemthing like
{ FeaturedResponse products = new JsonServiceClient("http://localhost").Get<FeaturedResponse>("/api/products/featured"); }
to your template. This would allow you to use the products variable like a Model.
Or, use JavaScript to pull the data into the template. You would have have to use JavaScript to get your data into the HTML elements, though.
You could then render the template using #Html.Partial('Featured')
Hope this helps.

Rich web app using ASP.NET MVC and Backbone.js and progressive enhancement

I'm working on a small web app using mvc and backbone.js and I have a couple of thoughts about how to handle async request vs regular requests.
Today I use a controller called /pages which returns a partial view if it's a ajax request and a standard view if it's a regular request. In another question I was told I'm doing it all wrong when I send a bunch of HTML back to the client.
So how should I structure my controllers etc to handle both async and non async requests?
In my case I have the following code in my pages controller
public ActionResult Index() {
var id = _model.Id;
var parentId = _model.Parent != null ? _model.Parent.Id : null;
var viewModel = new IndexViewModel
{
RootModel = _session.Query<IPageModel>().SingleOrDefault(model => model.Parent == null),
CurrentModel = _model,
ParentModel = parentId != null ? _session.Load<IPageModel>(parentId) : null,
Children = _session.Query<IPageModel>()
.Where(model => model.Parent.Id == id)
.Where(model => !model.Metadata.IsDeleted)
.OrderBy(model => model.Metadata.SortOrder)
.ToList()
};
if(Request.IsAjaxRequest()) {
return PartialView(viewModel);
}
return View(viewModel);
}
But if I understand things correctly I would be better off sending back a collection of pages instead of a complete view model? How should I handle this in my controller?
Is it a good idea to create a separate controller/api using eg. the api controller in mvc 4?
If you are using Backbone then you should return JSON insted of the PartialView and the PartialView should be a template in the page where the Backbone view will render that.
As mentioned earlier in different answers you should return JSON result instead of HTML view.JSON Result on MSDN Example of using JSON result
If you are using ASP.NET MVC 4 (as of now beta) you can use web api to get data in JSON through ajax/rest call.
I agree with Florim. You should be returning json from your MVC controllers. Backbone was built with REST in mind. Therefore your server should mimic a REST API, and return json for Backbone to work with. When I work with MVC 3 and Backbone, all my server code does is return the data. It usually has one view and that is the Backbone Application View. The web app "views" are rendered using templates from the data that is returned from my controllers. Hope this helps.
I have not played with the MVC 4 Web API as of yet, but I do think this situation would be the ideal choice for it.
I've compiled a couple of Backbone examples together into a working ASP.net MVC 3 application that is using REST interface. Here is the link to my tumblr Blog where I have provided information to the source code and the websites I used as resources. Backbone.js works really well with MVC 3 and I am always looking for new ways to push this example.

How can i do paging in asp.net mvc3?

I am working on asp.net mvc3 application and have many records coming from database. I want to display only 10 records first then user can click on button to see next 10 records and so on... Like facebook wall posting more records.
How can I implement this thing in my application using jQuery and ajax?
How can I control display data in view using paging?
I am using this to get 10 records but I want to display all records using more record button
var Entity = (from a
in context.Data
where <Condition>
select a).Take(10);
The following articles should give you an idea :
http://weblogs.asp.net/andrewrea/archive/2008/07/01/asp-net-mvc-quot-pager-quot-html-helper.aspx
http://weblogs.asp.net/gunnarpeipman/archive/2010/02/21/simple-pager-for-asp-net-mvc.aspx
On the other hand, you can implement it like this :
Get the nuget package called TugberkUg.MVC
Then, your controller should look like below :
public ActionResult Index(int page = 0) {
const int pageSize = 10;
#region _filter the model
IQueryable<myModel> model = _myrepo.GetAll()
#endregion
#region _convert the model to paginatedList
var paginatedModel =
new TugberkUg.MVC.Helpers.PaginatedList<myModel>(model, page, pageSize);
#endregion
return View(paginatedModel);
}
And, here how your controller should look like :
#model TugberkUg.MVC.Helpers.PaginatedList<myModel>
#foreach(var item in Model) {
<p>#item.id</p>
}
You need to handle the pager as well and here is a sample for you :
ASP.NET MVC PaginatedList Pager - Put wise "..." after certain point
This TugberkUg.MVC.Helpers.PaginatedList class will provide all the necessary fields for pager.
I'm not aware of any .net library that will do pagination for you out of the box, so I will roll out a DIY solution for you to think about.
How can i implement this thing in my application using jquery and ajax?
It is probably wise to have a specific Controller for ajax requests. Just have a separate Area (Ajax), and send your ajax requests to that url you set up.
How can i control display data in view using paging?
Set up a controller that takes in a "page" parameter.
public ActionResult GetData(int? page){
// page is nullable to allow for default page
// Do your query here to get the specific page.
}
I'm not sure if you require more information other than this. If I were trying to do what you were doing, this is what I would do. Hope that helps.
If your are using Webgrid to display data then rowsperpage will do.
var grid = new WebGrid(source: Model, selectionFieldName: "SelectedRow", rowsPerPage: 10, canPage: true, canSort: true);
I would suggest using a telerik mvc control. They are really easy to use, powerful and free.
www.telerik.com

Client-side partial page rendering and search engines

I'm beginning to write what may grow to be a large commercial website. The business has several facets, so I'm considering a 'widget' based UI not dissimilar to the BBC homepage (http://www.bbc.co.uk).
I'm writing a content management system that would allow an administrator to compose pages using a selection of pre-defined widgets (i.e a text widget, a product widget, news headlines widget etc). I'm writing the application using ASP.NET MVC.
The mark-up for each widget type will be encapsulated in a user control (ascx). I'm considering two approaches for rendering the widgets (which I may mix):
Use RenderPartial to build the page up on the server (very much like the Kona sample from the ASP.NET MVC site)
Render widget place-holders on the served page then get the client to issue a request for each place-holder (using a jquery to call standard MVC actions returning HTML).
The second approach is appealing for two reasons:
It allows for a page to be returned without having to wait for slow-to-render widgets (like a comments widget, perhaps).
It allows me to control caching at a widget level (rather than at a page level). I can use the built-in OutputCache attributes to individually control the type of caching on each widget type.
Here's the question:
If some of the content of a page is only rendered by javascript-initiated HTTP GET requests (i.e. not included in the initial response), would this content be included in Google's appraisal/indexing of the page?
Assume that the javascript requests are triggered on document.ready
For those who like code, my prototype implements the second approach using this code at the server:
[OutputCache(Duration = 30, VaryByParam = "widgetUniqueKey")]
public ActionResult RenderWidget(int widgetId)
{
var cmsService = new CmsService();
var widget = cmsService.GetWidget(widgetId);
string viewName = widget.Accept(new ViewNameWidgetVisitor());
object viewData = widget.Accept(new ViewDataWidgetVisitor());
return View(viewName, viewData);
}
with this code on the client (the initial page GET has returned placeholder divs)
$('.widgetPlaceholder').each(function() { renderWidget(this) });
function renderWidget(source) {
var placeholder = $(source);
var widgetId = placeholder.attr('id').replace("widget", "");
var widgetUniqueKey = placeholder.children('div.uniqueId').text();
$.get('/home/RenderWidget', {
widgetId: widgetId,
widgetUniqueKey: widgetUniqueKey
},
function(data) {
placeholder.replaceWith(data);
}, "html"
);
}
The widgetUniqueKey will vary between widget types: it may be the product id for a product widget; the user id for a recommendations widget; etc.
Many thanks
No. The google bot (and any other indexers as far as I'm aware) doesn't execute any javascript code, so anything that you fetch via ajax won't be indexed.

Resources