Commits

Edison Chuang committed 55e8ead Merge

Merge with develop. The project has been updated to 0.11.0.0.

This verison includes a number of new features and updates listed below.
* The feature of supporting ActionName attribute was added.
* Demo project was refined. Include several improvements such as more clear
introduction of each demo item, more stylish interface...etc.

Comments (0)

Files changed (27)

source/MVC.ApiExplorer.Demo/Areas/AnArea/AnAreaAreaRegistration.cs

 		public override void RegisterArea(AreaRegistrationContext context)
 		{
 			context.MapRoute(
+				"AnArea_InArea",
+				"AnArea/api/{action}",
+				new { action = "Index", controller = "InArea"},
+				new[] { "The.controller.belong.to.an.area" }
+			);
+
+			context.MapRoute(
+				"AnArea_OutterAPI",
+				"AnArea/outer/{action}",
+				new { action = "Index", controller = "ExploreOutterAPI" },
+				new[] { "The.controller.belong.to.an.area" }
+			);
+
+			context.MapRoute(
 				"AnArea_default",
 				"AnArea/{controller}/{action}/{id}",
 				new { action = "Index", id = UrlParameter.Optional },

source/MVC.ApiExplorer.Demo/Areas/AnArea/Controllers/Default1Controller.cs

-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Web;
-using System.Web.Mvc;
-using MVC.ApiExplorer;
-
-namespace The.controller.belong.to.an.area
-{
-	public class Default1Controller : Controller
-	{
-		[NonApi]
-		public ActionResult Index()
-		{
-			return View();
-		}
-
-
-		public ActionResult AreaAPI()
-		{
-			return Json(new { Success = true }, JsonRequestBehavior.AllowGet);
-		}
-	}
-}

source/MVC.ApiExplorer.Demo/Areas/AnArea/Controllers/Default2Controller.cs

-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Web;
-using System.Web.Mvc;
-
-namespace The.controller.belong.to.an.area
-{
-	public class Default2Controller : Controller
-	{
-		public ActionResult Index()
-		{
-			return View();
-		}
-	}
-}

source/MVC.ApiExplorer.Demo/Areas/AnArea/Controllers/Default3Controller.cs

-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Web;
-using System.Web.Mvc;
-
-namespace The.controller.belong.to.an.area
-{
-	public class Default3Controller : Controller
-	{
-		public ActionResult Index()
-		{
-			return View();
-		}
-
-	}
-}

source/MVC.ApiExplorer.Demo/Areas/AnArea/Controllers/ExploreOutterAPIController.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using System.Web.Mvc;
+
+namespace The.controller.belong.to.an.area
+{
+	public class ExploreOutterAPIController : Controller
+	{
+		public ActionResult Index()
+		{
+			return View();
+		}
+
+	}
+}

source/MVC.ApiExplorer.Demo/Areas/AnArea/Controllers/InAreaController.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using System.Web.Mvc;
+using MVC.ApiExplorer;
+using System.ComponentModel;
+
+namespace The.controller.belong.to.an.area
+{
+	public class InAreaController : Controller
+	{
+		[NonApi]
+		public ActionResult Index()
+		{
+			return View();
+		}
+		
+		[HttpPost]
+		[Description("This api is belong to an area and accepts http-post invocation only.")]
+		public ActionResult PostData(string msg, int value)
+		{
+			return Json(new { msg=msg, value=value });
+		}
+	}
+}

source/MVC.ApiExplorer.Demo/Areas/AnArea/Views/Default1/Index.cshtml

-@Html.Action("Explore", "ActionExplore", new { area = "", controllerType = typeof(The.controller.belong.to.an.area.Default1Controller) })

source/MVC.ApiExplorer.Demo/Areas/AnArea/Views/Default2/Index.cshtml

-@Html.Action("Explore", "ActionExplore", new { area = "", controllerType = typeof(The.controller.belong.to.an.area.Default1Controller) })

source/MVC.ApiExplorer.Demo/Areas/AnArea/Views/Default3/Index.cshtml

-@{Html.RenderAction("Explore", "ActionExplore", new { area = "", controllerType = typeof(The.controller.you.want.to.explore.HomeController), routeName = "Default" });}

source/MVC.ApiExplorer.Demo/Areas/AnArea/Views/ExploreOutterAPI/Index.cshtml

+@{Html.RenderAction("Explore", "ActionExplore", new { area = "", routeName = "Default_API",
+	controllerType = typeof(The.controller.you.want.to.explore.APIController) });}

source/MVC.ApiExplorer.Demo/Areas/AnArea/Views/InArea/Index.cshtml

+@Html.Action("Explore", "ActionExplore", 
+	new { area = "", controllerType = typeof(The.controller.belong.to.an.area.InAreaController) })

source/MVC.ApiExplorer.Demo/Content/css/reset.css

+/* http://meyerweb.com/eric/tools/css/reset/ 
+   v2.0 | 20110126
+   License: none (public domain)
+*/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed, 
+figure, figcaption, footer, header, hgroup, 
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+	margin: 0;
+	padding: 0;
+	border: 0;
+	font-size: 100%;
+	font: inherit;
+	vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure, 
+footer, header, hgroup, menu, nav, section {
+	display: block;
+}
+body {
+	line-height: 1;
+}
+ol, ul {
+	list-style: none;
+}
+blockquote, q {
+	quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+	content: '';
+	content: none;
+}
+table {
+	border-collapse: collapse;
+	border-spacing: 0;
+}

source/MVC.ApiExplorer.Demo/Content/css/style.css

+body {
+	background-color: #FFFFFF;
+	color: #1B1919;
+	font: normal 16px "Helvetica Neue","HelveticaNeue",Helvetica,Arial,sans-serif;
+	line-height: 1.5;
+}
+
+h1
+{
+	font-size: 36px;
+}
+
+.section
+{
+	padding:20px;
+	min-width:600px;
+}
+
+.header
+{
+	background-color: #f24114;
+	color: #fff;
+}
+
+.content
+{
+	width: 760px;
+}
+
+.footer
+{
+	width: 760px;
+}
+
+.demo
+{
+	margin:20px 0;
+}
+
+.demo-item
+{
+	margin:15px 0;
+	padding:5px 10px;
+	border-left:4px solid #f24114;
+}

source/MVC.ApiExplorer.Demo/Controllers/APIController.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using System.Web.Mvc;
+using System.ComponentModel;
+using System.Text;
+using MVC.ApiExplorer;
+using MVC.ApiExplorer.Demo.Models;
+
+namespace The.controller.you.want.to.explore
+{
+	public class APIController : Controller
+	{
+		[NonApi]
+		[Description("As it be decorated with NonApi. This action won't be displayed in test page.")]
+		public ActionResult Index()
+		{
+			return View();
+		}
+
+		[Description("A normal page I want to be testable.")]
+		public ActionResult NormalPage()
+		{
+			return View();
+		}
+
+		[HttpGet]
+		[Description("An action which accepts http-get invocation only.")]
+		public ActionResult GetOnly(string msg, [Description("This value is a nullable integer.")]int? value)
+		{
+			return Json(new { msg = msg, value = value }, JsonRequestBehavior.AllowGet);
+		}
+
+		[HttpPost]
+		[ValidateAntiForgeryToken]
+		[Description("An action which accepts http-post invocation only.")]
+		public ActionResult PostOnly(string msg, string value1, int value2, bool valut3)
+		{
+			return Json(new { msg = msg, value1 = value1, value2 = value2, value3 = valut3 });
+		}
+
+		[HttpPost]
+		[ValidateAntiForgeryToken(Salt="SaltValue")]
+		[Description("An action which accepts http-post invocation only and validates an anit-forgery toekn with a salt value.")]
+		public ActionResult PostOnlyAndValidWithSalt(string msg, string value)
+		{
+			return Json(new { msg = msg, value = value });
+		}
+
+		[HttpPut]
+		[ValidateAntiForgeryToken]
+		[Description("An action which accepts http-put invocation only.")]
+		public ActionResult PutOnly(string msg, string value)
+		{
+			return Json(new { msg = msg, value = value });
+		}
+
+		[HttpDelete]
+		[ValidateAntiForgeryToken]
+		[Description("An action which accepts http-delete invocation only.")]
+		public ActionResult DeleteOnly(string msg, string value)
+		{
+			return Json(new { msg = msg, value = value });
+		}
+
+		[Description("Get the javascript snippet.")]
+		public ActionResult GetJavascript(string msg)
+		{
+			return JavaScript(string.Format("//this is a javascript code.\r\nalert('{0}');", msg));
+		}
+
+		[HttpPost]
+		[Description("Upload a file to service.")]
+		public ActionResult UploadFile(HttpPostedFileBase file)
+		{
+			return Json(new { Message = "Received a file from client.", FileName = file.FileName });
+		}
+
+		[HttpPost]
+		[Description("Download a file form service.")]
+		public ActionResult DownloadFile()
+		{
+			var data = Encoding.UTF8.GetBytes("Dummy content.");
+			return File(data, "application/zip", "DummyFile.txt");
+		}
+
+		[HttpPost]
+		[ActionName("changed-action-name")]
+		[Description("An action which the name was changed by decorating with an ActionName attritube.")]
+		public ActionResult OriginalActionName(string msg, string value1, int value2 = 0, bool valut3 = false)
+		{
+			return Json(new { msg = msg, value1 = value1, value2 = value2, value3 = valut3 });
+		}
+
+	}
+}

source/MVC.ApiExplorer.Demo/Controllers/ComplexModelAPIController.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using System.Web.Mvc;
+using System.ComponentModel;
+using System.Text;
+using MVC.ApiExplorer;
+using MVC.ApiExplorer.Demo.Models;
+
+namespace The.controller.you.want.to.explore
+{
+	public class ComplexModelAPIController : Controller
+	{
+		[NonApi]
+		public ActionResult Index()
+		{
+			return View();
+		}
+
+		[HttpPost]
+		[Description("Complex model binding")]
+		public ActionResult ComplexModelBinding(Person person1, Person person2, List<Person> people)
+		{
+			return Json(new { People = new[]{person1, person2} });
+		}
+
+	}
+}

source/MVC.ApiExplorer.Demo/Controllers/EnumerableAPIController.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using System.Web.Mvc;
+using System.ComponentModel;
+using System.Text;
+using MVC.ApiExplorer;
+using MVC.ApiExplorer.Demo.Models;
+
+namespace The.controller.you.want.to.explore
+{
+	public class EnumerableAPIController : Controller
+	{
+		[NonApi]
+		public ActionResult Index()
+		{
+			return View();
+		}
+
+		[HttpPost]
+		[Description("Enumerable primitive types")]
+		public ActionResult EnumerablePrimitiveTypes(List<string> msgs, List<int> values)
+		{
+			return Json(new { Messages = msgs, Values = values });
+		}
+
+		[HttpPost]
+		[Description("Enumerable complex types")]
+		public ActionResult EnumerableComplexTypes(List<Person> people)
+		{
+			return Json(new { People = people });
+		}
+	}
+}

source/MVC.ApiExplorer.Demo/Controllers/HomeController.cs

 {
 	public class HomeController : Controller
 	{
-		[NonApi]
-		[Description("That will be displayed on test page")]
 		public ActionResult Index()
 		{
 			return View();
 		}
-
-		[Description("A normal page I want to be testable.")]
-		public ActionResult SomePage()
-		{
-			return new ContentResult()
-			{
-				ContentType = "text/html",
-				Content =
-@"<html>
-<head><title>MVC.ApiExplorer</title></head>
-<body>
-<strong>Dummy</strong> content.
-</body>
-</html>"
-			};
-		}
-
-		[HttpGet]
-		[Description("An action which accepts http-get invocation only.")]
-		public ActionResult GetOnly(string msg, int? value)
-		{
-			return Json(new { msg = msg, value = value }, JsonRequestBehavior.AllowGet);
-		}
-
-		[HttpPost]
-		[ValidateAntiForgeryToken]
-		[Description("An action which accepts http-post invocation only.")]
-		public ActionResult PostOnly([Description("This is a descriptioin for the field")]string msg, string value1, int value2, bool valut3)
-		{
-			return Json(new { msg = msg, value1 = value1, value2 = value2, value3 = valut3 });
-		}
-
-		[HttpPost]
-		[ValidateAntiForgeryToken(Salt="SaltValue")]
-		[Description("An action which accepts http-post invocation only and validates anit forgery toekn with a salt value.")]
-		public ActionResult PostOnlyAndValidWithSalt(string msg, string value)
-		{
-			return Json(new { msg = msg, value = value });
-		}
-
-		[HttpPut]
-		[ValidateAntiForgeryToken]
-		[Description("An action which accepts http-put invocation only.")]
-		public ActionResult PutOnly(string msg, string value)
-		{
-			return Json(new { msg = msg, value = value });
-		}
-
-		[HttpDelete]
-		[ValidateAntiForgeryToken]
-		[Description("An action which accepts http-delete invocation only.")]
-		public ActionResult DeleteOnly(string msg, string value)
-		{
-			return Json(new { msg = msg, value = value });
-		}
-
-		[Description("Get the javascript snippet.")]
-		public ActionResult GetJavascript(string msg)
-		{
-			return JavaScript(string.Format("alert('{0}');", msg));
-		}
-
-		[HttpPost]
-		[Description("Uing this to upload a file.")]
-		public ActionResult UploadFile(HttpPostedFileBase file)
-		{
-			return Json(new { FileName=file.FileName });
-		}
-
-		[HttpPost]
-		[Description("Download a file form service.")]
-		public ActionResult DownloadFile()
-		{
-			var data = Encoding.UTF8.GetBytes("Dummy content.");
-			return File(data, "application/zip", "DummyFile.txt");
-		}
-
-		[HttpPost]
-		[Description("Enumerable primitive types")]
-		public ActionResult EnumerablePrimitiveTypes(List<string>msgs, List<int> values)
-		{
-			return Json(new { Messages = msgs, Values = values });
-		}
-
-		[HttpPost]
-		[Description("Enumerable complex types")]
-		public ActionResult EnumerableComplexTypes(List<Person> people)
-		{
-			return Json(new { People = people });
-		}
-
-		[HttpPost]
-		[Description("Complex model binding")]
-		public ActionResult ComplexModelBinding(Person person1, Person person2, List<Person> people)
-		{
-			return Json(new { People = new[]{person1, person2} });
-		}
 	}
 }

source/MVC.ApiExplorer.Demo/Global.asax.cs

 			);
 
 			routes.MapRoute(
+				"Default_API", // Route name
+				"api/{action}", // URL with parameters
+				new { controller = "API", action = "Index"}, // Parameter defaults
+				new[] { "The.controller.you.want.to.explore" }
+			);
+
+			routes.MapRoute(
+				"Default_Complex_Model_API", // Route name
+				"complex_model_api/{action}", // URL with parameters
+				new { controller = "ComplexModelAPI", action = "Index" }, // Parameter defaults
+				new[] { "The.controller.you.want.to.explore" }
+			);
+
+			routes.MapRoute(
+				"Default_Enumerable_API", // Route name
+				"enumerable_api/{action}", // URL with parameters
+				new { controller = "EnumerableAPI", action = "Index" }, // Parameter defaults
+				new[] { "The.controller.you.want.to.explore" }
+			);
+
+			routes.MapRoute(
 				"Default", // Route name
 				"{action}/{id}", // URL with parameters
 				new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults

source/MVC.ApiExplorer.Demo/MVC.ApiExplorer.Demo.csproj

   </ItemGroup>
   <ItemGroup>
     <Compile Include="Areas\AnArea\AnAreaAreaRegistration.cs" />
-    <Compile Include="Areas\AnArea\Controllers\Default1Controller.cs" />
-    <Compile Include="Areas\AnArea\Controllers\Default2Controller.cs" />
-    <Compile Include="Areas\AnArea\Controllers\Default3Controller.cs" />
+    <Compile Include="Areas\AnArea\Controllers\InAreaController.cs" />
+    <Compile Include="Areas\AnArea\Controllers\ExploreOutterAPIController.cs" />
+    <Compile Include="Controllers\APIController.cs" />
+    <Compile Include="Controllers\ComplexModelAPIController.cs" />
+    <Compile Include="Controllers\EnumerableAPIController.cs" />
     <Compile Include="Controllers\HomeController.cs" />
     <Compile Include="Global.asax.cs">
       <DependentUpon>Global.asax</DependentUpon>
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   <ItemGroup>
+    <Content Include="Content\css\reset.css" />
+    <Content Include="Content\css\style.css" />
     <Content Include="Global.asax" />
-    <Content Include="Web.config" />
+    <Content Include="Web.config">
+      <SubType>Designer</SubType>
+    </Content>
     <Content Include="Web.Debug.config">
       <DependentUpon>Web.config</DependentUpon>
     </Content>
   <ItemGroup>
     <Folder Include="Areas\AnArea\Models\" />
     <Folder Include="Areas\AnArea\Views\Shared\" />
-    <Folder Include="Views\Shared\" />
   </ItemGroup>
   <ItemGroup>
     <Content Include="Views\Home\Index.cshtml" />
     <Content Include="Areas\AnArea\Views\Web.config" />
   </ItemGroup>
   <ItemGroup>
-    <Content Include="Areas\AnArea\Views\Default1\Index.cshtml" />
+    <Content Include="Areas\AnArea\Views\InArea\Index.cshtml" />
   </ItemGroup>
   <ItemGroup>
-    <Content Include="Areas\AnArea\Views\Default2\Index.cshtml" />
+    <Content Include="Areas\AnArea\Views\ExploreOutterAPI\Index.cshtml" />
   </ItemGroup>
   <ItemGroup>
-    <Content Include="Areas\AnArea\Views\Default3\Index.cshtml" />
+    <Content Include="Views\API\Index.cshtml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Views\ComplexModelAPI\Index.cshtml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Views\EnumerableAPI\Index.cshtml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Views\Shared\_Layout.cshtml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Views\API\NormalPage.cshtml" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />

source/MVC.ApiExplorer.Demo/Views/API/Index.cshtml

+@{Html.RenderAction("Explore", "ActionExplore",
+	  new {controllerType = typeof(The.controller.you.want.to.explore.APIController) });}

source/MVC.ApiExplorer.Demo/Views/API/NormalPage.cshtml

+@{
+    Layout = null;
+}
+
+<!DOCTYPE html>
+
+<html>
+<head>
+    <title>NormalPage</title>
+</head>
+<body>
+    <div>
+    A normal page.
+    </div>
+</body>
+</html>

source/MVC.ApiExplorer.Demo/Views/ComplexModelAPI/Index.cshtml

+@{Html.RenderAction("Explore", "ActionExplore",
+	  new { controllerType = typeof(The.controller.you.want.to.explore.ComplexModelAPIController) });}

source/MVC.ApiExplorer.Demo/Views/EnumerableAPI/Index.cshtml

+@{Html.RenderAction("Explore", "ActionExplore",
+	  new { controllerType = typeof(The.controller.you.want.to.explore.EnumerableAPIController) });}

source/MVC.ApiExplorer.Demo/Views/Home/Index.cshtml

-@{Html.RenderAction("Explore", "ActionExplore", new { controllerType = typeof(The.controller.you.want.to.explore.HomeController) });}
+@{
+	Layout = "~/Views/Shared/_Layout.cshtml";
+}
+
+<p>
+This is a demo website that demostrates how MVC.ApiExplorer can help you on developing APIs in an ASP.NET MVC project.
+</p>
+
+<ul class="demo">
+	<li class="demo-item">
+		<a href="@Url.Action("Index", "API")" target="_blank">Basic Api</a> (@Url.Action("Index", "API"))
+		<p>
+		This part of example demonstrates that the most basic APIs people made on an ASP.MVC project. 
+		You can find that MVC.ApiExplorer automaticlly generates all actions (and it's parameters) of API controller except "Index" action which decorated with "NonApi" attribute.
+		</p>
+		<p>
+		To get more understand please take a look at source of APIController.cs
+		</p>
+	</li>
+	<li class="demo-item">
+		<a href="@Url.Action("Index", "ComplexModelAPI")" target="_blank"s>Complex Model API</a> (@Url.Action("Index", "ComplexModelAPI"))
+		<p>
+		This part of example demonstrates that how MVC.ApiExplorer works with complex model binding. 
+		MVC.ApiExplorer automaticlly generates all properties of complex model and support for 3 deep of subclasses.
+		</p>
+		<p>
+		To get more understand please take a look at source of ComplexModelAPIController.cs
+		</p>
+	</li>
+	<li class="demo-item">
+		<a href="@Url.Action("Index", "EnumerableAPI")" target="_blank">Enumerable API</a> (@Url.Action("Index", "EnumerableAPI"))
+		<p>
+		This part of example demonstrates that how MVC.ApiExplorer works with enumerable parameters. 
+		MVC.ApiExplorer automaticlly generates all enumerable parameters of each action for 3 times.
+		</p>
+		<p>
+		To get more understand please take a look at source of EnumerableAPIController.cs
+		</p>
+	</li>
+	<li class="demo-item">
+		<a href="@Url.Action("Index", "InArea", new { area="AnArea"})" target="_blank">APIs in an area</a> (@Url.Action("Index", "InArea", new { area = "AnArea" }))
+		<p>
+		This part of example demonstrates that APIs which belong to an area.
+		</p>
+		<p>
+		To get more understand please take a look at source of InAreaController.cs and its View.
+		</p>
+	</li>
+	<li class="demo-item">
+		<a href="@Url.Action("Index", "ExploreOutterAPI", new { area = "AnArea" })" target="_blank">Explore APIs which out of current area</a> (@Url.Action("Index", "ExploreOutterAPI", new { area = "AnArea" }))
+		<p>
+		This part of example demonstrates that how to explore APIs which <strong>NOT</strong> belong to current area.
+		In this demo. We explore "Basic Api" in the area.
+		</p>
+		<p>
+		To get more understand please take a look at source of ExploreOutterAPIController.cs and its View.
+		</p>
+	</li>
+</ul>
+
+@section javascript{
+
+}

source/MVC.ApiExplorer.Demo/Views/Shared/_Layout.cshtml

+<!DOCTYPE html>
+<html>
+<head>
+	<meta charset="utf-8">
+	<title>MVC.ApiExplorer Demo - @ViewBag.Title</title>
+	<link rel="stylesheet" href="@Url.Content("Content/css/reset.css")">
+	<link rel="stylesheet" href="@Url.Content("Content/css/style.css")">
+</head>
+<body>
+	
+	<div class="header section">
+		<h1>MVC.ApiExplorer Demo</h1>
+	</div>
+	<div class="content section">
+		@RenderBody()
+	</div>
+	<div class="footer ">
+	</div>
+
+@RenderSection("javascript")
+</body>
+</html>

source/MVC.ApiExplorer/ApiExploreView.cs

 			
 			foreach (MethodInfo m in (model))
 			{
-				var actionUrl = GetActionUrl(routeName, viewContext, controllerName, m.Name);
+				var actionName = getActionName(m);
+				var actionUrl = getActionUrl(routeName, viewContext, controllerName, actionName);
 
-				html.AppendFormat(@"<li class=""api"">({1}) <strong class=""api-name"">{0}</strong><br />", m.Name, m.ReturnType.Name);
-				html.AppendFormat("<p>{0}</p>", GetDescription(m));
-				RenderHttpMethod(html, controllerName, m.Name);
-				html.AppendFormat(@"<form id=""{0}_{1}_form"" action=""{2}"" method=""get"" target=""output""  enctype=""multipart/form-data"">", controllerName, m.Name, actionUrl);
-				RenderAntiForgeryToken(viewContext, html, m);
+				html.AppendFormat(@"<li class=""api"">({1}) <strong class=""api-name"">{0}</strong><br />", actionName, m.ReturnType.Name);
+				html.AppendFormat("<p>{0}</p>", getDescription(m));
+				renderHttpMethod(html, controllerName, actionName);
+				html.AppendFormat(@"<form id=""{0}_{1}_form"" action=""{2}"" method=""get"" target=""output""  enctype=""multipart/form-data"">", controllerName, actionName, actionUrl);
+				renderAntiForgeryToken(viewContext, html, m);
 				html.Append("<table cellspacing=\"0\">");
 				var parameters = m.GetParameters();
 				foreach (var parameter in parameters)
 				{
 					Type fieldType = parameter.ParameterType;
 					string fieldName = parameter.Name;
-					string fieldDescription = GetDescription(parameter);
+					string fieldDescription = getDescription(parameter);
 
-					RenderField(htmlHelper, html, 0, fieldType, "", fieldName, fieldDescription);
+					renderField(htmlHelper, html, 0, fieldType, "", fieldName, fieldDescription);
 				}
 
 				html.Append("</table>");
 
 			writer.Write(html.ToString());
 		}
-		private static void RenderComplexField(HtmlHelper htmlHelper, StringBuilder html, string FieldNamespace, PropertyInfo[] properties, int deep)
+		private static void renderComplexField(HtmlHelper htmlHelper, StringBuilder html, string FieldNamespace, PropertyInfo[] properties, int deep)
 		{
 			html.AppendFormat(@"<fieldset class=""api-subfields""><legend>{0}:</legend>", FieldNamespace);
 			html.Append("<table cellspacing=\"0\">");
 			{
 				Type fieldType = property.PropertyType;
 				string fieldName = property.Name;
-				string fieldDescription = GetDescription(property);
+				string fieldDescription = getDescription(property);
 
-				RenderField(htmlHelper, html, deep, fieldType, FieldNamespace, fieldName, fieldDescription);
+				renderField(htmlHelper, html, deep, fieldType, FieldNamespace, fieldName, fieldDescription);
 			}
 			html.Append("</table>");
 			html.Append(@"</fieldset>");
 		}
-		private static void RenderField(HtmlHelper htmlHelper, StringBuilder html, int deep, Type fieldType, string fieldNamespace, string fieldName, string fieldDescription)
+		private static void renderField(HtmlHelper htmlHelper, StringBuilder html, int deep, Type fieldType, string fieldNamespace, string fieldName, string fieldDescription)
 		{
 			html.Append("<tr>");
-			if (IsGeneralField(fieldType))
+			if (isGeneralField(fieldType))
 			{
-				RenderInput(htmlHelper, html, fieldType, fieldNamespace, fieldName, fieldDescription, false);
+				renderInput(htmlHelper, html, fieldType, fieldNamespace, fieldName, fieldDescription, false);
 			}
-			else if (IsFileUploadField(fieldType))
+			else if (isFileUploadField(fieldType))
 			{
-				RenderInput(htmlHelper, html, fieldType, fieldNamespace, fieldName, fieldDescription, true);
+				renderInput(htmlHelper, html, fieldType, fieldNamespace, fieldName, fieldDescription, true);
 			}
 			else
 			{
 				//Complex model binding
-				if (IsDerivedFromGenericType(fieldType, typeof(IEnumerable<>)))
+				if (isDerivedFromGenericType(fieldType, typeof(IEnumerable<>)))
 				{
 					//Enumerable type
 					var innerFieldType = fieldType.GetGenericArguments()[0];
-					bool isComplexType = !IsGeneralField(innerFieldType) && !IsFileUploadField(innerFieldType);
+					bool isComplexType = !isGeneralField(innerFieldType) && !isFileUploadField(innerFieldType);
 					
 					for (int i = 0; i < 3; i++)
 					{
 						html.Append(htmlHelper.Hidden(fieldName + ".Index", i).ToHtmlString());
 						string enumerablefieldName = string.Format("{0}[{1}]", fieldName, i);
-						RenderField(htmlHelper, html, deep, innerFieldType, fieldNamespace, enumerablefieldName, fieldDescription);
+						renderField(htmlHelper, html, deep, innerFieldType, fieldNamespace, enumerablefieldName, fieldDescription);
 					}
 				}
 				else
 						deep++;
 						var subProperties = fieldType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
 						fieldNamespace = string.IsNullOrEmpty(fieldNamespace) ? fieldName : string.Format("{0}.{1}", fieldNamespace, fieldName);
-						RenderComplexField(htmlHelper, html, fieldNamespace, subProperties, deep);
+						renderComplexField(htmlHelper, html, fieldNamespace, subProperties, deep);
 					}
 					html.Append("</td>");
 				}
 			html.Append("</tr>");
 		}
 
-		private static void RenderInput(HtmlHelper htmlHelper, StringBuilder html, Type fieldType, string fieldNamespace, string fieldName, string fieldDescription, bool isFileUpload)
+		private static void renderInput(HtmlHelper htmlHelper, StringBuilder html, Type fieldType, string fieldNamespace, string fieldName, string fieldDescription, bool isFileUpload)
 		{
 			var name = string.IsNullOrEmpty(fieldNamespace) ? fieldName : string.Format("{0}.{1}", fieldNamespace, fieldName);
 			html.AppendFormat(@"<td>({1}) <span>{0}:</span></td><td>{3}</td><td>{2}</td>",
 				name,
-				SimplifyTypeName(fieldType),
+				simplifyTypeName(fieldType),
 				fieldDescription,
 				isFileUpload ? htmlHelper.TextBox(name, "", new { type="file"}) : htmlHelper.TextBox(name)
 				);
 		}
-		private static void RenderHttpMethod(StringBuilder html, string controllerName, string methodName)
+		private static void renderHttpMethod(StringBuilder html, string controllerName, string methodName)
 		{
 			html.AppendFormat(
 @"<fieldset class=""api-fields"">
 <input type=""radio"" id=""{0}_{1}_delete"" name=""{0}_{1}"" onchange=""var form = document.getElementById('{0}_{1}_form'); form.setAttribute('method','post'); var inputTag = document.getElementById('{0}_{1}_put_http_method_override'); if(inputTag){{form.removeChild(inputTag);}} inputTag = document.createElement('input'); inputTag.id='{0}_{1}_delete_http_method_override'; inputTag.type='hidden'; inputTag.name='X-HTTP-Method-Override'; inputTag.value='DELETE'; form.appendChild( inputTag );"" ><label for=""{0}_{1}_delete"">DELETE</label><br />",
 controllerName, methodName);
 		}
-		private static void RenderAntiForgeryToken(ViewContext viewContext, StringBuilder html, MethodInfo m)
+		private static void renderAntiForgeryToken(ViewContext viewContext, StringBuilder html, MethodInfo m)
 		{
 			if (m.GetCustomAttributes(typeof(ValidateAntiForgeryTokenAttribute), true).Length > 0)
 			{
 			}
 		}
 
-		private static string GetDescription(ParameterInfo parameter)
+		private static string getDescription(ParameterInfo parameter)
 		{
 			var attributes = parameter.GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false);
 			return attributes.Length > 0 ? ((DescriptionAttribute)attributes[0]).Description : string.Empty;
 		}
-		private static string GetDescription(PropertyInfo property)
+		private static string getDescription(PropertyInfo property)
 		{
 			var attributes = property.GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false);
 			return attributes.Length > 0 ? ((DescriptionAttribute)attributes[0]).Description : string.Empty;
 		}
-		private static string GetDescription(MethodInfo method)
+		private static string getDescription(MethodInfo method)
 		{
 			var attributes = method.GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false);
 			return attributes.Length > 0 ? ((DescriptionAttribute)attributes[0]).Description : string.Empty;
 		}
 
-		private static bool IsGeneralField(Type fieldType)
+		private static bool isGeneralField(Type fieldType)
 		{
 			return fieldType.IsPrimitive
 						|| fieldType.Equals(typeof(string))
 						|| fieldType.Equals(typeof(decimal))
 						|| (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Nullable<>));//Determine whether a field type object represents a Nullable type
 		}
-		private static bool IsFileUploadField(Type fieldType)
+		private static bool isFileUploadField(Type fieldType)
 		{
 			return fieldType.Equals(typeof(HttpPostedFileBase));
 		}
-		private static bool IsDerivedFromGenericType(Type type, Type genericType)
+		private static bool isDerivedFromGenericType(Type type, Type genericType)
 		{
 			if (!type.IsGenericType)
 			{
 				return false;
 			}
 			
-			return IsDerivedFromGenericType(type.BaseType, genericType);
+			return isDerivedFromGenericType(type.BaseType, genericType);
 		}
 
 		/// <summary>
 		/// Try to get the action url with specific route name.
 		/// </summary>
-		private string GetActionUrl(string routeName, ViewContext viewContext, string controllerName, string actionName)
+		private string getActionUrl(string routeName, ViewContext viewContext, string controllerName, string actionName)
 		{
 			var url = UrlHelper.GenerateUrl(routeName, actionName, controllerName, null, RouteTable.Routes, viewContext.RequestContext, true);
 			if (url == null)
 			{
-				url = GetActionUrl(viewContext, controllerName, actionName);
+				url = getActionUrl(viewContext, controllerName, actionName);
 			}
 
 			return url;
 		/// <summary>
 		/// Try to get the first matched action url.
 		/// </summary>
-		private string GetActionUrl(ViewContext viewContext, string controllerName, string actionName)
+		private string getActionUrl(ViewContext viewContext, string controllerName, string actionName)
 		{
 			var url = "";
 			FieldInfo field = typeof(RouteCollection).GetField("_namedMap", BindingFlags.Instance | BindingFlags.NonPublic);
 			return url;
 		}
 
-		private static string SimplifyTypeName(Type type)
+		private static string simplifyTypeName(Type type)
 		{
 			var result = "";
 			if (type.Equals(typeof(Byte)))
 			else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
 			{
 				var arg = type.GetGenericArguments()[0];
-				result = SimplifyTypeName(arg) + "?";
+				result = simplifyTypeName(arg) + "?";
 			}
 			else if (type.Equals(typeof(HttpPostedFileBase)))
 			{
 			return result;
 		}
 
+		private static string getActionName(MethodInfo m)
+		{
+			string result = m.Name;
+			var attrs = m.GetCustomAttributes(typeof(ActionNameAttribute), true);
+			if(attrs.Length > 0)
+			{
+				var attr = ((ActionNameAttribute)attrs[0]);
+				result = attr.Name;
+			}
+			return result;
+		}
 	}
 }

source/MVC.ApiExplorer/Properties/AssemblyInfo.cs

 
 [assembly: Guid("d5630fe9-8e88-4c43-b075-72133326b2a7")]
 
-[assembly: AssemblyVersion("0.10.0.0")]
-[assembly: AssemblyFileVersion("0.10.0.0")]
+[assembly: AssemblyVersion("0.11.0.0")]
+[assembly: AssemblyFileVersion("0.11.0.0")]
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.