- Fixed MR-84
[castle.git] / MonoRail / Castle.MonoRail.Framework / Services / DefaultUrlBuilder.cs
blob91e770dfbb764bf86bca97ec2fbd9d77b22bb587
1 // Copyright 2004-2007 Castle Project - http://www.castleproject.org/
2 //
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
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
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
17 using System;
18 using System.Collections;
19 using System.Collections.Specialized;
20 using Castle.Core;
21 using Castle.MonoRail.Framework.Internal;
22 using Framework;
24 /// <summary>
25 /// Default implementation of <see cref="IUrlBuilder"/>
26 /// </summary>
27 /// <remarks>
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
30 ///
31 /// <para>
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)"/>
34 /// </para>
35 ///
36 /// </remarks>
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;
44 /// <summary>
45 /// Initializes a new instance of the <see cref="DefaultUrlBuilder"/> class.
46 /// </summary>
47 public DefaultUrlBuilder()
51 /// <summary>
52 /// Initializes a new instance of the <see cref="DefaultUrlBuilder"/> class.
53 /// </summary>
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;
62 #region Properties
64 /// <summary>
65 /// Gets or sets a value indicating whether the builder should output an extension.
66 /// </summary>
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; }
74 /// <summary>
75 /// Gets or sets the server utility instance.
76 /// </summary>
77 /// <value>The server util.</value>
78 public IServerUtility ServerUtil
80 get { return serverUtil; }
81 set { serverUtil = value; }
84 /// <summary>
85 /// Gets or sets the routing engine.
86 /// </summary>
87 /// <value>The routing engine.</value>
88 public IRoutingEngine RoutingEngine
90 get { return routingEng; }
91 set { routingEng = value; }
94 #endregion
96 #region IServiceEnabledComponent
98 /// <summary>
99 /// Services the specified provider.
100 /// </summary>
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));
108 #endregion
110 #region IUrlBuilder
112 /// <summary>
113 /// Builds the URL using the current url as contextual information and a parameter dictionary.
114 /// <para>
115 /// Common parameters includes area, controller and action.
116 /// </para>
117 /// </summary>
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);
133 else
135 bool applySubdomain = false;
136 bool createAbsolutePath = CommonUtils.ObtainEntryAndRemove(parameters, "absolute", "false") == "true";
138 string area;
140 if (parameters.Contains("area"))
142 area = CommonUtils.ObtainEntryAndRemove(parameters, "area");
144 if (area == null) area = string.Empty;
146 else
148 area = current.Area;
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,
195 basePath, pathInfo);
199 /// <summary>
200 /// Builds the URL using the current url as contextual information and a parameter dictionary.
201 ///
202 /// </summary>
203 ///
204 /// <remarks>
205 /// <para>
206 /// Common parameters includes <c>area</c>, <c>controller</c> and <c>action</c>, which outputs
207 /// <c>/area/controller/name.extension</c>
208 /// </para>
209 ///
210 /// <para>
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:
214 /// </para>
215 ///
216 /// <example>
217 /// <code>
218 /// UrlInfo current = ... // Assume that the current is area Admin, controller Products and action List
219 ///
220 /// BuildUrl(current, {action: 'view'})
221 /// // returns /Admin/Products/view.castle
222 ///
223 /// BuildUrl(current, {controller: 'Home', action: 'index'})
224 /// // returns /Admin/Home/index.castle
225 ///
226 /// BuildUrl(current, {area:'', controller: 'Home', action: 'index'})
227 /// // returns /Home/index.castle
228 /// </code>
229 /// </example>
230 ///
231 /// <para>
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>
234 /// </para>
235 ///
236 /// <para>
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>
239 /// </para>
241 /// <para>
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>
246 /// </para>
247 ///
248 /// <para>
249 /// The <c>encode</c> parameter forces the builder to encode the querystring
250 /// <c>/controller/name.extension?id=1&amp;name=John</c> which is required to output full xhtml compliant content.
251 /// </para>
252 ///
253 /// </remarks>
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();
262 /// <summary>
263 /// Pendent.
264 /// </summary>
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();
273 /// <summary>
274 /// Pendent.
275 /// </summary>
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();
285 /// <summary>
286 /// Pendent.
287 /// </summary>
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();
297 /// <summary>
298 /// Builds an URL using the controller name and action name.
299 /// </summary>
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);
312 /// <summary>
313 /// Builds an URL using the controller name, action name, and a querystring dictionary.
314 /// </summary>
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);
329 /// <summary>
330 /// Builds an URL using the controller name, action name, and a querystring name value collection.
331 /// </summary>
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);
346 /// <summary>
347 /// Builds an URL using the area name, controller name and action name.
348 /// </summary>
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);
363 /// <summary>
364 /// Builds an URL using the area name, controller name, action name, and a querystring dictionary.
365 /// </summary>
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);
382 /// <summary>
383 /// Builds an URL using the area name, controller name, action name, and a querystring name value collection.
384 /// </summary>
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);
402 #endregion
404 /// <summary>
405 /// Internals the build URL.
406 /// </summary>
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,
422 string extension,
423 bool absolutePath, bool applySubdomain, string suffix)
425 return
426 InternalBuildUrl(area, controller, action, protocol, port, domain, subdomain, appVirtualDir, extension, absolutePath,
427 applySubdomain, suffix, null, null).BuildPath();
430 /// <summary>
431 /// Internals the build URL.
432 /// </summary>
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,
450 string extension,
451 bool absolutePath, bool applySubdomain, string suffix,
452 string basePath,
453 string pathInfo)
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");
460 string path;
462 if (basePath != null)
464 path = InternalBuildUrlUsingBasePath(action, area, basePath, controller);
466 else
468 path = InternalBuildUsingAppVirtualDir(absolutePath, action, applySubdomain, appVirtualDir, area, controller, domain, port, protocol, subdomain);
471 if (useExtensions)
473 path += "." + extension;
476 UrlPartsBuilder urlBuilder = new UrlPartsBuilder(path);
478 urlBuilder.PathInfoDict.Parse(pathInfo);
480 if (!string.IsNullOrEmpty(suffix))
482 urlBuilder.SetQueryString(suffix);
485 return urlBuilder;
488 /// <summary>
489 /// Internals the build route URL.
490 /// </summary>
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;
508 else
510 parameters = new ReflectionBasedDictionaryAdapter(routeParams);
513 else
515 parameters = new Hashtable();
518 String url = routingEng.CreateUrl(name, hostname, virtualDir, parameters);
520 // TODO: should encode?
522 return new UrlPartsBuilder(url);
525 /// <summary>
526 /// Internals the build using app virtual dir.
527 /// </summary>
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)
544 string path;
546 if (appVirtualDir.Length > 1 && !(appVirtualDir[0] == '/'))
548 appVirtualDir = "/" + appVirtualDir;
551 if (area != String.Empty)
553 path = appVirtualDir + "/" + area + "/" + controller + "/" + action;
555 else
557 path = appVirtualDir + "/" + controller + "/" + action;
560 if (absolutePath)
562 string url;
564 if (applySubdomain)
566 url = protocol + "://" + subdomain + domain;
568 else
570 url = protocol + "://" + domain;
573 if (port != "80")
575 url += ":" + port;
578 path = url + path;
581 return path;
584 /// <summary>
585 /// Internals the build URL using base path.
586 /// </summary>
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)
594 string path;
596 basePath = basePath[basePath.Length - 1] == '/' ? basePath.Substring(0, basePath.Length - 1) : basePath;
598 if (basePath.EndsWith(area))
600 path = basePath + "/" + controller + "/" + action;
602 else
604 path = basePath + "/" + area + "/" + controller + "/" + action;
607 return path;