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()
52 /// Initializes a new instance of the <see cref="DefaultUrlBuilder"/> class.
54 /// <param name="serverUtil">The server util.</param>
55 /// <param name="routingEng">The routing eng.</param>
56 public DefaultUrlBuilder(IServerUtility serverUtil
, IRoutingEngine routingEng
)
58 this.serverUtil
= serverUtil
;
59 this.routingEng
= routingEng
;
65 /// Gets or sets a value indicating whether the builder should output an extension.
67 /// <value><c>true</c> if should use extensions; otherwise, <c>false</c>.</value>
68 public bool UseExtensions
70 get { return useExtensions; }
71 set { useExtensions = value; }
75 /// Gets or sets the server utility instance.
77 /// <value>The server util.</value>
78 public IServerUtility ServerUtil
80 get { return serverUtil; }
81 set { serverUtil = value; }
85 /// Gets or sets the routing engine.
87 /// <value>The routing engine.</value>
88 public IRoutingEngine RoutingEngine
90 get { return routingEng; }
91 set { routingEng = value; }
96 #region IServiceEnabledComponent
99 /// Services the specified provider.
101 /// <param name="provider">The provider.</param>
102 public void Service(IServiceProvider provider
)
104 serverUtil
= (IServerUtility
) provider
.GetService(typeof(IServerUtility
));
105 routingEng
= (IRoutingEngine
) provider
.GetService(typeof(IRoutingEngine
));
113 /// Builds the URL using the current url as contextual information and a parameter dictionary.
115 /// Common parameters includes area, controller and action.
118 /// <param name="current">The current Url information.</param>
119 /// <param name="parameters">The parameters.</param>
120 /// <returns></returns>
121 public UrlPartsBuilder
CreateUrlPartsBuilder(UrlInfo current
, IDictionary parameters
)
123 string routeName
= CommonUtils
.ObtainEntryAndRemove(parameters
, "named");
124 bool encode
= CommonUtils
.ObtainEntryAndRemove(parameters
, "encode", "false") == "true";
126 if (routeName
!= null)
128 object routeParams
= parameters
["params"];
129 parameters
.Remove("params");
131 return InternalBuildRouteUrl(current
.Domain
, current
.AppVirtualDir
, routeName
, routeParams
, encode
);
135 bool applySubdomain
= false;
136 bool createAbsolutePath
= CommonUtils
.ObtainEntryAndRemove(parameters
, "absolute", "false") == "true";
140 if (parameters
.Contains("area"))
142 area
= CommonUtils
.ObtainEntryAndRemove(parameters
, "area");
144 if (area
== null) area
= string.Empty
;
151 string controller
= CommonUtils
.ObtainEntryAndRemove(parameters
, "controller", current
.Controller
);
152 string action
= CommonUtils
.ObtainEntryAndRemove(parameters
, "action", current
.Action
);
154 string domain
= CommonUtils
.ObtainEntryAndRemove(parameters
, "domain", current
.Domain
);
155 string subdomain
= CommonUtils
.ObtainEntryAndRemove(parameters
, "subdomain", current
.Subdomain
);
156 string protocol
= CommonUtils
.ObtainEntryAndRemove(parameters
, "protocol", current
.Protocol
);
157 string port
= CommonUtils
.ObtainEntryAndRemove(parameters
, "port", current
.Port
.ToString());
158 string pathInfo
= CommonUtils
.ObtainEntryAndRemove(parameters
, "pathinfo");
159 string suffix
= null;
161 object queryString
= CommonUtils
.ObtainObjectEntryAndRemove(parameters
, "querystring");
163 string basePath
= null;
165 if (parameters
.Contains("basepath"))
167 basePath
= CommonUtils
.ObtainEntryAndRemove(parameters
, "basepath");
170 if (queryString
!= null)
172 if (queryString
is IDictionary
)
174 IDictionary qsDictionary
= (IDictionary
) queryString
;
176 suffix
= CommonUtils
.BuildQueryString(serverUtil
, qsDictionary
, encode
);
178 else if (queryString
is NameValueCollection
)
180 suffix
= CommonUtils
.BuildQueryString(serverUtil
, (NameValueCollection
) queryString
, encode
);
182 else if (queryString
is string)
184 suffix
= queryString
.ToString();
188 if (subdomain
.ToLower() != current
.Subdomain
.ToLower())
190 applySubdomain
= true;
193 return InternalBuildUrl(area
, controller
, action
, protocol
, port
, domain
, subdomain
,
194 current
.AppVirtualDir
, current
.Extension
, createAbsolutePath
, applySubdomain
, suffix
,
200 /// Builds the URL using the current url as contextual information and a parameter dictionary.
206 /// Common parameters includes <c>area</c>, <c>controller</c> and <c>action</c>, which outputs
207 /// <c>/area/controller/name.extension</c>
211 /// Please note that if you dont specify an area or controller name, they will be inferred from the
212 /// context. If you want to use an empty area, you must specify <c>area=''</c>.
213 /// This is commonly a source of confusion, so understand the following cases:
218 /// UrlInfo current = ... // Assume that the current is area Admin, controller Products and action List
220 /// BuildUrl(current, {action: 'view'})
221 /// // returns /Admin/Products/view.castle
223 /// BuildUrl(current, {controller: 'Home', action: 'index'})
224 /// // returns /Admin/Home/index.castle
226 /// BuildUrl(current, {area:'', controller: 'Home', action: 'index'})
227 /// // returns /Home/index.castle
232 /// The <c>querystring</c> parameter can be a string or a dictionary. It appends a query string to the url:
233 /// <c>/area/controller/name.extension?id=1</c>
237 /// The <c>absolute</c> parameter forces the builder to output a full url like
238 /// <c>http://hostname/virtualdir/area/controller/name.extension</c>
242 /// The <c>pathinfo</c> parameter can be used to add information between the resource identifier and the
243 /// query string (optional)
244 /// <c>http://hostname/virtualdir/area/controller/name.extension/path/info/here</c> or
245 /// <c>http://hostname/virtualdir/area/controller/name.extension/path/info/here?key=value</c>
249 /// The <c>encode</c> parameter forces the builder to encode the querystring
250 /// <c>/controller/name.extension?id=1&name=John</c> which is required to output full xhtml compliant content.
254 /// <param name="current">The current Url information.</param>
255 /// <param name="parameters">The parameters.</param>
256 /// <returns></returns>
257 public virtual string BuildUrl(UrlInfo current
, IDictionary parameters
)
259 return CreateUrlPartsBuilder(current
, parameters
).BuildPath();
265 /// <param name="current">The current.</param>
266 /// <param name="routeName">Name of the route.</param>
267 /// <returns></returns>
268 public string BuildRouteUrl(UrlInfo current
, string routeName
)
270 return InternalBuildRouteUrl(current
.Domain
, current
.AppVirtualDir
, routeName
, null, false).BuildPath();
276 /// <param name="current">The current.</param>
277 /// <param name="routeName">Name of the route.</param>
278 /// <param name="parameters">The parameters.</param>
279 /// <returns></returns>
280 public string BuildRouteUrl(UrlInfo current
, string routeName
, IDictionary parameters
)
282 return InternalBuildRouteUrl(current
.Domain
, current
.AppVirtualDir
, routeName
, parameters
, false).BuildPath();
288 /// <param name="current">The current.</param>
289 /// <param name="routeName">Name of the route.</param>
290 /// <param name="parameters">The parameters.</param>
291 /// <returns></returns>
292 public string BuildRouteUrl(UrlInfo current
, string routeName
, object parameters
)
294 return InternalBuildRouteUrl(current
.Domain
, current
.AppVirtualDir
, routeName
, parameters
, false).BuildPath();
298 /// Builds an URL using the controller name and action name.
300 /// <param name="current">The current Url information.</param>
301 /// <param name="controller">The controller.</param>
302 /// <param name="action">The action.</param>
303 /// <returns></returns>
304 public virtual string BuildUrl(UrlInfo current
, string controller
, string action
)
306 Hashtable parameters
= new Hashtable();
307 parameters
["controller"] = controller
;
308 parameters
["action"] = action
;
309 return BuildUrl(current
, parameters
);
313 /// Builds an URL using the controller name, action name, and a querystring dictionary.
315 /// <param name="current">The current Url information.</param>
316 /// <param name="controller">The controller.</param>
317 /// <param name="action">The action.</param>
318 /// <param name="queryStringParams">The query string params.</param>
319 /// <returns></returns>
320 public virtual string BuildUrl(UrlInfo current
, string controller
, string action
, IDictionary queryStringParams
)
322 Hashtable parameters
= new Hashtable();
323 parameters
["controller"] = controller
;
324 parameters
["action"] = action
;
325 parameters
["querystring"] = queryStringParams
;
326 return BuildUrl(current
, parameters
);
330 /// Builds an URL using the controller name, action name, and a querystring name value collection.
332 /// <param name="current">The current Url information.</param>
333 /// <param name="controller">The controller.</param>
334 /// <param name="action">The action.</param>
335 /// <param name="queryStringParams">The query string params.</param>
336 /// <returns></returns>
337 public virtual string BuildUrl(UrlInfo current
, string controller
, string action
, NameValueCollection queryStringParams
)
339 Hashtable parameters
= new Hashtable();
340 parameters
["controller"] = controller
;
341 parameters
["action"] = action
;
342 parameters
["querystring"] = queryStringParams
;
343 return BuildUrl(current
, parameters
);
347 /// Builds an URL using the area name, controller name and action name.
349 /// <param name="current">The current Url information.</param>
350 /// <param name="area">The area.</param>
351 /// <param name="controller">The controller.</param>
352 /// <param name="action">The action.</param>
353 /// <returns></returns>
354 public virtual string BuildUrl(UrlInfo current
, string area
, string controller
, string action
)
356 Hashtable parameters
= new Hashtable();
357 parameters
["area"] = area
;
358 parameters
["controller"] = controller
;
359 parameters
["action"] = action
;
360 return BuildUrl(current
, parameters
);
364 /// Builds an URL using the area name, controller name, action name, and a querystring dictionary.
366 /// <param name="current">The current Url information.</param>
367 /// <param name="area">The area.</param>
368 /// <param name="controller">The controller.</param>
369 /// <param name="action">The action.</param>
370 /// <param name="queryStringParams">The query string params.</param>
371 /// <returns></returns>
372 public virtual string BuildUrl(UrlInfo current
, string area
, string controller
, string action
, IDictionary queryStringParams
)
374 Hashtable parameters
= new Hashtable();
375 parameters
["area"] = area
;
376 parameters
["controller"] = controller
;
377 parameters
["action"] = action
;
378 parameters
["querystring"] = queryStringParams
;
379 return BuildUrl(current
, parameters
);
383 /// Builds an URL using the area name, controller name, action name, and a querystring name value collection.
385 /// <param name="current">The current Url information.</param>
386 /// <param name="area">The area.</param>
387 /// <param name="controller">The controller.</param>
388 /// <param name="action">The action.</param>
389 /// <param name="queryStringParams">The query string params.</param>
390 /// <returns></returns>
391 public virtual string BuildUrl(UrlInfo current
, string area
, string controller
, string action
,
392 NameValueCollection queryStringParams
)
394 Hashtable parameters
= new Hashtable();
395 parameters
["area"] = area
;
396 parameters
["controller"] = controller
;
397 parameters
["action"] = action
;
398 parameters
["querystring"] = queryStringParams
;
399 return BuildUrl(current
, parameters
);
405 /// Internals the build URL.
407 /// <param name="area">The area.</param>
408 /// <param name="controller">The controller.</param>
409 /// <param name="action">The action.</param>
410 /// <param name="protocol">The protocol.</param>
411 /// <param name="port">The port.</param>
412 /// <param name="domain">The domain.</param>
413 /// <param name="subdomain">The subdomain.</param>
414 /// <param name="appVirtualDir">The app virtual dir.</param>
415 /// <param name="extension">The extension.</param>
416 /// <param name="absolutePath">if set to <c>true</c> [absolute path].</param>
417 /// <param name="applySubdomain">if set to <c>true</c> [apply subdomain].</param>
418 /// <param name="suffix">The suffix.</param>
419 /// <returns></returns>
420 protected virtual string InternalBuildUrl(string area
, string controller
, string action
, string protocol
,
421 string port
, string domain
, string subdomain
, string appVirtualDir
,
423 bool absolutePath
, bool applySubdomain
, string suffix
)
426 InternalBuildUrl(area
, controller
, action
, protocol
, port
, domain
, subdomain
, appVirtualDir
, extension
, absolutePath
,
427 applySubdomain
, suffix
, null, null).BuildPath();
431 /// Internals the build URL.
433 /// <param name="area">The area.</param>
434 /// <param name="controller">The controller.</param>
435 /// <param name="action">The action.</param>
436 /// <param name="protocol">The protocol.</param>
437 /// <param name="port">The port.</param>
438 /// <param name="domain">The domain.</param>
439 /// <param name="subdomain">The subdomain.</param>
440 /// <param name="appVirtualDir">The app virtual dir.</param>
441 /// <param name="extension">The extension.</param>
442 /// <param name="absolutePath">if set to <c>true</c> [absolute path].</param>
443 /// <param name="applySubdomain">if set to <c>true</c> [apply subdomain].</param>
444 /// <param name="suffix">The suffix.</param>
445 /// <param name="basePath">The base path.</param>
446 /// <param name="pathInfo">Path info</param>
447 /// <returns></returns>
448 protected virtual UrlPartsBuilder
InternalBuildUrl(string area
, string controller
, string action
, string protocol
,
449 string port
, string domain
, string subdomain
, string appVirtualDir
,
451 bool absolutePath
, bool applySubdomain
, string suffix
,
455 if (area
== null) throw new ArgumentNullException("area");
456 if (controller
== null) throw new ArgumentNullException("controller");
457 if (action
== null) throw new ArgumentNullException("action");
458 if (appVirtualDir
== null) throw new ArgumentNullException("appVirtualDir");
462 if (basePath
!= null)
464 path
= InternalBuildUrlUsingBasePath(action
, area
, basePath
, controller
);
468 path
= InternalBuildUsingAppVirtualDir(absolutePath
, action
, applySubdomain
, appVirtualDir
, area
, controller
, domain
, port
, protocol
, subdomain
);
473 path
+= "." + extension
;
476 UrlPartsBuilder urlBuilder
= new UrlPartsBuilder(path
);
478 urlBuilder
.PathInfoDict
.Parse(pathInfo
);
480 if (!string.IsNullOrEmpty(suffix
))
482 urlBuilder
.SetQueryString(suffix
);
489 /// Internals the build route URL.
491 /// <param name="hostname">The hostname.</param>
492 /// <param name="virtualDir">The virtual dir.</param>
493 /// <param name="name">The name.</param>
494 /// <param name="routeParams">The route params.</param>
495 /// <param name="encode">if set to <c>true</c> [encode].</param>
496 /// <returns></returns>
497 protected virtual UrlPartsBuilder
InternalBuildRouteUrl(string hostname
, string virtualDir
,
498 string name
, object routeParams
, bool encode
)
500 IDictionary parameters
;
502 if (routeParams
!= null)
504 if (typeof(IDictionary
).IsAssignableFrom(routeParams
.GetType()))
506 parameters
= (IDictionary
) routeParams
;
510 parameters
= new ReflectionBasedDictionaryAdapter(routeParams
);
515 parameters
= new Hashtable();
518 String url
= routingEng
.CreateUrl(name
, hostname
, virtualDir
, parameters
);
520 // TODO: should encode?
522 return new UrlPartsBuilder(url
);
526 /// Internals the build using app virtual dir.
528 /// <param name="absolutePath">if set to <c>true</c> [absolute path].</param>
529 /// <param name="action">The action.</param>
530 /// <param name="applySubdomain">if set to <c>true</c> [apply subdomain].</param>
531 /// <param name="appVirtualDir">The app virtual dir.</param>
532 /// <param name="area">The area.</param>
533 /// <param name="controller">The controller.</param>
534 /// <param name="domain">The domain.</param>
535 /// <param name="port">The port.</param>
536 /// <param name="protocol">The protocol.</param>
537 /// <param name="subdomain">The subdomain.</param>
538 /// <returns></returns>
539 protected static string InternalBuildUsingAppVirtualDir(bool absolutePath
, string action
,
540 bool applySubdomain
, string appVirtualDir
,
541 string area
, string controller
,
542 string domain
, string port
, string protocol
, string subdomain
)
546 if (appVirtualDir
.Length
> 1 && !(appVirtualDir
[0] == '/'))
548 appVirtualDir
= "/" + appVirtualDir
;
551 if (area
!= String
.Empty
)
553 path
= appVirtualDir
+ "/" + area
+ "/" + controller
+ "/" + action
;
557 path
= appVirtualDir
+ "/" + controller
+ "/" + action
;
566 url
= protocol
+ "://" + subdomain
+ domain
;
570 url
= protocol
+ "://" + domain
;
585 /// Internals the build URL using base path.
587 /// <param name="action">The action.</param>
588 /// <param name="area">The area.</param>
589 /// <param name="basePath">The base path.</param>
590 /// <param name="controller">The controller.</param>
591 /// <returns></returns>
592 protected static string InternalBuildUrlUsingBasePath(string action
, string area
, string basePath
, string controller
)
596 basePath
= basePath
[basePath
.Length
- 1] == '/' ? basePath
.Substring(0, basePath
.Length
- 1) : basePath
;
598 if (basePath
.EndsWith(area
))
600 path
= basePath
+ "/" + controller
+ "/" + action
;
604 path
= basePath
+ "/" + area
+ "/" + controller
+ "/" + action
;