1 // Copyright 2004-2007 Castle Project - http://www.castleproject.org/
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle
.MonoRail
.Framework
.Services
18 using System
.Collections
;
19 using System
.Collections
.Specialized
;
21 using Castle
.MonoRail
.Framework
.Internal
;
25 /// Default implementation of <see cref="IUrlBuilder"/>
28 /// The property <see cref="UseExtensions"/> defines whether the builder should output
29 /// file extension. This might be handy to use in combination with a url rewrite strategy
32 /// If you want to create a custom urlbuilder, you can extend this one and override
33 /// the <see cref="InternalBuildUrl(string,string,string,string,string,string,string,string,string,bool,bool,string)"/>
37 /// <seealso cref="BuildUrl(UrlInfo,IDictionary)"/>
38 public class DefaultUrlBuilder
: IUrlBuilder
, IServiceEnabledComponent
40 private bool useExtensions
= true;
41 private IServerUtility serverUtil
;
42 private IRoutingEngine routingEng
;
45 /// Initializes a new instance of the <see cref="DefaultUrlBuilder"/> class.
47 public DefaultUrlBuilder()
54 /// Gets or sets a value indicating whether the builder should output an extension.
56 /// <value><c>true</c> if should use extensions; otherwise, <c>false</c>.</value>
57 public bool UseExtensions
59 get { return useExtensions; }
60 set { useExtensions = value; }
64 /// Gets or sets the server utility instance.
66 /// <value>The server util.</value>
67 public IServerUtility ServerUtil
69 get { return serverUtil; }
70 set { serverUtil = value; }
74 /// Gets or sets the routing engine.
76 /// <value>The routing engine.</value>
77 public IRoutingEngine RoutingEngine
79 get { return routingEng; }
80 set { routingEng = value; }
85 #region IServiceEnabledComponent
88 /// Services the specified provider.
90 /// <param name="provider">The provider.</param>
91 public void Service(IServiceProvider provider
)
93 serverUtil
= (IServerUtility
) provider
.GetService(typeof(IServerUtility
));
94 routingEng
= (IRoutingEngine
) provider
.GetService(typeof(IRoutingEngine
));
102 /// Builds the URL using the current url as contextual information and a parameter dictionary.
104 /// Common parameters includes area, controller and action.
107 /// <param name="current">The current Url information.</param>
108 /// <param name="parameters">The parameters.</param>
109 /// <returns></returns>
110 public UrlPartsBuilder
CreateUrlPartsBuilder(UrlInfo current
, IDictionary parameters
)
112 string routeName
= CommonUtils
.ObtainEntryAndRemove(parameters
, "named");
113 bool encode
= CommonUtils
.ObtainEntryAndRemove(parameters
, "encode", "false") == "true";
115 if (routeName
!= null)
117 object routeParams
= parameters
["params"];
118 parameters
.Remove("params");
120 return InternalBuildRouteUrl(current
.Domain
, current
.AppVirtualDir
, routeName
, routeParams
, encode
);
124 bool applySubdomain
= false;
125 bool createAbsolutePath
= CommonUtils
.ObtainEntryAndRemove(parameters
, "absolute", "false") == "true";
129 if (parameters
.Contains("area"))
131 area
= CommonUtils
.ObtainEntryAndRemove(parameters
, "area");
133 if (area
== null) area
= string.Empty
;
140 string controller
= CommonUtils
.ObtainEntryAndRemove(parameters
, "controller", current
.Controller
);
141 string action
= CommonUtils
.ObtainEntryAndRemove(parameters
, "action", current
.Action
);
143 string domain
= CommonUtils
.ObtainEntryAndRemove(parameters
, "domain", current
.Domain
);
144 string subdomain
= CommonUtils
.ObtainEntryAndRemove(parameters
, "subdomain", current
.Subdomain
);
145 string protocol
= CommonUtils
.ObtainEntryAndRemove(parameters
, "protocol", current
.Protocol
);
146 string port
= CommonUtils
.ObtainEntryAndRemove(parameters
, "port", current
.Port
.ToString());
147 string pathInfo
= CommonUtils
.ObtainEntryAndRemove(parameters
, "pathinfo");
148 string suffix
= null;
150 object queryString
= CommonUtils
.ObtainObjectEntryAndRemove(parameters
, "querystring");
152 string basePath
= null;
154 if (parameters
.Contains("basepath"))
156 basePath
= CommonUtils
.ObtainEntryAndRemove(parameters
, "basepath");
159 if (queryString
!= null)
161 if (queryString
is IDictionary
)
163 IDictionary qsDictionary
= (IDictionary
) queryString
;
165 suffix
= CommonUtils
.BuildQueryString(serverUtil
, qsDictionary
, encode
);
167 else if (queryString
is NameValueCollection
)
169 suffix
= CommonUtils
.BuildQueryString(serverUtil
, (NameValueCollection
) queryString
, encode
);
171 else if (queryString
is string)
173 suffix
= queryString
.ToString();
177 if (subdomain
.ToLower() != current
.Subdomain
.ToLower())
179 applySubdomain
= true;
182 return InternalBuildUrl(area
, controller
, action
, protocol
, port
, domain
, subdomain
,
183 current
.AppVirtualDir
, current
.Extension
, createAbsolutePath
, applySubdomain
, suffix
,
189 /// Builds the URL using the current url as contextual information and a parameter dictionary.
195 /// Common parameters includes <c>area</c>, <c>controller</c> and <c>action</c>, which outputs
196 /// <c>/area/controller/name.extension</c>
200 /// Please note that if you dont specify an area or controller name, they will be inferred from the
201 /// context. If you want to use an empty area, you must specify <c>area=''</c>.
202 /// This is commonly a source of confusion, so understand the following cases:
207 /// UrlInfo current = ... // Assume that the current is area Admin, controller Products and action List
209 /// BuildUrl(current, {action: 'view'})
210 /// // returns /Admin/Products/view.castle
212 /// BuildUrl(current, {controller: 'Home', action: 'index'})
213 /// // returns /Admin/Home/index.castle
215 /// BuildUrl(current, {area:'', controller: 'Home', action: 'index'})
216 /// // returns /Home/index.castle
221 /// The <c>querystring</c> parameter can be a string or a dictionary. It appends a query string to the url:
222 /// <c>/area/controller/name.extension?id=1</c>
226 /// The <c>absolute</c> parameter forces the builder to output a full url like
227 /// <c>http://hostname/virtualdir/area/controller/name.extension</c>
231 /// The <c>pathinfo</c> parameter can be used to add information between the resource identifier and the
232 /// query string (optional)
233 /// <c>http://hostname/virtualdir/area/controller/name.extension/path/info/here</c> or
234 /// <c>http://hostname/virtualdir/area/controller/name.extension/path/info/here?key=value</c>
238 /// The <c>encode</c> parameter forces the builder to encode the querystring
239 /// <c>/controller/name.extension?id=1&name=John</c> which is required to output full xhtml compliant content.
243 /// <param name="current">The current Url information.</param>
244 /// <param name="parameters">The parameters.</param>
245 /// <returns></returns>
246 public virtual string BuildUrl(UrlInfo current
, IDictionary parameters
)
248 return CreateUrlPartsBuilder(current
, parameters
).BuildPath();
254 /// <param name="current">The current.</param>
255 /// <param name="routeName">Name of the route.</param>
256 /// <returns></returns>
257 public string BuildRouteUrl(UrlInfo current
, string routeName
)
259 return InternalBuildRouteUrl(current
.Domain
, current
.AppVirtualDir
, routeName
, null, false).BuildPath();
265 /// <param name="current">The current.</param>
266 /// <param name="routeName">Name of the route.</param>
267 /// <param name="parameters">The parameters.</param>
268 /// <returns></returns>
269 public string BuildRouteUrl(UrlInfo current
, string routeName
, IDictionary parameters
)
271 return InternalBuildRouteUrl(current
.Domain
, current
.AppVirtualDir
, routeName
, parameters
, false).BuildPath();
277 /// <param name="current">The current.</param>
278 /// <param name="routeName">Name of the route.</param>
279 /// <param name="parameters">The parameters.</param>
280 /// <returns></returns>
281 public string BuildRouteUrl(UrlInfo current
, string routeName
, object parameters
)
283 return InternalBuildRouteUrl(current
.Domain
, current
.AppVirtualDir
, routeName
, parameters
, false).BuildPath();
287 /// Builds an URL using the controller name and action name.
289 /// <param name="current">The current Url information.</param>
290 /// <param name="controller">The controller.</param>
291 /// <param name="action">The action.</param>
292 /// <returns></returns>
293 public virtual string BuildUrl(UrlInfo current
, string controller
, string action
)
295 Hashtable parameters
= new Hashtable();
296 parameters
["controller"] = controller
;
297 parameters
["action"] = action
;
298 return BuildUrl(current
, parameters
);
302 /// Builds an URL using the controller name, action name, and a querystring dictionary.
304 /// <param name="current">The current Url information.</param>
305 /// <param name="controller">The controller.</param>
306 /// <param name="action">The action.</param>
307 /// <param name="queryStringParams">The query string params.</param>
308 /// <returns></returns>
309 public virtual string BuildUrl(UrlInfo current
, string controller
, string action
, IDictionary queryStringParams
)
311 Hashtable parameters
= new Hashtable();
312 parameters
["controller"] = controller
;
313 parameters
["action"] = action
;
314 parameters
["querystring"] = queryStringParams
;
315 return BuildUrl(current
, parameters
);
319 /// Builds an URL using the controller name, action name, and a querystring name value collection.
321 /// <param name="current">The current Url information.</param>
322 /// <param name="controller">The controller.</param>
323 /// <param name="action">The action.</param>
324 /// <param name="queryStringParams">The query string params.</param>
325 /// <returns></returns>
326 public virtual string BuildUrl(UrlInfo current
, string controller
, string action
, NameValueCollection queryStringParams
)
328 Hashtable parameters
= new Hashtable();
329 parameters
["controller"] = controller
;
330 parameters
["action"] = action
;
331 parameters
["querystring"] = queryStringParams
;
332 return BuildUrl(current
, parameters
);
336 /// Builds an URL using the area name, controller name and action name.
338 /// <param name="current">The current Url information.</param>
339 /// <param name="area">The area.</param>
340 /// <param name="controller">The controller.</param>
341 /// <param name="action">The action.</param>
342 /// <returns></returns>
343 public virtual string BuildUrl(UrlInfo current
, string area
, string controller
, string action
)
345 Hashtable parameters
= new Hashtable();
346 parameters
["area"] = area
;
347 parameters
["controller"] = controller
;
348 parameters
["action"] = action
;
349 return BuildUrl(current
, parameters
);
353 /// Builds an URL using the area name, controller name, action name, and a querystring dictionary.
355 /// <param name="current">The current Url information.</param>
356 /// <param name="area">The area.</param>
357 /// <param name="controller">The controller.</param>
358 /// <param name="action">The action.</param>
359 /// <param name="queryStringParams">The query string params.</param>
360 /// <returns></returns>
361 public virtual string BuildUrl(UrlInfo current
, string area
, string controller
, string action
, IDictionary queryStringParams
)
363 Hashtable parameters
= new Hashtable();
364 parameters
["area"] = area
;
365 parameters
["controller"] = controller
;
366 parameters
["action"] = action
;
367 parameters
["querystring"] = queryStringParams
;
368 return BuildUrl(current
, parameters
);
372 /// Builds an URL using the area name, controller name, action name, and a querystring name value collection.
374 /// <param name="current">The current Url information.</param>
375 /// <param name="area">The area.</param>
376 /// <param name="controller">The controller.</param>
377 /// <param name="action">The action.</param>
378 /// <param name="queryStringParams">The query string params.</param>
379 /// <returns></returns>
380 public virtual string BuildUrl(UrlInfo current
, string area
, string controller
, string action
,
381 NameValueCollection queryStringParams
)
383 Hashtable parameters
= new Hashtable();
384 parameters
["area"] = area
;
385 parameters
["controller"] = controller
;
386 parameters
["action"] = action
;
387 parameters
["querystring"] = queryStringParams
;
388 return BuildUrl(current
, parameters
);
394 /// Internals the build URL.
396 /// <param name="area">The area.</param>
397 /// <param name="controller">The controller.</param>
398 /// <param name="action">The action.</param>
399 /// <param name="protocol">The protocol.</param>
400 /// <param name="port">The port.</param>
401 /// <param name="domain">The domain.</param>
402 /// <param name="subdomain">The subdomain.</param>
403 /// <param name="appVirtualDir">The app virtual dir.</param>
404 /// <param name="extension">The extension.</param>
405 /// <param name="absolutePath">if set to <c>true</c> [absolute path].</param>
406 /// <param name="applySubdomain">if set to <c>true</c> [apply subdomain].</param>
407 /// <param name="suffix">The suffix.</param>
408 /// <returns></returns>
409 protected virtual string InternalBuildUrl(string area
, string controller
, string action
, string protocol
,
410 string port
, string domain
, string subdomain
, string appVirtualDir
,
412 bool absolutePath
, bool applySubdomain
, string suffix
)
415 InternalBuildUrl(area
, controller
, action
, protocol
, port
, domain
, subdomain
, appVirtualDir
, extension
, absolutePath
,
416 applySubdomain
, suffix
, null, null).BuildPath();
420 /// Internals the build URL.
422 /// <param name="area">The area.</param>
423 /// <param name="controller">The controller.</param>
424 /// <param name="action">The action.</param>
425 /// <param name="protocol">The protocol.</param>
426 /// <param name="port">The port.</param>
427 /// <param name="domain">The domain.</param>
428 /// <param name="subdomain">The subdomain.</param>
429 /// <param name="appVirtualDir">The app virtual dir.</param>
430 /// <param name="extension">The extension.</param>
431 /// <param name="absolutePath">if set to <c>true</c> [absolute path].</param>
432 /// <param name="applySubdomain">if set to <c>true</c> [apply subdomain].</param>
433 /// <param name="suffix">The suffix.</param>
434 /// <param name="basePath">The base path.</param>
435 /// <param name="pathInfo">Path info</param>
436 /// <returns></returns>
437 protected virtual UrlPartsBuilder
InternalBuildUrl(string area
, string controller
, string action
, string protocol
,
438 string port
, string domain
, string subdomain
, string appVirtualDir
,
440 bool absolutePath
, bool applySubdomain
, string suffix
,
444 if (area
== null) throw new ArgumentNullException("area");
445 if (controller
== null) throw new ArgumentNullException("controller");
446 if (action
== null) throw new ArgumentNullException("action");
447 if (appVirtualDir
== null) throw new ArgumentNullException("appVirtualDir");
451 if (basePath
!= null)
453 path
= InternalBuildUrlUsingBasePath(action
, area
, basePath
, controller
);
457 path
= InternalBuildUsingAppVirtualDir(absolutePath
, action
, applySubdomain
, appVirtualDir
, area
, controller
, domain
, port
, protocol
, subdomain
);
462 path
+= "." + extension
;
465 UrlPartsBuilder urlBuilder
= new UrlPartsBuilder(path
);
467 urlBuilder
.PathInfoDict
.Parse(pathInfo
);
469 if (!string.IsNullOrEmpty(suffix
))
471 urlBuilder
.SetQueryString(suffix
);
478 /// Internals the build route URL.
480 /// <param name="hostname">The hostname.</param>
481 /// <param name="virtualDir">The virtual dir.</param>
482 /// <param name="name">The name.</param>
483 /// <param name="routeParams">The route params.</param>
484 /// <param name="encode">if set to <c>true</c> [encode].</param>
485 /// <returns></returns>
486 protected virtual UrlPartsBuilder
InternalBuildRouteUrl(string hostname
, string virtualDir
,
487 string name
, object routeParams
, bool encode
)
489 IDictionary parameters
;
491 if (routeParams
!= null)
493 if (typeof(IDictionary
).IsAssignableFrom(routeParams
.GetType()))
495 parameters
= (IDictionary
) routeParams
;
499 parameters
= new ReflectionBasedDictionaryAdapter(routeParams
);
504 parameters
= new Hashtable();
507 String url
= routingEng
.CreateUrl(name
, hostname
, virtualDir
, parameters
);
509 // TODO: should encode?
511 return new UrlPartsBuilder(url
);
515 /// Internals the build using app virtual dir.
517 /// <param name="absolutePath">if set to <c>true</c> [absolute path].</param>
518 /// <param name="action">The action.</param>
519 /// <param name="applySubdomain">if set to <c>true</c> [apply subdomain].</param>
520 /// <param name="appVirtualDir">The app virtual dir.</param>
521 /// <param name="area">The area.</param>
522 /// <param name="controller">The controller.</param>
523 /// <param name="domain">The domain.</param>
524 /// <param name="port">The port.</param>
525 /// <param name="protocol">The protocol.</param>
526 /// <param name="subdomain">The subdomain.</param>
527 /// <returns></returns>
528 protected static string InternalBuildUsingAppVirtualDir(bool absolutePath
, string action
,
529 bool applySubdomain
, string appVirtualDir
,
530 string area
, string controller
,
531 string domain
, string port
, string protocol
, string subdomain
)
535 if (appVirtualDir
.Length
> 1 && !(appVirtualDir
[0] == '/'))
537 appVirtualDir
= "/" + appVirtualDir
;
540 if (area
!= String
.Empty
)
542 path
= appVirtualDir
+ "/" + area
+ "/" + controller
+ "/" + action
;
546 path
= appVirtualDir
+ "/" + controller
+ "/" + action
;
555 url
= protocol
+ "://" + subdomain
+ domain
;
559 url
= protocol
+ "://" + domain
;
574 /// Internals the build URL using base path.
576 /// <param name="action">The action.</param>
577 /// <param name="area">The area.</param>
578 /// <param name="basePath">The base path.</param>
579 /// <param name="controller">The controller.</param>
580 /// <returns></returns>
581 protected static string InternalBuildUrlUsingBasePath(string action
, string area
, string basePath
, string controller
)
585 basePath
= basePath
[basePath
.Length
- 1] == '/' ? basePath
.Substring(0, basePath
.Length
- 1) : basePath
;
587 if (basePath
.EndsWith(area
))
589 path
= basePath
+ "/" + controller
+ "/" + action
;
593 path
= basePath
+ "/" + area
+ "/" + controller
+ "/" + action
;