A product I've inherited is using WebClient to read HTML from a MVC based site. Each page is a different type of e-mail, so in order to compose and send an e-mail they use WebClient to request a URL and download the string.
var outputHtml = string.Empty;
using (WebClient client = new WebClient())
{
client.Encoding = Encoding.UTF8;
outputHtml = client.DownloadString(emailURL);
}
return outputHtml;
Is there a way to remove the need to host this email based site but retain most of this code. I guess what I need to do is pass my request to the controller and retrieve the output after the razor engine has passed the view model through the cshtml page.
Is that possible?
There are many ways you could render a Razor view to a string. One possibility is to use RazorEngine. Another possibility is to use some specifically designed framework for this purpose such as Postal.
I have an existing MVC3 application which allows users to upload files and share them with others. The current model is that if a user wants to change a file, they have to delete the one there and re-upload the new version. To improve this, we are looking into integrating WebDAV to allow the online editing of things like Word documents.
So far, I have been using the .Net server and client libraries from http://www.webdavsystem.com/ to set the website up as a WebDAV server and to talk with it.
However, we don't want users to interact with the WebDAV server directly (we have some complicated rules on which users can do what in certain situations based on domain logic) but go through the previous controller actions we had for accessing files.
So far it is working up to the point where we can return the file and it gives the WebDAV-y type prompt for opening the file.
The problem is that it is always stuck in read-only mode. I have confirmed that it works and is editable if I use the direct WebDAV URL but not through my controller action.
Using Fiddler I think I have found the problem is that Word is trying to talk negotiate with the server about the locking with a location that isn't returning the right details. The controller action for downloading the file is "/Files/Download?filePath=bla" and so Word is trying to talk to "/Files" when it sends the OPTIONS request.
Do I simply need to have an action at that location that would know how to respond to the OPTIONS request and if so, how would I do that response? Alternatively, is there another way to do it, perhaps by adding some property to the response that could inform Word where it should be looking instead?
Here is my controller action:
public virtual FileResult Download(string filePath)
{
FileDetails file = _fileService.GetFile(filePath);
return File(file.Stream, file.ContentType);
}
And here is the file service method:
public FileDetails GetFile(string location)
{
var fileName = Path.GetFileName(location);
var contentType = ContentType.Get(Path.GetExtension(location));
string license ="license";
var session = new WebDavSession(license) {Credentials = CredentialCache.DefaultCredentials};
IResource resource = session.OpenResource(string.Format("{0}{1}", ConfigurationManager.AppSettings["WebDAVRoot"], location));
resource.TimeOut = 600000;
var input = resource.GetReadStream();
return new FileDetails { Filename = fileName, ContentType = contentType, Stream = input };
}
It is still very early days on this so I appreciate I could be doing this in entirely the wrong way and so any form of help is welcome.
In the end it seems that the better option was to allow users to directly talk to the WebDAV server and implement the authentication logic to control it.
The IT Hit server has extensions that allow you to authenticate against the forms authentication for the rest of the site using basic or digest authentication from Office. Using that along with some other customisations to the item request logic gave us what we needed.
This is exactly what i did for a MVC 4 project.
https://mvc4webdav.codeplex.com/
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
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.
Currently my project has a page domain/cocktail which displays a list of all cocktails. If user wants to filter the list he can choose sorting order, cocktail's starting letter and strength. So url will look like domain/cocktail?letter=B&sort=nu&strength=2&page=4.
As I've read it is not the best choice to use such urls. What approach can you suggest to get SEO-friendly URLs with the same functionality.
ASP.NET MVC applications use the ASP.NET routing system, which decides how URLs map to particular controllers and actions.
Under default routing system when a browser requests http://yoursite/Home, it gets back the output from Controller's action method. Therefore if you use CoctailList action method to derive the list from backend, user will need to point at http://yoursite/Controller/CoctailList
In your situation, I would POST the search criteria, but I would put the page on end of the URL like:
domain/cocktail/2
domain/cocktail/3
You can post the data by creating a JSON object:
var viewModel = new Object();
viewModel.Letter = "B";
viewModel.Sort = "nu";
$.post("/domain/cocktail/" + page, viewModel, function () { });