I have been hesitant to setup URL Rewriting using IIS. Previously, I setup simple redirects that never seem to work correctly. Plus figuring out the right Regex is a pain.
So I thought I might try again by setting up a single url call to be URL Friendly. I used the IIS URL Rewrite module. I was able to get the URL to accept and process my friendly url but, I had issues with post backs and then the required validators quit working.
So after a little research, I decided to try ASP.NET URL Routing available to for web form in 4.0. It turned out to be pretty easy for what I needed. Here are my steps:
Set up ASP.NET URL Routing in web.config
ASP.NET Development Server:
<httpModules>
<add name="RoutingModule"
type="System.Web.Routing.UrlRoutingModule,
System.Web.Routing,
Version=3.5.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"/>
<!-- ... -->
</httpModules>
IIS 7:
<modules runAllManagedModulesForAllRequests="true">
....
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</modules>
<handlers>
<add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, PublicKeyToken=b03f5f7f11d50a3a" />
</handlers>
I wanted the url look like this:
http:/mysite.com/program/submit/12356789
But act like this:
http:/mysite.com/program/submit/page.aspx?key=12356789
I wanted to pick up the 'key' query string and then go my merry way with it.
So with the URL routing I needed to setup some code in the Global.asax to tell it how to set up the identify and use the url:
void Application_Start(object sender, EventArgs e)
{
....
RegisterRoutes(System.Web.Routing.RouteTable.Routes);
}
void RegisterRoutes(System.Web.Routing.RouteCollection routes)
{
routes.MapPageRoute(
"View Page", // Route name
"program/submit/{key}", // Route URL
"~/program/submit/page.aspx" // Web page to handle route
);
}
Inside the Page.aspx Page_load I can pick up the 'key' value like this:
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
return;
if (Page.RouteData.Values["key"] != null || Page.RouteData.Values["key"] != string.Empty)
{
string key = Page.RouteData.Values["key"].ToString(); //Request.QueryString["key"].ToString();
...
}
Also, in pages that produce the user friendly url, I was able to build the url using the GetRouteUrl method:
string guid = "XXXX";
string url = Page.GetRouteUrl("View CIK", new { key = guid }); //returns: program/submit/1234567
So far it seems like I am not having any post back issues. So I think I'm liking this feature! So maybe I will set up a few more links as friendly urls sometime soon.
Wednesday, May 4, 2011
Thursday, April 7, 2011
Having Trouble with TabIndex in ASP.NET?
I have had some trouble with my TabIndex on a couple of ASP.NET forms for a long while now. I just recently decided to investigate further on this annoying issue (really who has time for that stuff?!).
For the most part, this property does work correctly, however, inside of Wizard or ASP.NET pre-made controls (Login), it seems to not function as you would expect?
Let's start with the first check, make sure that you set the first TabIndex to 1 (TabIndex="1"). Supposedly browsers default the address bar to 0 so you are supposed to set yours to 1. If that does not fix your issue, continue.
If you are using a Wizard control or Login control, set the Wizard or Login control to TabIndex="1", then proceed to number the rest of your TextBoxes and stuff from there. That should fix it.
Amazingly, this solution was my own idea, I couldn't find anything on the 'net that covered this issue! So now I'm posting to help some poor busy programmer left to solve such an annoying problem.
Hope that helps someone.
For the most part, this property does work correctly, however, inside of Wizard or ASP.NET pre-made controls (Login), it seems to not function as you would expect?
Let's start with the first check, make sure that you set the first TabIndex to 1 (TabIndex="1"). Supposedly browsers default the address bar to 0 so you are supposed to set yours to 1. If that does not fix your issue, continue.
If you are using a Wizard control or Login control, set the Wizard or Login control to TabIndex="1", then proceed to number the rest of your TextBoxes and stuff from there. That should fix it.
Amazingly, this solution was my own idea, I couldn't find anything on the 'net that covered this issue! So now I'm posting to help some poor busy programmer left to solve such an annoying problem.
Hope that helps someone.
Labels:
Asp.Net,
Login Control,
TabIndex,
Wizard Control
Friday, March 4, 2011
Gmail - Email Blast - Fix Ugly Table Gap with Images
I recently changed my email blast code which then began creating gaps in between images in a table, however, the problem only seemed to be in Gmail. Everything else looked ok.
The fix was easy. I just had to add style="display:block" to the image tag. And it was good to go!
I took the tip from this site: http://www.nurulam.in/2010/06/email-newsletter-extra-table-row-gap-in-gmail/
Thought I would share just in case someone else needed to know.
The fix was easy. I just had to add style="display:block" to the image tag. And it was good to go!
I took the tip from this site: http://www.nurulam.in/2010/06/email-newsletter-extra-table-row-gap-in-gmail/
Thought I would share just in case someone else needed to know.
Tuesday, December 14, 2010
Sys.InvalidOperationException: Could not find UpdatePanel with ID 'xxx'
Recently, I was working on an AJAX UpdatePanel with multiple triggers. I noticed I received the following JavaScript error in IE7, but not FireFox:
'Sys.InvalidOperationException: Could not find UpdatePanel with ID 'xxx'. If it is being updated dynamically then it must be inside another UpdatePanel.'
My page was written as follows (with each trigger on its own line (to make it readable)):
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate></ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="btnImg1" EventName="Click" />
<asp:AsyncPostBackTrigger ControlID="btnImg2" EventName="Click" />
<asp:AsyncPostBackTrigger ControlID="btnImg3" EventName="Click" />
<asp:AsyncPostBackTrigger ControlID="btnImg4" EventName="Click" />
</Triggers>
</asp:UpdatePanel>
I had read that adding the UpdateMode=Conditional and adding an specific EventName to each trigger would maybe resolve my problem, however, it did not, but I left it in just in case.
I had only noticed this problem in IE after I had added the fourth trigger. I also noticed if I removed the fourth trigger, there was no JavaScript error. So I then change my page to the following (put all the triggers on a single line):
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate></ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="btnImg1" EventName="Click" /><asp:AsyncPostBackTrigger ControlID="btnImg2" EventName="Click" /> <asp:AsyncPostBackTrigger ControlID="btnImg3" EventName="Click" /><asp:AsyncPostBackTrigger ControlID="btnImg4" EventName="Click" />
</Triggers>
</asp:UpdatePanel>
This seemed to fix the problem, even moving it to two lines worked. So I guess it was a whitespace issue? Very strange...only in IE!
*note my content template it blank because I'm actually working with a swfobject to play a movie...so there is a lot more code here but I'm only showing the specific area that was broken...
'Sys.InvalidOperationException: Could not find UpdatePanel with ID 'xxx'. If it is being updated dynamically then it must be inside another UpdatePanel.'
My page was written as follows (with each trigger on its own line (to make it readable)):
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate></ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="btnImg1" EventName="Click" />
<asp:AsyncPostBackTrigger ControlID="btnImg2" EventName="Click" />
<asp:AsyncPostBackTrigger ControlID="btnImg3" EventName="Click" />
<asp:AsyncPostBackTrigger ControlID="btnImg4" EventName="Click" />
</Triggers>
</asp:UpdatePanel>
I had read that adding the UpdateMode=Conditional and adding an specific EventName to each trigger would maybe resolve my problem, however, it did not, but I left it in just in case.
I had only noticed this problem in IE after I had added the fourth trigger. I also noticed if I removed the fourth trigger, there was no JavaScript error. So I then change my page to the following (put all the triggers on a single line):
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate></ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="btnImg1" EventName="Click" /><asp:AsyncPostBackTrigger ControlID="btnImg2" EventName="Click" /> <asp:AsyncPostBackTrigger ControlID="btnImg3" EventName="Click" /><asp:AsyncPostBackTrigger ControlID="btnImg4" EventName="Click" />
</Triggers>
</asp:UpdatePanel>
This seemed to fix the problem, even moving it to two lines worked. So I guess it was a whitespace issue? Very strange...only in IE!
*note my content template it blank because I'm actually working with a swfobject to play a movie...so there is a lot more code here but I'm only showing the specific area that was broken...
Monday, November 22, 2010
Deleted SSL Certificate Request - Recreate Request
After importing a new certificate, I deleted it because the friendly name was blank. (little did I know that was a known error with a simple fix.) However, I figured I had done something wrong when I installed so I deleted the certificate, which left me with no pending certificate request to complete the install again.
So now, I had to either contact the CA and re-process the request, or somehow get my pending request back. I went for 'get my pending request back'.
After googling for this info, I was able to find some help on this. So these are the steps I did to accomplish this. (Windows 2008 Server/IIS 7)
1. Click Start, point to Run, type cmd, and then click OK.
2. Navigate to the directory where Certutil.exe is stored; by default, this is %windir%\system32.
3. Type the following command at the command prompt: certutil -addstore my "C:\junk\www_sitename_com.cer" *a little note "my" is a part of the command, at first i thought it was part of an example name.
You should see the following message somewhere in the message that follows: CertUtil: -addstore command completed successfully.
4. Get the Thumbprint of the certificate.
5. Start- type 'mmc'. File - Add/Remove Snapin. Add Certificates, Select Local Computer.
6. Next, go to: Certificates - Certificate Enrollment Requests - Certificate. Double-click the certificate.
7. Go to the Details tab, scroll down, copy the weird thumbprint value, paste into Notepad for reference.
8. Return to the Command prompt, type the following command: certutil -repairstore my "xx dd xx xx oo" (include your own thumbprint value in double quotes.)
Go back to the IIS and follow the steps to Complete Certificate request.
And, btw, don't forget to go back to the IIS manager, and select the new certificate for the SSL/HTTPS binding
for the site.
So now, I had to either contact the CA and re-process the request, or somehow get my pending request back. I went for 'get my pending request back'.
After googling for this info, I was able to find some help on this. So these are the steps I did to accomplish this. (Windows 2008 Server/IIS 7)
1. Click Start, point to Run, type cmd, and then click OK.
2. Navigate to the directory where Certutil.exe is stored; by default, this is %windir%\system32.
3. Type the following command at the command prompt: certutil -addstore my "C:\junk\www_sitename_com.cer" *a little note "my" is a part of the command, at first i thought it was part of an example name.
You should see the following message somewhere in the message that follows: CertUtil: -addstore command completed successfully.
4. Get the Thumbprint of the certificate.
5. Start- type 'mmc'. File - Add/Remove Snapin. Add Certificates, Select Local Computer.
6. Next, go to: Certificates - Certificate Enrollment Requests - Certificate. Double-click the certificate.
7. Go to the Details tab, scroll down, copy the weird thumbprint value, paste into Notepad for reference.
8. Return to the Command prompt, type the following command: certutil -repairstore my "xx dd xx xx oo" (include your own thumbprint value in double quotes.)
Go back to the IIS and follow the steps to Complete Certificate request.
And, btw, don't forget to go back to the IIS manager, and select the new certificate for the SSL/HTTPS binding
for the site.
Monday, November 15, 2010
IIS 7 Secure Files Type like .txt or .html
I've had an .Net 2.0/IIS 4 site running for a long time that was to provide access to documents, but the documents were to be categorized and secured. I did my best to obscure the file structure and document names and not allow bot files in. But I was never quite satisfied with this because you could not fully secure the files such as .doc, or .txt. There was potential to serve up these pages, very annoying.
My understanding of the IIS 7.0/4.0 Integrated Pipeline is that you can secure files other than .aspx. I started using IIS 7 months ago, but never really understood what this new integrated pipeline could do for me until recently. I had to dig to even figure this out on my own. sigh...
Anyhow, by putting your site in 4.0 Integrated Pipeline mode and adding a few configuration lines to a "Form secured site", you can now require security on those "other" file types. Yes, just what i needed!
It was pretty simple, and I am hoping I'm right on with my understanding. If I'm wrong please let me know!!
I just modified the Application Pool to use 4.0 Integrated Mode. Then added the following lines to the web.config, inside the system.webserver tag:
<modules>
<remove name="FormsAuthenticationModule" />
<add name="FormsAuthenticationModule" type="System.Web.Security.FormsAuthenticationModule" />
<remove name="UrlAuthorization" />
<add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
<remove name="DefaultAuthentication" />
<add name="DefaultAuthentication" type="System.Web.Security.DefaultAuthenticationModule" />
</modules>
My understanding of the IIS 7.0/4.0 Integrated Pipeline is that you can secure files other than .aspx. I started using IIS 7 months ago, but never really understood what this new integrated pipeline could do for me until recently. I had to dig to even figure this out on my own. sigh...
Anyhow, by putting your site in 4.0 Integrated Pipeline mode and adding a few configuration lines to a "Form secured site", you can now require security on those "other" file types. Yes, just what i needed!
It was pretty simple, and I am hoping I'm right on with my understanding. If I'm wrong please let me know!!
I just modified the Application Pool to use 4.0 Integrated Mode. Then added the following lines to the web.config, inside the system.webserver tag:
<modules>
<remove name="FormsAuthenticationModule" />
<add name="FormsAuthenticationModule" type="System.Web.Security.FormsAuthenticationModule" />
<remove name="UrlAuthorization" />
<add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
<remove name="DefaultAuthentication" />
<add name="DefaultAuthentication" type="System.Web.Security.DefaultAuthenticationModule" />
</modules>
Thursday, September 16, 2010
Tips: FedEx WebService for Rate Quotes C#/Asp.Net
After a long run of using the UPS quotes for online orders, we moved to FedEx. So that meant, I needed to update the ecommerce app to reflect our new shipper.
This task wasn't *too* hard since I had already written the UPS one about 4 years ago, but the FedEx one uses a WebService, for the UPS rates, I sent an HttpWebRequest. I don't know why they don't have webservice, maybe they do now...
Anyhow, hopefully these tips will help get you started on your way.
1. Get the WSDL. To do this, I downloaded the WSDL file from FedEx, this file includes the RateService_v9.wsdl file. I unzipped this file to my local webserver, then to my production webserver.
2. Create a reference to the WebService in Visual Studio. In my project, I right-clicked then selected Add Web Reference. Then I pointed the reference to my localhost (http://localhost/fedex/RateService_v9.wsdl) and gave it a name.
3. Requested a Test Key from FedEx that I plugged into the values Account Number, Meter Number, Key and Password.
4. Downloaded the FedEx example for getting Rate Service Quotes. Modified it to my liking. Here's a snippet:
RateRequest request = new RateRequest();
RateService service = new RateService(); // Initialize the service
request.WebAuthenticationDetail = new WebAuthenticationDetail();
request.WebAuthenticationDetail.UserCredential = new WebAuthenticationCredential();
request.WebAuthenticationDetail.UserCredential.Key = this._key; // Replace "XXX" with the Key
request.WebAuthenticationDetail.UserCredential.Password = this._password; // Replace "XXX" with the Password
//
request.ClientDetail = new ClientDetail();
request.ClientDetail.AccountNumber = this._accountNumber; // account number
request.ClientDetail.MeterNumber = this._meter; // meter number
//
request.TransactionDetail = new TransactionDetail();
request.TransactionDetail.CustomerTransactionId = "MY-RATE-REQUEST";
//
request.Version = new VersionId(); // automatically set from wsdl
if (packages != null && packages.Count > 0)
{
SetShipmentDetails(request, packages.Count);
int i = 0;
decimal totalWeight = 0.0M;
foreach (Package package in packages){
RequestedPackageLineItem rpli = new RequestedPackageLineItem();
Weight packageWeight = new Weight();
packageWeight.Value = (decimal)package.Weight;
packageWeight.Units = WeightUnits.LB;
rpli.Weight = packageWeight;
totalWeight += (decimal)package.Weight;
request.RequestedShipment.RequestedPackageLineItems[i] = rpli;
i++;
}
Weight weight = new Weight();
weight.Value = totalWeight;
weight.Units = WeightUnits.LB;
request.RequestedShipment.TotalWeight = weight;
}
.....
5. Once I was satisfied with the results, I requested a Production Key from FedEx then plugged in those values. I kept receiving a "Meter Number is missing or invalid".
This where I got tripped up. I had to change the URL being called in the app.config (that seems to have been created for me with properties in it). There is a url value that is referenced by the WebService.
However, even though I kept changing this value, it would continue to use the old value which was the test site, and of course, give me the same error - since my meter number was a production one not a test one.
Finally, I thought to open up the Properties - Settings.settings file. Right when I double-clicked the file to open it, the proprety inside there was updated with the new value I had in the app.config file.
I assume it was some sort of Visual Studio 2010 thing. It seems like it was supposed to auto-update but it didn't. Later, to be sure, I just updated it manually.
After that everything worked like a charm.
FYI:
(for SOAP Requests)
The test url is: https://wsbeta.fedex.com:443/web-services/rate
The production url is: https://gateway.fedex.com:443/web-services
This task wasn't *too* hard since I had already written the UPS one about 4 years ago, but the FedEx one uses a WebService, for the UPS rates, I sent an HttpWebRequest. I don't know why they don't have webservice, maybe they do now...
Anyhow, hopefully these tips will help get you started on your way.
1. Get the WSDL. To do this, I downloaded the WSDL file from FedEx, this file includes the RateService_v9.wsdl file. I unzipped this file to my local webserver, then to my production webserver.
2. Create a reference to the WebService in Visual Studio. In my project, I right-clicked then selected Add Web Reference. Then I pointed the reference to my localhost (http://localhost/fedex/RateService_v9.wsdl) and gave it a name.
3. Requested a Test Key from FedEx that I plugged into the values Account Number, Meter Number, Key and Password.
4. Downloaded the FedEx example for getting Rate Service Quotes. Modified it to my liking. Here's a snippet:
RateRequest request = new RateRequest();
RateService service = new RateService(); // Initialize the service
request.WebAuthenticationDetail = new WebAuthenticationDetail();
request.WebAuthenticationDetail.UserCredential = new WebAuthenticationCredential();
request.WebAuthenticationDetail.UserCredential.Key = this._key; // Replace "XXX" with the Key
request.WebAuthenticationDetail.UserCredential.Password = this._password; // Replace "XXX" with the Password
//
request.ClientDetail = new ClientDetail();
request.ClientDetail.AccountNumber = this._accountNumber; // account number
request.ClientDetail.MeterNumber = this._meter; // meter number
//
request.TransactionDetail = new TransactionDetail();
request.TransactionDetail.CustomerTransactionId = "MY-RATE-REQUEST";
//
request.Version = new VersionId(); // automatically set from wsdl
if (packages != null && packages.Count > 0)
{
SetShipmentDetails(request, packages.Count);
int i = 0;
decimal totalWeight = 0.0M;
foreach (Package package in packages){
RequestedPackageLineItem rpli = new RequestedPackageLineItem();
Weight packageWeight = new Weight();
packageWeight.Value = (decimal)package.Weight;
packageWeight.Units = WeightUnits.LB;
rpli.Weight = packageWeight;
totalWeight += (decimal)package.Weight;
request.RequestedShipment.RequestedPackageLineItems[i] = rpli;
i++;
}
Weight weight = new Weight();
weight.Value = totalWeight;
weight.Units = WeightUnits.LB;
request.RequestedShipment.TotalWeight = weight;
}
.....
5. Once I was satisfied with the results, I requested a Production Key from FedEx then plugged in those values. I kept receiving a "Meter Number is missing or invalid".
This where I got tripped up. I had to change the URL being called in the app.config (that seems to have been created for me with properties in it). There is a url value that is referenced by the WebService.
However, even though I kept changing this value, it would continue to use the old value which was the test site, and of course, give me the same error - since my meter number was a production one not a test one.
Finally, I thought to open up the Properties - Settings.settings file. Right when I double-clicked the file to open it, the proprety inside there was updated with the new value I had in the app.config file.
I assume it was some sort of Visual Studio 2010 thing. It seems like it was supposed to auto-update but it didn't. Later, to be sure, I just updated it manually.
After that everything worked like a charm.
FYI:
(for SOAP Requests)
The test url is: https://wsbeta.fedex.com:443/web-services/rate
The production url is: https://gateway.fedex.com:443/web-services
Labels:
Asp.Net,
C#,
FedEx WebService,
FedEx WSDL,
Rates Available
Subscribe to:
Posts (Atom)