Skip to content

Commit b097a33

Browse files
committed
Merge pull request #19 from panesofglass/master
Merge all files to better support source downloads via tools like Paket.
2 parents 93a2f08 + 171e0b4 commit b097a33

17 files changed

+462
-239
lines changed

WebApiContrib.Formatting.Razor.sln

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 2012
3+
# Visual Studio 2013
4+
VisualStudioVersion = 12.0.31101.0
5+
MinimumVisualStudioVersion = 10.0.40219.1
46
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiContrib.Formatting.Razor", "src\WebApiContrib.Formatting.Razor\WebApiContrib.Formatting.Razor.csproj", "{8C15B0D3-9108-4EA3-AE19-3C9571266C47}"
57
EndProject
68
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MvcWebApiSiteTest", "samples\MvcWebApiSiteTest\MvcWebApiSiteTest.csproj", "{5D90E626-1C40-4B23-887A-DC2527D696CB}"
@@ -9,6 +11,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiContrib.Razor.Tests",
911
EndProject
1012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiContrib.Formatting.Razor.Templating", "src\WebApiContrib.Formatting.Razor.Templating\WebApiContrib.Formatting.Razor.Templating.csproj", "{36287DD3-6930-4C13-93B7-06FE600278F9}"
1113
EndProject
14+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fsharp", "fsharp", "{1258C816-9FA3-43F5-85C7-AA2BE6E842DE}"
15+
ProjectSection(SolutionItems) = preProject
16+
fsharp\HtmlTemplateBase.fs = fsharp\HtmlTemplateBase.fs
17+
fsharp\WebApiContrib.Formatting.Razor.fs = fsharp\WebApiContrib.Formatting.Razor.fs
18+
EndProjectSection
19+
EndProject
1220
Global
1321
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1422
Debug|Any CPU = Debug|Any CPU

fsharp/HtmlTemplateBase.fs

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
namespace WebApiContrib.Formatting.Razor
2+
3+
#if INTERACTIVE
4+
#I "../packages"
5+
#r "System.Net.Http"
6+
#r "Microsoft.AspNet.Mvc.5.0.0/lib/net45/System.Web.Mvc.dll"
7+
#r "Microsoft.AspNet.Razor.3.0.0/lib/net45/System.Web.Razor.dll"
8+
#r "Microsoft.AspNet.WebApi.Client.5.0.0/lib/net45/System.Net.Http.Formatting.dll"
9+
#r "Microsoft.AspNet.WebApi.Core.5.0.0/lib/net45/System.Web.Http.dll"
10+
#r "RazorEngine.3.3.0/lib/net40/RazorEngine.dll"
11+
#r "WebApiContrib.Formatting.Html.2.0.0/lib/net45/WebApiContrib.Formatting.Html.dll"
12+
#endif
13+
14+
open System
15+
open System.Collections.Generic
16+
open System.IO
17+
open System.Web
18+
open System.Web.Mvc
19+
open System.Web.Routing
20+
open RazorEngine.Configuration
21+
open RazorEngine.Templating
22+
open RazorEngine.Text
23+
24+
/// <summary>
25+
/// A <see cref="TemplateBase"/> class for use when rendering HTML with Razor in Web API applications.
26+
/// </summary>
27+
/// <typeparam name="T">The Model type.</typeparam>
28+
[<AbstractClass>]
29+
[<RequireNamespaces("System.Web.Mvc.Html")>]
30+
type HtmlTemplateBase<'T>() =
31+
inherit TemplateBase<'T>()
32+
33+
let urlHelper = lazy (new System.Web.Http.Routing.UrlHelper())
34+
let mutable viewData : ViewDataDictionary = null
35+
36+
/// <summary>
37+
/// Writes markup to the specified <paramref name="writer"/>, encoding by default
38+
/// except in cases where the <paramref name="value"/> is already encoded.
39+
/// </summary>
40+
/// <param name="writer">The current <see cref="TextWriter"/>.</param>
41+
/// <param name="value">The value to write to the <paramref name="writer"/>.</param>
42+
/// <see href="http://stackoverflow.com/questions/19431365/razorengine-html-helpers-work-but-escape-the-html"/>
43+
override this.WriteTo(writer: TextWriter, value: obj) =
44+
if writer = null then
45+
raise (new ArgumentNullException("writer"))
46+
47+
if value = null then () else
48+
let valueType = value.GetType()
49+
if typeof<IEncodedString>.IsAssignableFrom(valueType) then
50+
let encodedString = unbox<IEncodedString> value
51+
writer.Write(encodedString)
52+
elif typeof<IHtmlString>.IsAssignableFrom(valueType) then
53+
let htmlString = unbox<IHtmlString> value
54+
writer.Write(htmlString.ToHtmlString())
55+
else
56+
// This was the base template's implementation:
57+
writer.Write(this.TemplateService.EncodedStringFactory.CreateEncodedString(value))
58+
59+
/// <summary>
60+
/// Returns a <see cref="System.Web.Mvc.HtmlHelper{T}"/> based on the current Model type.
61+
/// </summary>
62+
member this.Html =
63+
// NOTE: Removed caching, as that would block updates the ViewData property, which has a setter.
64+
let writer = this.CurrentWriter
65+
let viewContext = new ViewContext(Writer = writer, ViewData = this.ViewData)
66+
new HtmlHelper<'T>(viewContext, this)
67+
68+
/// <summary>
69+
/// Returns a <see cref="System.Web.Http.Routing.UrlHelper"/>.
70+
/// </summary>
71+
/// <remarks>
72+
/// This is not currently tied to the current <see cref="System.Net.Http.HttpRequestMessage"/> but should be.
73+
/// </remarks>
74+
member this.Url = urlHelper.Force()
75+
76+
/// <summary>
77+
/// Returns a <see cref="ViewDataDictionary"/> typed to the Model's type.
78+
/// </summary>
79+
member this.ViewData
80+
with get() : ViewDataDictionary =
81+
if viewData = null then
82+
viewData <- new ViewDataDictionary<'T>(TemplateInfo = new TemplateInfo(HtmlFieldPrefix = ""), Model = this.Model)
83+
viewData
84+
and set(value) =
85+
viewData <- value
86+
87+
interface IViewDataContainer with
88+
member this.ViewData
89+
with get() = this.ViewData
90+
and set(value) = this.ViewData <- value
+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
namespace WebApiContrib.Formatting.Razor
2+
3+
#if INTERACTIVE
4+
#I "../packages"
5+
#r "System.Net.Http"
6+
#r "Microsoft.AspNet.WebApi.Client.5.0.0/lib/net45/System.Net.Http.Formatting.dll"
7+
#r "Microsoft.AspNet.WebApi.Core.5.0.0/lib/net45/System.Web.Http.dll"
8+
#r "Microsoft.AspNet.Razor.2.0.30506.0/lib/net40/System.Web.Razor.dll"
9+
#r "RazorEngine.3.3.0/lib/net40/RazorEngine.dll"
10+
#r "WebApiContrib.Formatting.Html.2.0.0/lib/net45/WebApiContrib.Formatting.Html.dll"
11+
#endif
12+
13+
open System
14+
open System.IO
15+
open System.Reflection
16+
open RazorEngine.Configuration
17+
open RazorEngine.Templating
18+
open WebApiContrib.Formatting.Html
19+
open WebApiContrib.Formatting.Html.Formatting
20+
21+
type RazorViewLocator() =
22+
let viewLocationFormats =
23+
[| "~\\Views\\{0}.cshtml"
24+
"~\\Views\\{0}.vbhtml"
25+
"~\\Views\\Shared\\{0}.cshtml"
26+
"~\\Views\\Shared\\{0}.vbhtml" |]
27+
28+
static member internal GetPhysicalSiteRootPath(siteRootPath: string) =
29+
if String.IsNullOrWhiteSpace(siteRootPath) then
30+
Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase)
31+
.Replace("file:\\", String.Empty)
32+
.Replace("\\bin", String.Empty)
33+
.Replace("\\Debug", String.Empty)
34+
.Replace("\\Release", String.Empty)
35+
else siteRootPath
36+
37+
interface IViewLocator with
38+
member this.GetView(siteRootPath, view) =
39+
if view = Unchecked.defaultof<_> then
40+
raise (new ArgumentNullException("view"))
41+
42+
let path = RazorViewLocator.GetPhysicalSiteRootPath(siteRootPath)
43+
44+
let result = seq {
45+
for viewLocationFormat in viewLocationFormats do
46+
let potentialViewPathFormat = viewLocationFormat.Replace("~", RazorViewLocator.GetPhysicalSiteRootPath(siteRootPath));
47+
48+
let viewPath = String.Format(potentialViewPathFormat, view.ViewName);
49+
50+
if File.Exists(viewPath) then
51+
yield File.ReadAllText(viewPath) }
52+
53+
if Seq.isEmpty result then
54+
raise (new FileNotFoundException(String.Format("Can't find a view with the name '{0}.cshtml' or '{0}.vbhtml in the '\\Views' folder under path '{1}'", view.ViewName, path)))
55+
else Seq.head result
56+
57+
type TemplateResolver() =
58+
interface ITemplateResolver with
59+
member this.Resolve(name) =
60+
//Replace the "~/" to the root path of the web.
61+
let name = name.Replace("~", RazorViewLocator.GetPhysicalSiteRootPath(null)).Replace("/", "\\")
62+
63+
if not (File.Exists(name)) then
64+
raise (new FileNotFoundException(name))
65+
66+
File.ReadAllText(name)
67+
68+
// Type passed should be located at the root of the folder structure where the embedded templates are located
69+
type EmbeddedResolver(rootLocatorType: Type) =
70+
interface ITemplateResolver with
71+
member this.Resolve(name) =
72+
// To locate embedded files,
73+
// - they must be marked as "Embedded Resource"
74+
// - you must use a case senstive path and filename
75+
// - the namespaces and project folder names must match.
76+
//
77+
name = name.Replace("~/", "").Replace("/", ".") //Convert "web path" to "resource path"
78+
let viewStream = rootLocatorType.Assembly.GetManifestResourceStream(rootLocatorType, name)
79+
use reader = new StreamReader(viewStream)
80+
reader.ReadToEnd()
81+
82+
/// <summary>
83+
/// An <see cref="IViewParser"/> for Razor templates.
84+
/// </summary>
85+
/// <param name="templateService">The <see cref="ITemplateService"/>.</param>
86+
type RazorViewParser(templateService: ITemplateService) =
87+
88+
do if templateService = Unchecked.defaultof<_> then
89+
raise (new ArgumentNullException("templateService"))
90+
91+
/// <summary>
92+
/// Initializes a new <see cref="RazorViewParser"/> with the specified <paramref name="ITemplateServiceConfiguration"/>.
93+
/// </summary>
94+
/// <param name="config">The <see cref="ITemplateServiceConfiguration"/>.</param>
95+
new (config: ITemplateServiceConfiguration) =
96+
new RazorViewParser(new TemplateService(config) :> ITemplateService)
97+
98+
/// <summary>
99+
/// Initializes a new <see cref="RazorViewParser"/> with optional arguments.
100+
/// </summary>
101+
/// <param name="resolver">The <see cref="ITemplateResolver"/>. If not provided, <see cref="TemplateResolver"/> is used.</param>
102+
/// <param name="baseTemplateType">The <see cref="Type"/> to use as the TemplateBase.</param>
103+
new (?resolver: ITemplateResolver, ?baseTemplateType: Type) =
104+
let resolver = defaultArg resolver (new TemplateResolver() :> ITemplateResolver)
105+
let baseTemplateType = defaultArg baseTemplateType Unchecked.defaultof<_>
106+
let config = new TemplateServiceConfiguration(BaseTemplateType = baseTemplateType, Resolver = resolver)
107+
new RazorViewParser(new TemplateService(config) :> ITemplateService)
108+
109+
interface IViewParser with
110+
/// <summary>
111+
/// Parses the <paramref name="viewTemplate"/> with the provided <paramref name="view"/>.
112+
/// </summary>
113+
/// <param name="view">The <see cref="IView"/>.</param>
114+
/// <param name="viewTemplate">The view template to parse.</param>
115+
/// <param name="encoding">The <see cref="System.Text.Encoding"/> to use in parsing.</param>
116+
/// <returns>The <see cref="byte[]"/> representing the parsed view.</returns>
117+
member this.ParseView(view, viewTemplate, encoding) =
118+
templateService.Compile(viewTemplate, view.ModelType, view.ViewName)
119+
let parsedView = templateService.Run(view.ViewName, view.Model, null)
120+
encoding.GetBytes(parsedView);
121+
122+
/// <summary>
123+
/// <see cref="HtmlMediaTypeFormatter"/> using the Razor syntax.
124+
/// </summary>
125+
/// <param name="siteRootPath">The root path containing view files. This defaults to "~/Views".</param>
126+
/// <param name="viewLocator">The <see cref="IViewLocator"/> instance used to locate the correct view. This defaults to <see cref="RazorViewLocator"/>.</param>
127+
/// <param name="viewParser">The <see cref="IViewParser"/> instance used to parse the view. This defaults to <see cref="RazorViewParser"/>.</param>
128+
[<Sealed>]
129+
type RazorViewFormatter(?siteRootPath: string, ?viewLocator: IViewLocator, ?viewParser: IViewParser) =
130+
inherit HtmlMediaTypeViewFormatter(
131+
defaultArg siteRootPath null,
132+
defaultArg viewLocator (new RazorViewLocator() :> IViewLocator),
133+
defaultArg viewParser (new RazorViewParser() :> IViewParser))

samples/MvcWebApiSiteTest/Controllers/CustomerController.cs

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
using System.Web.Http;
2+
using WebApiContrib.Formatting.Html;
23

34
namespace MvcWebApiSiteTest.Controllers
45
{
56
public class CustomerController : ApiController
67
{
7-
//// GET api/customer
8-
////public View Get()
9-
////{
10-
//// return new View("CustomerViaView", new Customer { Name = "John Doe", Country = "Sweden" });
11-
////}
12-
138
// GET api/customer
14-
public Customer Get()
9+
public ViewResult Get()
10+
{
11+
return new ViewResult(this.Request, "CustomerViaViewResult", new Customer { Name = "John Doe", Country = "Sweden" });
12+
}
13+
14+
// GET api/customer/1
15+
public Customer Get(int id)
1516
{
1617
return new Customer { Name = "John Doe", Country = "Sweden" };
1718
}

samples/MvcWebApiSiteTest/MvcWebApiSiteTest.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@
236236
<Content Include="Views\Customer.cshtml" />
237237
<Content Include="Views\CustomerViaAttrib.cshtml" />
238238
<Content Include="Views\CustomerViaConfig.cshtml" />
239-
<Content Include="Views\CustomerViaView.cshtml" />
239+
<Content Include="Views\CustomerViaViewResult.cshtml" />
240240
<Content Include="Views\Shared\HttpError.cshtml" />
241241
<Content Include="Views\Index.cshtml" />
242242
</ItemGroup>

samples/MvcWebApiSiteTest/Views/Customer.cshtml

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616

1717
<script type="text/javascript">
1818
$("button").click(function () {
19-
$.ajax({
20-
url: '/customer',
19+
$.ajax({
20+
url: '/customer/1',
2121
type: "GET",
2222
contentType: "application/json; charset=utf-8",
2323
success: function(data, status, xhr) {
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
1-
<!DOCTYPE html>
2-
<html>
3-
<head>
4-
<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.5.1.min.js" type="text/javascript"></script>
5-
</head>
6-
<body>
7-
<div id="body">
8-
<section>
9-
<header>
10-
<h1>Welcome '@Model.Name' to ASP.NET Web API Razor Formatter! Using View to locate View.</h1>
11-
</header>
12-
<p>Using the same URL "api/values" but using AJAX: <button>Press to show content!</button></p>
13-
<p></p>
14-
</section>
15-
</div>
16-
17-
<script type="text/javascript">
18-
$("button").click(function () {
19-
$.ajax({
20-
url: '/customer',
21-
type: "GET",
22-
contentType: "application/json; charset=utf-8",
23-
success: function(data, status, xhr) {
24-
alert(data.Model.Name);
25-
},
26-
error: function(xhr, status, error) {
27-
alert(error);
28-
}
29-
});
30-
});
31-
</script>
32-
</body>
33-
</html>
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.5.1.min.js" type="text/javascript"></script>
5+
</head>
6+
<body>
7+
<div id="body">
8+
<section>
9+
<header>
10+
<h1>Welcome '@Model.Name' to ASP.NET Web API Razor Formatter! Using View to locate View.</h1>
11+
</header>
12+
<p>Using the same URL "api/values" but using AJAX: <button>Press to show content!</button></p>
13+
<p></p>
14+
</section>
15+
</div>
16+
17+
<script type="text/javascript">
18+
$("button").click(function () {
19+
$.ajax({
20+
url: '/customer',
21+
type: "GET",
22+
contentType: "application/json; charset=utf-8",
23+
success: function(data, status, xhr) {
24+
alert(data);
25+
},
26+
error: function(xhr, status, error) {
27+
alert(error);
28+
}
29+
});
30+
});
31+
</script>
32+
</body>
33+
</html>

src/WebApiContrib.Formatting.Razor/EmbeddedResolver.cs

-31
This file was deleted.

0 commit comments

Comments
 (0)