Archive for the 'MVC' Category

Removing the Page directive from you Views in Asp.Net Mvc

Phil Haack shows how you can remove the Page directive from the view, more or less. I have known for a long time that you could register your user controls in your web.config but never thought that this could be possible. It is quite nice.

How to serve the same data in Json, Xml or Html with Asp.Net Mvc revised

After I had blogged about my first example I talked with Eric Hexter (from the mvccontrib team) and Christian Dalager. Christian pointed out that there is an issue with the paths that doesn’t act in the same way as it does in Rails.

FormatController did something like this, when handling parameters, “/home/view.xml/4” when it should have done “/home/view/4.xml”.

Another thing that also needed to change was the requirement to inherit from the base class before it would work.

Now you can decorate your controller or action with an attribute that will handle how your data will be rendered. It is also possible to disallow one or more of the formats.

I have of course submitted it to MvcContrib, but I don’t know if it will or when it will make it into the project. So, I hope that someone can benefit from my post in the meantime.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public sealed class FormatFilterAttribute : FilterAttribute, IActionFilter
{
    /// <summary>
    /// The type of files we can server the requested content in
    /// </summary>
    private enum FileFormat { Html, Json, Xml }
 
    public FormatFilterAttribute()
    {
        Disallow = string.Empty;
        RequestedFormat = FileFormat.Html;
    }
 
    /// <summary>
    /// Formats to disallow can be either Html, Json or Xml. Use comma to seperate multiple formats.
    /// </summary>
    public string Disallow { get; set; }
 
    /// <summary>
    /// The format that has been requested
    /// </summary>
    private FileFormat RequestedFormat { get; set; }
 
    /// <summary>
    /// Occurs before an action is executed
    /// </summary>
    /// <param name="filterContext"></param>
    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        FileFormat format = GetFileFormat(filterContext.HttpContext.Request.Path);
        if(IsDisallowed(format))
        {
            throw new ArgumentException("Requested format has been disallowed");
        }
 
        RequestedFormat = format;
    }
 
    /// <summary>
    /// Occurs after an action is executed
    /// </summary>
    /// <param name="filterContext"></param>
    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if(!(filterContext.Result is ViewResult))
        {
            throw new InvalidOperationException("You need to call the View method, when the FormatFilter attribute is applied");
        }
 
        var view = (ViewResult)(filterContext.Result);            
        filterContext.Result = FormatViewResult(view);
    }
 
    /// <summary>
    /// Verifies if the format has been disallowed
    /// </summary>
    /// <param name="format"></param>
    /// <returns></returns>
    private bool IsDisallowed(FileFormat format)
    {
        return Disallow.Split(',').Any(s => s.ToLower() == format.ToString().ToLower());
    }
 
    /// <summary>
    /// Verifies that the requested format is one that can be servered
    /// </summary>
    /// <param name="ext"></param>
    /// <returns></returns>
    private bool IsValidFileExtension(string ext)
    {
        return Enum.GetNames(typeof(FileFormat)).Any(format => format.ToLower() == ext.Substring(1).ToLower());
    }
 
    /// <summary>
    /// 
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    private FileFormat GetFileFormat(string path)
    {
        string ext = Path.GetExtension(path);
        if(string.IsNullOrEmpty(ext))
        {
            return FileFormat.Html;
        }
 
        if (!IsValidFileExtension(ext))
        {
            throw new FormatException("Requested format is not available");
        }
 
        return (FileFormat)Enum.Parse(typeof(FileFormat), ext.Substring(1), true);
    }
 
    private ActionResult FormatViewResult(ViewResultBase view)
    {
        switch (RequestedFormat)
        {
            case FileFormat.Html:
                return view;
            case FileFormat.Json:
                return new JsonResult { Data = view.ViewData.Model };
            case FileFormat.Xml:
                return new XmlResult(view.ViewData.Model);
            default:
                throw new FormatException(string.Concat("Cannot server the content in the request format: ", RequestedFormat));
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[FormatFilter]
public class HomeController : Controller
{      
    public ActionResult Index()
    {
        var pizzas = new[]
                         {
                             new Pizza {Number = 1, Name = "Pizza 1", Price = "$10"},
                             new Pizza {Number = 2, Name = "Pizza 2", Price = "$13"},
                             new Pizza {Number = 3, Name = "Pizza 3", Price = "$20"}
                         };
 
        return View(pizzas);
    }
}

Working with parameters

If you are going send data, like an ID, to your Action then you need to add an extra route or else it might not get parse correctly.

1
2
3
4
5
6
// Add this to Global.asax
routes.MapRoute(
	"Format",
	""{controller}/{action}/{id}.{format}"",
	new {id = "", format = ""}
);

How to serve the same data in Json, Xml or Html with Asp.Net MVC

A revised version can be found here

About four weeks ago I went to a small event for developers where one of the speakers (christian dalager) compared Ruby on Rails to Asp.Net Mvc. One of the things that really caught my attention in Ruby on Rails was the ability to have an Action serve content in various formats like Json, Xml or Html just by changing the file extension of the requested resource.

I thought it would be cool if you could do something similar in Asp.net MVC and found it to be quite easy, however if you want to serve your content in Xml, you need to download MvcContrib.

To do this, we need to create a new controller that we can inherit from.

The Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/// <summary>
/// A base controller that will enable you to serve your data in either Json, Xml, Html
/// </summary>
public abstract class FormatController : Controller
{
	/// <summary>
	/// The Key to extract the format from Route Data
	/// </summary>
	private const string FORMAT_KEY = "Format";
 
	/// <summary>
	/// The type of files we can server the requested content in
	/// </summary>
	public enum FileFormat { Html, Json, Xml }
 
	protected FormatController()
	{
		// Our Default format
		RequestedFormat = FileFormat.Html;
	}
 
	/// <summary>
	/// The format that has been requested
	/// </summary>
	protected FileFormat RequestedFormat { get; private set; }
 
	/// <summary>
	/// Occurs before an action is executed
	/// </summary>
	/// <param name="filterContext"></param>
	protected override void OnActionExecuting(ActionExecutingContext filterContext)
	{
		base.OnActionExecuting(filterContext);
 
		var routeValues = filterContext.RouteData.Values;
		if (routeValues.ContainsKey(FORMAT_KEY))
		{
			var requestedFormat = routeValues[FORMAT_KEY].ToString();
			if (IsValidFormat(requestedFormat))
			{
				RequestedFormat = (FileFormat)Enum.Parse(typeof(FileFormat), requestedFormat, true);
			}
		}
	}
 
	/// <summary>
	/// Verifies that the requested format is one that can be servered
	/// </summary>
	/// <param name="requestedFormat"></param>
	/// <returns></returns>
	private bool IsValidFormat(string requestedFormat)
	{
		return Enum.GetNames(typeof(FileFormat)).Any(format => format.ToLower() == requestedFormat.ToLower());
	}
 
	/// <summary>
	/// Returns the content in the requested format
	/// </summary>
	/// <param name="viewModel">Viewmodel</param>
	/// <returns>ActionResult</returns>
	protected ActionResult FormatView(object viewModel)
	{
		switch (RequestedFormat)
		{
			case FileFormat.Html:
				return View(viewModel);
			case FileFormat.Json:
				return Json(viewModel);
			case FileFormat.Xml:
				return new XmlResult(viewModel);
			default:
				throw new FormatException(string.Concat("Cannot server the content in the request format: ", RequestedFormat));
		}
	}
}

Setting it up

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Pizza
{
	public int Number { get; set; }
	public string Name { get; set; }
	public string Price { get; set; }
}
 
public class HomeController : FormatController
{
	public ActionResult Index()
	{
		var pizzas = new[]
			{
				new Pizza {Number = 1, Name = "Pizza 1", Price = "$10"},
				new Pizza {Number = 2, Name = "Pizza 2", Price = "$13"},
				new Pizza {Number = 3, Name = "Pizza 3", Price = "$20"}
			};
 
		return FormatView(pizzas);
	}
}
1
2
3
4
5
6
// Add this to Global.asax
routes.MapRoute(
	"Format",
	"{controller}/{action}.{format}/{id}",
	new {id = ""}
);

Fire up your application and then type something like “home/index.xml” or “home/index.json” in the browser.

Finale word

Imaging that on your website you have different categories that contain products, by applying this technique you will be able to create a REST like webservice for your users in no time, everyone will be able to integrate your website into their own.

Sharing data has never been easier, this is so awesome.

I have submittet this piece of code (and some other stuff) to MvcContrib, I hope that it makes into the project.

Further reading
A revised version can be found here
Controllers and Action Methods in MVC Applications

JSON (JavaScript Object Notation)

Next Page »