├── Martin ├── Enumerations.cs ├── obj │ └── Debug │ │ └── DesignTimeResolveAssemblyReferencesInput.cache ├── IActionFactory.cs ├── IContextAware.cs ├── IResponse.cs ├── IFilter.cs ├── IAction.cs ├── RouteAction.cs ├── EmptyResponse.cs ├── DefaultActionFactory.cs ├── FilterBase.cs ├── Config.cs ├── Martin.csproj.user ├── TextResponse.cs ├── ValueProvider.cs ├── XmlResponse.cs ├── JsonResponse.cs ├── ActionBuilder.cs ├── ActionHandler.cs ├── FileResponse.cs ├── Properties │ └── AssemblyInfo.cs ├── ActionBase.cs ├── ActionInvoker.cs └── Martin.csproj ├── SampleWeb ├── Global.asax ├── obj │ └── Debug │ │ └── DesignTimeResolveAssemblyReferencesInput.cache ├── DateAction.cs ├── Global.asax.cs ├── index.htm ├── SampleWeb.csproj.user ├── Properties │ └── AssemblyInfo.cs ├── SampleWeb.csproj └── Web.config ├── .gitignore ├── README.markdown ├── License.txt └── Martin.sln /Martin/Enumerations.cs: -------------------------------------------------------------------------------- 1 | namespace Martin { 2 | public enum HttpVerbs { GET, POST, PUT, DELETE } 3 | } 4 | -------------------------------------------------------------------------------- /SampleWeb/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="SampleWeb.Global" Language="C#" %> 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.ReSharper.user 2 | Martin.suo 3 | _ReSharper.Martin 4 | Martin\bin 5 | Martin\obj 6 | SampleWeb\bin 7 | SampleWeb\obj -------------------------------------------------------------------------------- /Martin/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegrubbsian/Martin/HEAD/Martin/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache -------------------------------------------------------------------------------- /SampleWeb/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegrubbsian/Martin/HEAD/SampleWeb/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache -------------------------------------------------------------------------------- /Martin/IActionFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Routing; 2 | 3 | namespace Martin { 4 | 5 | public interface IActionFactory { 6 | IAction CreateAction(RequestContext requestContext) where T : IAction; 7 | } 8 | } -------------------------------------------------------------------------------- /Martin/IContextAware.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Routing; 2 | 3 | namespace Martin { 4 | 5 | public interface IContextAware { 6 | RequestContext Context { get; } 7 | T ValueOf(string hashKey); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Martin/IResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Routing; 2 | 3 | namespace Martin { 4 | 5 | public interface IResponse { 6 | string ContentType { get; } 7 | void Respond(RequestContext requestContext); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Martin/IFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Routing; 2 | 3 | namespace Martin { 4 | 5 | public interface IFilter { 6 | RequestContext Context { get; set; } 7 | void BeforeExecute(); 8 | void AfterExecute(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Martin/IAction.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Routing; 2 | 3 | namespace Martin { 4 | 5 | public interface IAction { 6 | RequestContext Context { get; set; } 7 | IResponse Get(); 8 | IResponse Post(); 9 | IResponse Put(); 10 | IResponse Delete(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SampleWeb/DateAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Martin; 3 | 4 | namespace SampleWeb { 5 | 6 | public class DateAction : ActionBase { 7 | 8 | public override IResponse Get() { 9 | return Text(string.Format("The current date is {0}.", DateTime.Now)); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SampleWeb/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Martin; 3 | 4 | namespace SampleWeb { 5 | 6 | public class Global : System.Web.HttpApplication { 7 | 8 | protected void Application_Start(object sender, EventArgs e) { 9 | 10 | Config.AddAction("API/CurrentDate"); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Martin/RouteAction.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Routing; 3 | 4 | namespace Martin { 5 | 6 | public class RouteAction : IRouteHandler where T : IAction { 7 | 8 | public IHttpHandler GetHttpHandler(RequestContext requestContext) { 9 | return new ActionHandler(requestContext); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Martin/EmptyResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Routing; 2 | 3 | namespace Martin { 4 | 5 | public class EmptyResponse : IResponse { 6 | public string ContentType { get { return "text/text"; } } 7 | public void Respond(RequestContext requestContext) { 8 | requestContext.HttpContext.Response.Write(string.Empty); 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /Martin/DefaultActionFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Routing; 3 | 4 | namespace Martin { 5 | 6 | public class DefaultActionFactory : IActionFactory { 7 | 8 | public IAction CreateAction(RequestContext requestContext) where T : IAction { 9 | var action = Activator.CreateInstance(); 10 | action.Context = requestContext; 11 | return action; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Martin/FilterBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Routing; 3 | 4 | namespace Martin { 5 | 6 | public class FilterBase : Attribute, IFilter, IContextAware { 7 | 8 | public RequestContext Context { get; set; } 9 | public virtual void BeforeExecute() {} 10 | public virtual void AfterExecute() {} 11 | 12 | public T ValueOf(string hashKey) { 13 | return ValueProvider.ValueOf(Context, hashKey); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Martin/Config.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Routing; 2 | 3 | namespace Martin { 4 | 5 | public static class Config { 6 | 7 | public static void AddAction(string routeUrl) where T : IAction { 8 | RouteTable.Routes.Add(new Route(routeUrl, new RouteAction())); 9 | } 10 | 11 | public static void SetActionFactory(IActionFactory actionFactory) { 12 | ActionBuilder.Current.SetActionFactory(actionFactory); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Martin/Martin.csproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | en-US 11 | false 12 | 13 | -------------------------------------------------------------------------------- /Martin/TextResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Routing; 2 | 3 | namespace Martin { 4 | 5 | public class TextResponse : IResponse { 6 | 7 | private readonly string _text = string.Empty; 8 | 9 | public string ContentType { get { return "text/plain"; } } 10 | 11 | public TextResponse(string text) { 12 | _text = text; 13 | } 14 | 15 | public void Respond(RequestContext requestContext) { 16 | requestContext.HttpContext.Response.Write(_text); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Martin/ValueProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Routing; 3 | 4 | namespace Martin { 5 | 6 | public static class ValueProvider { 7 | 8 | public static T ValueOf(RequestContext requextContext, string hashKey) { 9 | 10 | var obj = default(T); 11 | 12 | if (requextContext.RouteData.Values.ContainsKey(hashKey)) 13 | obj = (T)Convert.ChangeType(requextContext.RouteData.Values[hashKey], typeof(T)); 14 | else if (requextContext.HttpContext.Request[hashKey] != null) 15 | obj = (T)Convert.ChangeType(requextContext.HttpContext.Request[hashKey], typeof(T)); 16 | 17 | return obj; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Martin/XmlResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Routing; 2 | using System.Xml.Serialization; 3 | 4 | namespace Martin { 5 | 6 | public class XmlResponse : IResponse { 7 | 8 | private readonly object _data; 9 | 10 | public string ContentType { get { return "text/xml"; } } 11 | 12 | public XmlResponse(object data) { 13 | _data = data; 14 | } 15 | 16 | public void Respond(RequestContext requestContext) { 17 | var xs = new XmlSerializer(_data.GetType()); 18 | requestContext.HttpContext.Response.ContentType = ContentType; 19 | xs.Serialize(requestContext.HttpContext.Response.Output, _data); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Martin is a lightweight web framework for .NET inspired by ruby's Sinatra. 2 | ================================================================================= 3 | 4 | Martin is a simple .NET web framework based on the idea of mapping URLs directly to actions. An action in Martin vernacular is a class that can implement one or more of the four major HTTP verbs (GET, POST, PUT, DELETE) as instance methods. When a request comes in the appropriate action class is instantiated and based on the request verb the correct method is invoked, plain and simple. 5 | 6 | If you would like to contribute please feel free to fork the repo, make some changes, and submit a pull request...all enhancements are welcome. If you'd like to write some documentation please feel free to send me anything you have and I'll add it to the wiki. -------------------------------------------------------------------------------- /Martin/JsonResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Web.Routing; 4 | using System.Web.Script.Serialization; 5 | 6 | namespace Martin { 7 | 8 | public class JsonResponse : IResponse { 9 | 10 | private readonly object _data; 11 | 12 | public string ContentType { get { return "text/json"; } } 13 | 14 | public JsonResponse(object data) { 15 | if (data is IEnumerable) 16 | throw new ApplicationException("Security Warning: Arrays should not be used as the top level container for JSON packages."); 17 | _data = data; 18 | } 19 | 20 | public void Respond(RequestContext requestContext) { 21 | var serializer = new JavaScriptSerializer(); 22 | requestContext.HttpContext.Response.Write(serializer.Serialize(_data)); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /SampleWeb/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sample AJAX Page Using Martin 5 | 6 | 18 | 19 | 20 | 21 | Get Current Date 22 | 23 |

24 | 25 |
26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Martin/ActionBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Martin { 4 | 5 | public class ActionBuilder { 6 | 7 | private Func _factoryActivator; 8 | private static readonly ActionBuilder _instance = new ActionBuilder(); 9 | 10 | public static ActionBuilder Current { 11 | get { return _instance; } 12 | } 13 | 14 | public ActionBuilder() { 15 | var defaultActionFactory = new DefaultActionFactory(); 16 | _factoryActivator = () => defaultActionFactory; 17 | } 18 | 19 | public void SetActionFactory(IActionFactory actionFactory) { 20 | if (actionFactory == null) { 21 | throw new ArgumentNullException("actionFactory"); 22 | } 23 | _factoryActivator = () => actionFactory; 24 | } 25 | 26 | public IActionFactory GetActionFactory() { 27 | return _factoryActivator(); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Martin/ActionHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Routing; 3 | 4 | namespace Martin { 5 | 6 | public class ActionHandler : IHttpHandler where T : IAction { 7 | 8 | public RequestContext Context { get; private set; } 9 | 10 | public bool IsReusable { 11 | get { return false; } 12 | } 13 | 14 | private ActionBuilder _actionBuilder; 15 | internal ActionBuilder ActionBuilder { 16 | get { 17 | if (_actionBuilder == null) 18 | _actionBuilder = ActionBuilder.Current; 19 | return _actionBuilder; 20 | } 21 | } 22 | 23 | public ActionHandler(RequestContext requestContext) { 24 | Context = requestContext; 25 | } 26 | 27 | public void ProcessRequest(HttpContext context) { 28 | var action = ActionBuilder.Current.GetActionFactory().CreateAction(Context); 29 | ActionInvoker.Execute(action, Context); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /SampleWeb/SampleWeb.csproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | CurrentPage 10 | True 11 | False 12 | False 13 | False 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | False 23 | True 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | Martin may be used free of charge under the conditions of the following license. 2 | ------------------------------------------------------------------------------- 3 | The MIT License 4 | 5 | Copyright (c) 2010 Frank (JC) Grubbs - jc.grubbs@devmynd.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. -------------------------------------------------------------------------------- /Martin/FileResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Web.Routing; 4 | 5 | namespace Martin { 6 | 7 | public class FileResponse : IResponse { 8 | 9 | private readonly string _path; 10 | 11 | public string ContentType { 12 | get { 13 | if (!String.IsNullOrEmpty(_path)) { 14 | // get the content type from the registry 15 | var ext = System.IO.Path.GetExtension(_path).ToLower(); 16 | var regKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext); 17 | // if the extension is not in the registry it could be added manually on the server 18 | if (regKey != null && regKey.GetValue("Content Type") != null) { 19 | return regKey.GetValue("Content Type").ToString(); 20 | } 21 | } 22 | return "application/unknown"; 23 | } 24 | } 25 | 26 | public FileResponse(string path) { 27 | _path = path; 28 | } 29 | 30 | public void Respond(RequestContext requestContext) { 31 | var info = new FileInfo(_path); 32 | requestContext.HttpContext.Response.ContentType = ContentType; 33 | requestContext.HttpContext.Response.Write(info.Extension); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Martin.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleWeb", "SampleWeb\SampleWeb.csproj", "{9BFE8C61-DE99-49CB-965B-1995BCCBB68E}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Martin", "Martin\Martin.csproj", "{22990F91-C8E8-46B8-A793-7D3712B9112C}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {9BFE8C61-DE99-49CB-965B-1995BCCBB68E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {9BFE8C61-DE99-49CB-965B-1995BCCBB68E}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {9BFE8C61-DE99-49CB-965B-1995BCCBB68E}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {9BFE8C61-DE99-49CB-965B-1995BCCBB68E}.Release|Any CPU.Build.0 = Release|Any CPU 18 | {22990F91-C8E8-46B8-A793-7D3712B9112C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {22990F91-C8E8-46B8-A793-7D3712B9112C}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {22990F91-C8E8-46B8-A793-7D3712B9112C}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {22990F91-C8E8-46B8-A793-7D3712B9112C}.Release|Any CPU.Build.0 = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /SampleWeb/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SampleWeb")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("SampleWeb")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2009")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("3d5900ae-111a-45be-96b3-d9e4606ca793")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /Martin/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Martin")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("Martin")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2009")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("7c85b26c-e564-43fd-a20d-fcb94c6bff33")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Martin/ActionBase.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Routing; 3 | 4 | namespace Martin { 5 | 6 | public abstract class ActionBase : IAction, IContextAware { 7 | 8 | public RequestContext Context { get; set; } 9 | 10 | public HttpRequestBase Request { 11 | get { return Context.HttpContext.Request; } 12 | } 13 | 14 | public HttpResponseBase Response { 15 | get { return Context.HttpContext.Response; } 16 | } 17 | 18 | public T ValueOf(string hashKey) { 19 | return ValueProvider.ValueOf(Context, hashKey); 20 | } 21 | 22 | public virtual IResponse Get() { return new EmptyResponse(); } 23 | public virtual IResponse Post() { return new EmptyResponse(); } 24 | public virtual IResponse Put() { return new EmptyResponse(); } 25 | public virtual IResponse Delete() { return new EmptyResponse(); } 26 | 27 | #region IResponse Helpers 28 | 29 | public static TextResponse Text(string text) { 30 | return new TextResponse(text); 31 | } 32 | 33 | public static JsonResponse Json(object data) { 34 | return new JsonResponse(data); 35 | } 36 | 37 | public static XmlResponse Xml(object data) { 38 | return new XmlResponse(data); 39 | } 40 | 41 | public static FileResponse File(string path) { 42 | return new FileResponse(path); 43 | } 44 | 45 | public static EmptyResponse Empty() { 46 | return new EmptyResponse(); 47 | } 48 | 49 | #endregion 50 | } 51 | } -------------------------------------------------------------------------------- /Martin/ActionInvoker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Web.Routing; 6 | 7 | namespace Martin { 8 | 9 | public static class ActionInvoker { 10 | 11 | public static void Execute(IAction action, RequestContext requestContext) { 12 | 13 | var httpVerb = (HttpVerbs)Enum.Parse(typeof(HttpVerbs), action.Context.HttpContext.Request.HttpMethod, true); 14 | 15 | if (!HasHttpMethod(action, httpVerb)) 16 | throw new ApplicationException("The HTTP Verb '" + Enum.GetName(typeof(HttpVerbs), httpVerb) + "' did not have a matching method on the specified action."); 17 | 18 | RunPreExecutionFilters(action, httpVerb); 19 | 20 | switch (httpVerb) { 21 | case HttpVerbs.GET: 22 | action.Get().Respond(requestContext); 23 | break; 24 | case HttpVerbs.POST: 25 | action.Post().Respond(requestContext); 26 | break; 27 | case HttpVerbs.PUT: 28 | action.Put().Respond(requestContext); 29 | break; 30 | case HttpVerbs.DELETE: 31 | action.Delete().Respond(requestContext); 32 | break; 33 | } 34 | 35 | RunPostExecutionFilters(action, httpVerb); 36 | } 37 | 38 | private static List GetFilters(IAction action, HttpVerbs httpVerb) { 39 | var methodInfo = GetMethod(action, httpVerb); 40 | return methodInfo.GetCustomAttributes(typeof(IFilter), false).Select(x => (IFilter)x).ToList(); 41 | } 42 | 43 | private static MethodInfo GetMethod(IAction action, HttpVerbs httpVerb) { 44 | return action.GetType().GetMethods().ToList().Find( 45 | x => x.Name.ToUpperInvariant() == Enum.GetName(typeof(HttpVerbs), httpVerb)); 46 | } 47 | 48 | private static void RunPreExecutionFilters(IAction action, HttpVerbs httpVerb) { 49 | var filters = GetFilters(action, httpVerb); 50 | filters.ForEach(x => { 51 | x.Context = action.Context; 52 | x.BeforeExecute(); 53 | }); 54 | } 55 | 56 | private static void RunPostExecutionFilters(IAction action, HttpVerbs httpVerb) { 57 | var filters = GetFilters(action, httpVerb); 58 | filters.ForEach(x => { 59 | x.Context = action.Context; 60 | x.AfterExecute(); 61 | }); 62 | } 63 | 64 | private static bool HasHttpMethod(IAction action, HttpVerbs httpVerb) { 65 | return action.GetType().GetMethods().ToList().Find( 66 | x => x.Name.ToLowerInvariant() == Enum.GetName(typeof(HttpVerbs), httpVerb).ToLowerInvariant()) != null; 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /SampleWeb/SampleWeb.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 9.0.30729 7 | 2.0 8 | {9BFE8C61-DE99-49CB-965B-1995BCCBB68E} 9 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 10 | Library 11 | Properties 12 | SampleWeb 13 | SampleWeb 14 | v3.5 15 | 16 | 17 | 3.5 18 | 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | AllRules.ruleset 29 | 30 | 31 | pdbonly 32 | true 33 | bin\ 34 | TRACE 35 | prompt 36 | 4 37 | AllRules.ruleset 38 | 39 | 40 | 41 | 42 | 43 | 3.5 44 | 45 | 46 | 3.5 47 | 48 | 49 | 3.5 50 | 51 | 52 | 3.5 53 | 54 | 55 | 3.5 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | Global.asax 74 | 75 | 76 | 77 | 78 | 79 | {22990F91-C8E8-46B8-A793-7D3712B9112C} 80 | Martin 81 | 82 | 83 | 84 | 85 | 92 | 93 | 94 | 95 | 96 | False 97 | True 98 | 7404 99 | / 100 | 101 | 102 | False 103 | False 104 | 105 | 106 | False 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /Martin/Martin.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 9.0.30729 7 | 2.0 8 | {22990F91-C8E8-46B8-A793-7D3712B9112C} 9 | Library 10 | Properties 11 | Martin 12 | Martin 13 | v3.5 14 | 512 15 | 16 | 17 | 3.5 18 | 19 | publish\ 20 | true 21 | Disk 22 | false 23 | Foreground 24 | 7 25 | Days 26 | false 27 | false 28 | true 29 | 0 30 | 1.0.0.%2a 31 | false 32 | false 33 | true 34 | 35 | 36 | true 37 | full 38 | false 39 | bin\Debug\ 40 | DEBUG;TRACE 41 | prompt 42 | 4 43 | AllRules.ruleset 44 | 45 | 46 | pdbonly 47 | true 48 | bin\Release\ 49 | TRACE 50 | prompt 51 | 4 52 | AllRules.ruleset 53 | 54 | 55 | 56 | 57 | 3.5 58 | 59 | 60 | 61 | 3.5 62 | 63 | 64 | 3.5 65 | 66 | 67 | 3.5 68 | 69 | 70 | 3.5 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 | False 101 | .NET Framework 3.5 SP1 Client Profile 102 | false 103 | 104 | 105 | False 106 | .NET Framework 3.5 SP1 107 | true 108 | 109 | 110 | False 111 | Windows Installer 3.1 112 | true 113 | 114 | 115 | 116 | 123 | -------------------------------------------------------------------------------- /SampleWeb/Web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 |
9 |
10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 38 | 39 | 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 | --------------------------------------------------------------------------------