Client-side partial page rendering and search engines - asp.net-mvc

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.

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.

Is there a method to cache Razor page in Service Stack?

I'm new to Service Stack, just discovered and looks very interesting to use.
I'd like my future websites to be quite backbone heavy but still ensure they can mostly be indexed by Google and seen by people with JavaScript (more for Google indexing)
I know there is content caching, such as lists etc which can be retrieved and severed the a razor page.
But I didn't see any methods of docs covering caching the entire razor page after being rendered, which is what I believe OutputCache attribute does on normal ASP.NET MVC 3.
So if anyone could direct me to possible examples of entire razor pages being cached using Service Stack, or a possible method of doing it would be greatly appreciated.
Thanks
Caching Razor/HTML views in ServiceStack is done in the same way as every other format by using ToOptimizedResultUsingCache e.g:
public object Any(CachedAllReqstars request)
{
if (request.Aged <= 0)
throw new ArgumentException("Invalid Age");
var cacheKey = typeof(CachedAllReqstars).Name;
return RequestContext.ToOptimizedResultUsingCache(Cache, cacheKey, () =>
new ReqstarsResponse {
Aged = request.Aged,
Total = Db.GetScalar<int>("select count(*) from Reqstar"),
Results = Db.Select<Reqstar>(q => q.Age == request.Aged)
});
}
This service caches the output of any requested format, inc. HTML Razor Views.

How can I write an MVC3/4 application that can both function as a web API and a UI onto that API?

My title sums this up pretty well. My first though it to provide a few data formats, one being HTML, which I can provide and consume using the Razor view engine and MVC3 controller actions respectively. Then, maybe provide other data formats through custom view engines. I have never really worked in this area before except for very basic web services, very long ago. What are my options here? What is this Web API I see linked to MVC4?
NOTE: My main HTML app need not operate directly off the API. I would like to write the API first, driven by the requirements of a skeleton HTML client, with a very rudimentary UI, and once the API is bedded down, then write a fully featured UI client using the same services as the API but bypassing the actual data parsing and presentation API components.
I had this very same thought as soon as the first talk of the Web API was around. In short, the Web API is a new product from the MS .NET Web Stack that builds on top of WCF, OData and MVC to provide a uniform means of creating a RESTful Web API. Plenty of resources on that, so go have a Google.
Now onto the question..
The problem is that you can of course make the Web API return HTML, JSON, XML, etc - but the missing piece here is the Views/templating provided by the Razor/ASPX/insertviewenginehere. That's not really the job of an "API".
You could of course write client-side code to call into your Web API and perform the templating/UI client-side with the mass amount of plugins available.
I'm pretty sure the Web API isn't capable of returning templated HTML in the same way an ASP.NET MVC web application can.
So if you want to "re-use" certain portions of your application (repository, domain, etc), it would probably be best to wrap the calls in a facade/service layer of sorts and make both your Web API and seperate ASP.NET MVC web application call into that to reduce code.
All you should end up with is an ASP.NET MVC web application which calls into your domain and builds templated HTML, and an ASP.NET Web API application which calls into your domain and returns various resources (JSON, XML, etc).
If you have a well structured application then this form of abstraction shouldn't be a problem.
I'd suggest developing your application in such a way that you use a single controller to return the initial application assets (html, javascript, etc) to the browser. Create your API / logic in WebAPI endpoint services and access those services via JavaScript. Essentially creating a single page application. Using MVC 4 our controller can return different Views depending on the device (phone, desktop, tablet), but using the same JavaScript all of your clients will be able to access the service.
Good libraries to look into include KnockoutJS, SammyJS , or BackBoneJS
If you do have a requirement to return HTML using the WebAPI e.g. to allow users to
click around and explore your API using the same URL then you can use routing\an html message handler.
public class HtmlMessageHandler : DelegatingHandler
{
private List<string> contentTypes = new List<string> { "text/html", "application/html", "application/xhtml+xml" };
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Method == HttpMethod.Get && request.Headers.Accept.Any(h => contentTypes.Contains(h.ToString())))
{
var response = new HttpResponseMessage(HttpStatusCode.Redirect);
var htmlUri = new Uri(String.Format("{0}/html", request.RequestUri.AbsoluteUri));
response.Headers.Location = htmlUri;
return Task.Factory.StartNew<HttpResponseMessage>(() => response);
}
else
{
return base.SendAsync(request, cancellationToken);
}
}
}
For a full example check out:-
https://github.com/arble/WebApiContrib.MessageHandlers.Html
I've played with this idea before. I exposed an API through MVC3 as JSONResult methods on different controllers. I implemented custom security for the API using controller action filters. Then built a very AJAX heavy HTML front-end which consumed the JSON services. It worked quite well and had great performance, as all data transferred for the web app was through AJAX.
Frederik Normen has a good post on Using Razor together with ASP.NET Web API:
http://weblogs.asp.net/fredriknormen/archive/2012/06/28/using-razor-together-with-asp-net-web-api.aspx
One important constraint of a well designed REST service is utilizing "hypermedia as the engine of application state" (HATEOAS - http://en.wikipedia.org/wiki/HATEOAS).
It seems to me that HTML is an excellent choice to support as one of the media formats. This would allow developers and other users to browse and interact with your service without a specially built client. Which in turn would probably result in the faster development of a client to your service. (When it comes to developing actual HTML clients it would make more sense to use a json or xml.) It would also force a development team into a better designed rest service as you will be forced to structure your representations in such a way that facilitates an end users navigation using a browser.
I think it would be smart for any development team to consider taking a similar approach to Frederik's example and create a media type formatter that generates an HTML UI for a rest service based on reflecting on the return type and using conventions (or something similar - given the reflection I would make sure the html media format was only used for exploration by developers. Maybe you only make it accessible in certain environments.).
I'm pretty sure I'll end up doing something like this (if someone hasn't already or if there is not some other feature in the web api that does this. I'm a little new to Web API). Maybe it'll be my first NuGet package. :) If so I'll post back here when it's done.
Creating Html is a job for an Mvc Controller not for Web Api, so if you need something that is able to return both jSon and Html generated with some view engine the best option is a standard Mvc Controller Action methosd. Content Negotiation, that is the format to return, can be achieved with an Action Fiter. I have an action filter that enable the the controller to receive "hints" from the client on the format to return. The client can ask to return a view with a specific name, or jSon. The hint is sent either in the query string or in an hidden field (in case the request comes from a form submit). The code is below:
public class AcceptViewHintAttribute : ActionFilterAttribute
{
private JsonRequestBehavior jsBehavior;
public AcceptViewHintAttribute(JsonRequestBehavior jsBehavior = JsonRequestBehavior.DenyGet)
{
this.jsBehavior = jsBehavior;
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
string hint = filterContext.RequestContext.HttpContext.Request.Params["ViewHint"];
if (hint == null) hint = filterContext.RequestContext.RouteData.Values["ViewHint"] as string;
if (!string.IsNullOrWhiteSpace(hint) && hint.Length<=100 && new Regex(#"^\w+$").IsMatch(hint) )
{
ViewResultBase res = filterContext.Result as ViewResultBase;
if (res != null)
{
if (hint == "json")
{
JsonResult jr = new JsonResult();
jr.Data = res.ViewData.Model;
jr.JsonRequestBehavior = jsBehavior;
filterContext.Result = jr;
}
else
{
res.ViewName = hint;
}
}
}
base.OnActionExecuted(filterContext);
}
}
Now that it's been a little while through the Beta, MS just released the Release Candidate version of MVC4/VS2012/etc. Speaking to the navigation/help pages (mentioned by some other posters), they've added a new IApiExplorer class. I was able to put together a self-documenting help page that picks up all of my ApiControllers automatically and uses the comments I've already put inline to document them.
My recommendation, architecture-wise, as others have said as well, would be to abstract your application into something like "MVCS" (Model, View, Controller, Services), which you may know as something else. What I did was separate my models into a separate class library, then separated my services into another library. From there, I use dependency injection with Ninject/Ninject MVC3 to hook my implementations up as needed, and simply use the interfaces to grab the data I need. Once I have my data (which is of course represented by my models), I do whatever is needed to adjust it for presentation, and send it back to the client.
Coming from MVC3, I have one project that I ported to MVC4, which uses the "traditional" Razor markup and such, and a new project that will be a single page AJAX application using Backbone + Marionette and some other things sprinkled in. So far, the experience has been really great, it's super easy to use. I found some good tutorials on Backbone + Marionette here, although they can be a bit convoluted, and require a bit of digging through documentation to put it all together, it's easy once you get the hang of it:
Basic intro to Backbone.js: http://arturadib.com/hello-backbonejs/docs/1.html
Use cases for Marionette views (I found this useful when deciding how to create views for my complex models): https://github.com/derickbailey/backbone.marionette/wiki/Use-cases-for-the-different-views

ASP.NET MVC - Complicated view logic

I'm making the transition from webforms to MVC (I know, 3 years late) and I "get it" for the most part, but there's a few things I'd like advice and clarification on:
First off, what happens if you want to dynamically add inputs to a view? for example, in an old webform for generating invoices I had a button with a server-side click event handler that added an extra 5 invoice item rows. The stateful nature of webforms meant the server handled the POST event "safely" without altering the rest of the page.
In MVC I can't think how I'd do this without using client-side scripting (not a showstopper, but I would like to support clients that don't have scripting enabled).
The second problem relates to the invoices example. If my Model has a List, how should I be generating inputs for it?
I know data binding is a possible solution, but I dint like surrendering control.
Finally, back to the "stateful pages" concept - say I've got a Dashboard page that has a calendar on it (I wrote my own calendar Control class, the control itself is stateless, but can use the webform viewstate to store paging information) - how could a user page through the calendar months? Obviously POST is inappropriate, so it would have to be with GET with a querystring parameter - how can I do this in MVC? (don't say AJAX).
Thanks!
In MVC you design your actions to accommodate your needs. For example, if you wanted to be able to add 5 rows to an invoice NOT using client-side scripting, you'd probably have your GET action for the invoice generation take a nullable int parameter for the number of rows. Store the current number of rows in the view model for the page and generate a link on the page to your GET action that has the parameter value set to 5 more than the current value. The user clicks the link and the GET view generates the page with the requested number of rows.
Controller
[HttpGet]
public ActionResult Invoice( int? rows )
{
rows = rows ?? 5; // probably you'd pull the default from a configuration
...
viewModel.CurrentRows = rows;
return View( viewModel );
}
View
#Html.ActionLink( "Add 5 Lines", "invoice", new { rows = Model.CurrentRows + 5 }, new { #class = "add-rows" } )
You would probably also add some script to the page that intercepts the click handler and does the same thing via the script that your action would do so that in the general case the user doesn't have to do a round trip to the server.
<script type="text/javascript">
$(function() {
$('.add-rows').click( function() {
...add additional inputs to the invoice...
return false; // abort the request
});
});
</script>
Likewise for your calendar. The general idea is you put enough information in your view model to generate all the actions that you want to perform from your view. Construct the links or forms (yes you can have multiple forms!) in your view to do the action. Use parameters to communicate to the controller/action what needs to be done. In the rare case where you need to retain state between actions, say when performing a wizard that takes multiple actions, you can store the information in the session or use TempData (which uses the session).
For things like a calendar you'd need the current date and the current view type (month/day/year). From that you can construct an action that takes you to the next month/day/year. For a paged list you need the current page, the current sort column and direction, the number of items per page, and the number of pages. Using this information you can construct your paging links that call back to actions expecting those parameters which simply do the right thing for the parameters with which they are called.
Lastly, don't fear AJAX, embrace it. It's not always appropriate (you can't upload files with it, for example), but your users will appreciate an AJAX-enabled interface.
In MVC you can store application state in various ways. In your controller you have direct access to the Session object and you can also store state to the database.
your view can contain basic control flow logic, so, if your model has a list you can iterate over it in the view and, for example, render an input control for each item in the list. you could also set a variable in a model to be the maximum number of rows on the viewpage and then render a row in a table for the number specified by the model.
paging is basically the same thing. you can create a partial view (user control in the webform world) that shows page numbers as links, where each link calls an action that fetches the data for that page of results.
i'm not sure what your beef is with ajax or javascript

Retrieving and caching HTML from website using ASP.NET MVC 3

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)

Resources