by Jörg Jooss
22. August 2007 10:23
While preparing my home machine for Visual Studio 2008 Beta 2, I found that uninstalling the .NET Framework 3.5 Beta 1 seems to break the ASP.NET 2.0 AJAX Extensions in Visual Studio 2005. After uninstalling, every time I tried to drag a AJAX control from the Toolbox to the design surface, Visual Studio 2005 complained it coudn't find the System.Web.Extensions.dll. To fix that, delete the existing AJAX Extensions tab from the Toolbox and recreate it using Add Tab and Choose Items…
(I also did a repair installation of the ASP.NET 2.0 AJAX Extensions as well, but I don't think that was necessary.)
by Jörg Jooss
20. June 2007 09:05
Today I ran into Daniel Walzenbach, one of our German Developer Evangelists and one of the nicest guys you can possibly ever meet. Daniel owns MSDN Solve, and has begged me for some basic architecture content since last X-mas. Unfortunately, I had to put him off until September once more, which he suffered bravely. Before he turned to leave, he held up a small GPS receiver and asked "Do you know WoIstDaniel? Check it out."
I had no idea what he was referring to, so once I'd returned home I fired up a browser and had a look. I'm not sure how Daniel handles that kind of transparency in his private life, but his web application is way cool .
by Jörg Jooss
29. April 2007 11:55
My previous post on restricting the cacheability of static content focused on IIS5/6. Now lets check out IIS 7 on Windows Vista. Note that all of these settings work at folder level, so you should put your static content in one or more subfolders to make this work.
The bad news first: The IIS 7 Manager doesn't expose any of the following settings. You must use appcmd.exe and your favorite Windows command line interpreter to update your server and application configuration. By the way, if you find yourself struggling with appcmd.exe and its less than intuitive syntax, try Kanwaljeet Singla's excellent AppcmdUI.
First, you'll likely need to unlock the configuration section for static content handling. By default, most configuration sections are locked down at server level and cannot be overridden by application settings unless unlocked.
appcmd unlock config /section:staticContent
Now you're good to change the caching options for static content. IIS 7 offers the following options here:
Make static content non-cacheable by setting "Cache-Control: no-cache":
appcmd set config "Default Web Site/<Application>/<Folder>" /section:staticContent /clientCache.cacheControlMode:DisableCache
(Replace <Application>/<Folder> with the virtual path to your static content.)
Make static content expire after a fixed duration by setting "Cache-Control: max-age":
appcmd set config "Default Web Site/<Application>/<Folder>" /section:staticContent /clientCache.cacheControlMode:UseMaxAge
The default value for max-age is 86400 seconds (i.e. one day). To change this, use
appcmd set config "Default Web Site/<Application>/<Folder>" /section:staticContent /clientCache.cacheControlMaxAge:"<hh>:<mm>:<ss>"
(Replace <hh>:<mm>:<ss> with an appropriate time span.)
Make static content expire at a fixed point in time by setting the "Expires" header. That's the same appcmd command as before, but instead of UseMaxAge specify UseExpires, and set /clientCache.httpExpires to your desired expiration date.
Issue your own Cache-Control headers by setting /clientCache.cacheControlCustom:<Header>. This also works in combination with the previously mentioned options. This is useful for setting headers like "Cache-Control: no-store" or "Cache-Control: must-revalidate".
If you want to revert to the default behavior of not setting any caching headers, use
appcmd set config "Default Web Site/<Application>/<Folder>" /section:staticContent /clientCache.cacheControlMode:NoControl
Note that this does not affect any custom headers set by /clientCache.cacheControlCustom. To get rid of those, set /clientCache.cacheControlCustom:"".
The astute reader will notice a couple of issues here. First of all, there's no way to combine HTTP 1.1 and HTTP 1.0 headers like "Cache-Control: max-age" and "Expires". Secondly, the current "Expires" implementation is rather useless—configuring a fixed expiration time stamp requires a configuration update each time the expiration date has been reached. "Expires" needs to be computed based on an expiration interval, and that's exactly how it is done in IIS 5/6. I assume all these issues are only shortcomings of the current Vista implementation. Unfortunately, I haven't had time to delve into any of the Longhorn Server Betas and their respective IIS 7 implementations so far, but once I've got Beta 3 up and running I will post another update.
Windows Server 2008 Beta 3 update (29-07-2007): Same behavior as described above
by Jörg Jooss
20. February 2007 03:55
Judging from the recurrence of certain really frequently asked questions in microsoft.public.dotnet.framework.aspnet, one of the greater mysteries of ASP.NET web development remains caching, or HTTP caching to be more precisely. So let's review some of the seminal caching issues in ASP.NET web development.
Problem: You reference one or more static resources (e.g. an image or a style sheet) in your ASP.NET page, either through server controls or plain old HTML. But when you update such a resource, your browser continues to use its previous version. Only after an end-to-end reload (typically, this means hitting Ctrl-F5) does the browser use the updated resource.
Reason: IIS 5/6 don't add any HTTP Cache-Control headers to static resources by default, which makes caches apply their own heuristics to consider whether such a resource is to be considered "fresh" or "stale". That's deliberate lack of control coupled with implementation specific behavior, which in turn is just a fancy way of saying you're asking for trouble.
Solution: Put all static resources with similar caching requirements in a common folder in your web application, and set specific caching instructions for this folder in IIS Manager. Here you have three options:
Let IIS apply expiration rules
"Expire immediately" sets "Cache-Control: no-cache", and "Expires" to the same timestamp as "Date". Any resource served like this becomes immediately stale.
The other two options let you specify either a relative point in time or an absolute point in time for expiration. These options set both the "Cache-Control: max-age" and "Expires" headers.
Set your own HTTP Cache-Control headers.
Both of the above.
If you can not or do not want to use folders for this purpose, you can also apply the same options to individual files.
Each time you update a static resource, make sure that the resource's file modification time is updated to the current time (i.e. "touch" the resource). If the modification time doesn't change, IIS may fail to recognize that the resource has been updated (which is a topic for another blog entry).
by Jörg Jooss
6. November 2006 20:09
Finally, it's here.
And for good measure, make sure to grab Beta 2 of the AJAX ASP.NET Extensions.
by Jörg Jooss
25. September 2006 07:12
One of the many things that have kept me busy during my first months at Microsoft was producing content for MSDN Solve. It is a new section on the German MSDN portal that aims to provide solutions (read “how do I?” as opposed to “why do I?”) for developers trying to get their feet wet with .NET development. After much discussion and thinking, the team decided to produce a format called “CodeClips”. That's pretty much like a recorded 20 minute web cast that focuses on a showing how to handle a very specific .NET development task. And watching CodeClips pays off: You can get a Visual Studio 2005 Standard Edition for free! Get the details here. Note that German language skills are mandatory.
One of the goals of MSDN Solve is to provide interesting content for Java and PHP developers interested in ASP.NET. Thus, the team asked me to cover the Java part. Alas, providing content that appeals to every Java web developer is incredibly difficult. Unlike ASP.NET or PHP, there's really no such thing as Java web development. There is Java web development using JSF. There is Java web development using Struts. There is Java web development using Spring Web MVC. There is Java web development using Tapestry. The list goes on, and on, and on. The lowest common demoninator is not even JSP, it's Servlets. That's why I tried to show how to implement typical web application components like a compression filter or the Synchronizer Token pattern in ASP.NET, because these are canonical Java web development examples without any bias for a particular framework. I'm going to blog more about my CodeClips during the next couple of days.
If you have any questions or feedback reagrding my CodeClips, feel free to contact me via msdnsolve@joergjooss.de. The guy who runs the show is Daniel Walzenbach, and he blogs at http://blogs.msdn.com/walzenbach/.
by Jörg Jooss
25. September 2006 04:14
Hrast!
Sorry about impersonating a Ed Greenwood novel character, but dasBlog's instructions regarding ASP.NET 2.0 should have included a note saying: We still require full trust. Unfortunately, I can only run medium trust ASP.NET 2.0 code on this site, which makes perfect sense from a security and requirements perspective.
It's pretty sad not to be able to host live ASP.NET 2.0 samples on this site. Just today, I posted an answer on microsoft.public.dotnet.framework.aspnet that screams for a sample I have had sitting idle on my hard drive for more than half a year. Unfortunately, it requires ASP.NET 2.0…
Seems like I will finally sit down a have a good hard look at ThinkJot and Nblogr.
by Jörg Jooss
18. September 2005 02:59
The .NET 2.0 BCL offers various means to create URL encoded strings. One can use System.Uri or System.UriBuilder to create RFC 2396 compliant URLs, or System.Web.HttpUtiliy.UrlEncode(System.String, System.Text.Encoding) to URL encode individual strings. Unfortunately, neither System.Uri nor System.UriBuilder allow one to specify a character encoding for URL encoding. Both classes use UTF-8 exclusively. System.Uri is nonetheless capable of representing URIs whose query strings are based on encodings other than UTF-8, if the user creates a System.Uri from an already URL encoded string. System.Web.HttpUtiliy.UrlEncode(System.String, System.Text.Encoding) on the other hand cannot encode entire query strings, only individual names or values, so a developer must parse query string and encode each name or value individually.
Enter UrlEncoder. This class is able to encode or reencode System.Uri and System.String objects with arbitrary character encodings. Enjoy.
Note for Firefox users: The formatted code below may display scroll bars below some of the longer lines. To get rid of them, reduce the pixel size for fixed width fonts — sorry!
1: using System;
2: using System.Collections.Specialized;
3: using System.Text;
4: using System.Web;
5:
6: namespace JoergJooss.SharpAgent.Http
7: {
8: /// <summary>
9: /// Provides URL encoding capabilities for <see cref="System.Uri"/> and
10: /// <see cref="System.String"/> using arbitrary character encodings.
11: /// </summary>
12: /// <remarks>
13: /// <para>
14: /// The .NET BCL offers various means to create URL encoded strings
15: /// or URI objects with query strings. One can use <see cref="System.Uri"/> or
16: /// <see cref="System.UriBuilder"/> to create RFC 2396 compliant
17: /// URLs, or <see cref="System.Web.HttpUtiliy.UrlEncode(System.String, System.Text.Encoding)"/>
18: /// to URL encode individual strings. Unfortunately, neither
19: /// <see cref="System.Uri"/> nor <see cref="System.UriBuilder"/> allow one
20: /// to specify a character encoding for URL encoding. Both classes use
21: /// UTF-8 exclusively. <see cref="System.Uri"/> is nonetheless capable
22: /// of representing URIs whose query strings are based on encodings
23: /// other than UTF-8, if the user creates a <see cref="System.Uri"/> from
24: /// an already URL encoded string.
25: /// <see cref="System.Web.HttpUtiliy.UrlEncode(System.String, System.Text.Encoding)"/>
26: /// on the other hand cannot encode entire query strings, only individual
27: /// names or values. <see cref="JoergJooss.SharpAgent.Http.UrlEncoder"/>
28: /// is able to encode or reencode <see cref="System.Uri"/> and
29: /// <see cref="System.String"/> objects with arbitrary character encodings.
30: /// </para>
31: /// <para>
32: /// <see cref="JoergJooss.SharpAgent.Http.UrlEncoder"/> instances are
33: /// immutable. All non-inherited instance methods are thread safe.
34: /// </para>
35: /// </remarks>
36: public sealed class UrlEncoder
37: {
38: private readonly Encoding encoding;
39:
40: private static readonly UrlEncoder utf8;
41: private static readonly UrlEncoder latin1;
42: private static readonly UrlEncoder latin9;
43:
44: static UrlEncoder()
45: {
46: utf8 = new UrlEncoder(Encoding.UTF8);
47: latin1 = new UrlEncoder(Encoding.GetEncoding(28591));
48: latin9 = new UrlEncoder(Encoding.GetEncoding(28605));
49: }
50:
51: /// <summary>
52: /// Initializes a new instance of the
53: /// <see cref="JoergJooss.SharpAgent.Http.UrlEncoder"/> class
54: /// that uses the specified character encoding.
55: /// </summary>
56: /// <param name="encoding">An encoding.</param>
57: /// <exception cref="System.ArgumentNullException">
58: /// encoding is <c>null</c>.
59: /// </exception>
60: public UrlEncoder(Encoding encoding)
61: {
62: if (encoding == null)
63: {
64: throw new ArgumentNullException("encoding");
65: }
66:
67: this.encoding = encoding;
68: }
69:
70: /// <summary>
71: /// Gets the encoding used by this instance.
72: /// </summary>
73: /// <value>
74: /// A <see cref="System.Text.Encoding"/> object representing the
75: /// character encoding that is used by this instance.
76: /// </value>
77: public Encoding Encoding
78: {
79: get { return encoding; }
80: }
81:
82: /// <summary>
83: /// Gets a <see cref="JoergJooss.SharpAgent.Http.UrlEncoder"/> for the
84: /// Latin 1 (ISO-8859-1) character encoding.
85: /// </summary>
86: public static UrlEncoder Latin1
87: {
88: get { return latin1; }
89: }
90:
91: /// <summary>
92: /// Gets a <see cref="JoergJooss.SharpAgent.Http.UrlEncoder"/> for
93: /// the Latin 9 (ISO-8859-15) character encoding.
94: /// </summary>
95: public static UrlEncoder Latin9
96: {
97: get { return latin9; }
98: }
99:
100: /// <summary>
101: /// Gets a <see cref="JoergJooss.SharpAgent.Http.UrlEncoder"/> for
102: /// the UTF-8 character encoding.
103: /// </summary>
104: public static UrlEncoder Utf8
105: {
106: get { return utf8; }
107: }
108:
109: /// <summary>
110: /// Gets the Base Class Library's default Encoding object
111: /// used by <see cref="System.Uri"/> and
112: /// <see cref="System.UriBuilder"/>.
113: /// </summary>
114: internal static Encoding DefaultUriEncoding
115: {
116: get { return Encoding.UTF8; }
117: }
118:
119: /// <summary>
120: /// Creates a new Uri object from an existing Uri but
121: /// using the UrlEncoder's character encoding.
122: /// </summary>
123: /// <param name="uri">A URI.</param>
124: /// <returns>The URI whose str string is encoded with
125: /// the UrlEncoder's character encoding.</returns>
126: /// <exception cref="System.ArgumentNullException">
127: /// uri is <c>null</c>.
128: /// </exception>
129: public Uri Encode(Uri uri)
130: {
131: if (uri == null)
132: {
133: throw new ArgumentNullException("uri");
134: }
135:
136: UriBuilder ub = new UriBuilder(uri);
137: ub.Query = EncodeString(uri.Query);
138:
139: return ub.Uri;
140: }
141:
142: /// <summary>
143: /// Creates a new Uri object from a string
144: /// using the UrlEncoder's character encoding.
145: /// </summary>
146: /// <param name="uri">A URI.</param>
147: /// <returns>The URI whose str string is encoded with
148: /// the UrlEncoder's character encoding.</returns>
149: /// <exception cref="System.ArgumentNullException">
150: /// uri is <c>null</c>.
151: /// </exception>
152: public Uri Encode(string uri)
153: {
154: return Encode(new Uri(uri));
155: }
156:
157: /// <summary>
158: /// URL encodes a string with the UrlEncoder's encoding.
159: /// </summary>
160: /// <remarks>
161: /// <paramref name="str"/> is URL decoded before it is being
162: /// encoded. This method assumes UTF-8 as original character
163: /// encoding.
164: /// </remarks>
165: /// <param name="str">A string.</param>
166: /// <returns>A URL encoded representation of <paramref name="str"/>
167: /// using the UrlEncoder's encoding.
168: /// </returns>
169: public string EncodeString(string str)
170: {
171: return EncodeString(str, null);
172: }
173:
174: /// <summary>
175: /// URL encodes a (potentially already URL encoded) string
176: /// with the UrlEncoder's encoding.
177: /// </summary>
178: /// <remarks>
179: /// <paramref name="str"/> is URL decoded before it is being
180: /// encoded, using <paramref name="enc"/> as original character
181: /// encoding.
182: /// </remarks>
183: /// <param name="str">A string.</param>
184: /// <param name="enc">The original character encoding. If <c>null</c>
185: /// is passed, UTF-8 is assumed. </param>
186: /// <returns>A URL encoded representation of <paramref name="str"/>
187: /// using the UrlEncoder's encoding.
188: /// </returns>
189: public string EncodeString(string str, Encoding enc)
190: {
191: if (str == null)
192: {
193: throw new ArgumentNullException("uri");
194: }
195:
196: if (str == String.Empty)
197: {
198: return String.Empty;
199: }
200:
201: StringBuilder sb = new StringBuilder(str.Length);
202: NameValueCollection coll = HttpUtility.ParseQueryString(
203: str,
204: enc != null ? enc : DefaultUriEncoding);
205:
206: foreach (string key in coll)
207: {
208: string encodedKey = HttpUtility.UrlEncode(key, Encoding);
209: string encodedValue = HttpUtility.UrlEncode(coll[key], Encoding);
210: sb.AppendFormat("{0}={1}&", encodedKey, encodedValue);
211: }
212:
213: sb.Length -= 1;
214: return sb.ToString();
215: }
216: }
217: }
by Jörg Jooss
24. September 2004 05:22
Setting up dasBlog isn't hard at all. Actually, it's a breeze if you have just basic knowledge of IIS. Knowing ASP.NET won't hurt either, but you don't need to program in order to use the software, unless you find a silly nagging bug that drives you crazy…
In my case, some of the advanced features of FreeTextBox like color pickers or the code formatter wouldn't work — all I got was a security error. After digging through dasBlog's forum over at GotDotNet, I found the problem. In SiteSecurity.cs, there are two calls (lines 60 and 71) to HttpServerUtility.MapPath() that do not properly specify the path to the config file. Fixing it easy — prepend a root path reference "~":
1: using (StreamWriter writer =
2: new StreamWriter(HttpContext.Current.Server.MapPath("~/SiteConfig/siteSecurity.config")))
If you want to rebuild dasBlog, make sure to map a virtual directory called "DasBlog" to dasBlog's web application project directory. For example, I have put the project tree on the path C:\Shared\WebApps\DasBlogSource. Thus, I had to map the virtual directoy "DasBlog" to C:\Shared\WebApps\DasBlogSource\newtelligence.DasBlog.Web. Oddly enough, Visual Studio .NET 2003 refused to load the web application project while loading the solution. I simply ignored the error and was able to reconnect to the web project from within the project tree (there's an option in the project node's context menu).
So here I am in blog space.