Joseph Michael Pesch
VP Programming

Restart IIS Web Site from C# Code

by 24. April 2016 04:58

In some cases I have setup caching on the website for items such as menus, header, footer, etc.  However, when there is a CMS in place on the site and a user changes any of that relevant content there needs to be a way to reset the web cache.  One easy way is to force the IIS web site to restart by making a benign change in the web.config file, which will automatically force the restart.  Below is a sample version of code to do this, where I update an otherwise unused entry in the web.config that just indicates the last date/time of restart request by the user.  In the case of an MVC application I put this into a controller with some specific route provided to the CMS users so they can navigate to that route after making CMS changes that need to be updated in the cache.  As a side note, using this method will require granting write access to the web.config file for the IIS application pool user (by default "IIS AppPool\WebSiteName" user).

    public static void RestartWeb()
    {
      var configuration = WebConfigurationManager.OpenWebConfiguration("~");
      var section = (AppSettingsSection)configuration.GetSection("appSettings");
      section.Settings["LastRestart"].Value = 
        DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToLongTimeString();
      configuration.Save();
    }

Tags:

ASP.Net | C#

Piranha CMS Uses TinyMCE and Removes Certain Tags Like FontAwesome <i> Tags

by 9. May 2015 10:27

I was using Piranha CMS with the out-of-the-box implementation in conjunction with an MVC Bootstrap HTML/CSS template.  This was working great with the exception of certain tags (specifically in my case the <i> tag used in conjunction with FontAwesome) would get removed.  This is because by default the TinyMCE html editor that Piranha CMS uses will only retain the configured allowable tags.  I fixed this issue by adding my <i> tag to the configuration of allowable tags using the "extended_valid_elements" configuration setting of TinyMCE.  This setting adds additional allowed tags/attributes in the TinyMCE editor in addition to the default allowed tags/attributes. 

In my case the specific setting looked like this: extended_valid_elements : 'i[*]'

In order to implement this with Piranha CMS I needed to add a new file "TinyMCE.cshtml" to the folder "Areas\Manager\Views\Shared\Partial" in my MVC project (side note, that full folder path did not already exist so I added the necessary sub-folders).  Here is the full content of the TinyMCE.cshtml file:

<script type="text/javascript" src="~/res.ashx/areas/manager/content/js/ext/tiny_mce/tinymce.min.js"></script>
<script type="text/javascript">
  tinymce.init({
    extended_valid_elements : 'i[*]',
    mode: 'specific_textareas',
    editor_selector: "editor",
    convert_urls: false,
    plugins: [
        "autoresize autolink code hr paste piranhaimage link"
    ],
    width: "100%",
    height: "340",
    autoresize_min_height: 340,
    @if (File.Exists(Server.MapPath("~/areas/manager/content/css/editor.css"))) {
        <text>content_css: "@Url.Content("~/areas/manager/content/css/editor.css")",</text>
        }
    toolbar: "bold italic underline | bullist numlist hr | formatselect removeformat | cut copy paste | link piranhaimage | code",
  });
</script>

Tags:

ASP.Net | MVC

Restore NUGET Packages in Visual Studio Online TFS Continuous Integration Builds

by 16. February 2015 13:25

http://geekswithblogs.net/TarunArora/archive/2014/09/14/auto-restore-nuget-packages-with-tfs-build-the-right-way.aspx

  • Step 1 - Enable NuGet Package Restore

        image

        Projects that use MSBuild-Integrated package restore typically contain a .nuget folder with three files:

    • NuGet.Config
    • NuGet.exe
    • NuGet.targets

Since the presence of a NuGet.targets file determines whether NuGet will continue to use the MSBuild-Integrated approach, this file must be removed. Also, as the .nuget\NuGet.exe file is not used by Automatic Package Restore, it likewise can be removed. By default, the NuGet.Config file instructs NuGet to bypass adding package binaries to source control. Automatic Package Restore will honour this as long as you leave this file in place. Note that NuGet.Config only has an effect when using Visual Studio to integrate with Team Foundation Server (TFS). In addition to these files, NuGet modifies the project files in the solution to reference the NuGet.targets file so it can participate in the build process. When migrating to Automatic Package Restore, these references must also be removed.

       Remove the NuGet.exe and NuGet.targets files from the solution's .nuget folder. Make sure the files themselves are also removed from the solution workspace.
       Retain the NuGet.Config file to continue to bypass adding packages to source control.

  • Step 2 – Close Down Visual Studio

        Each project in your solution that has picked up any of the below listed tags in the project properties will need to be removed. Open the project in Notepad and look for the below tags, remove these tags…

1. ------------------- 
<RestorePackages>true</RestorePackages>
----------------------

2. -------------------
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />  
----------------------

3. --------------------
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">  
    <PropertyGroup>
        <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, 
see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
    </PropertyGroup>
    <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
------------------------
  • Step 3 – Open the solution and check in the changes

        Once you have made the above listed changes, you are good to check in the changes.

Trigger the  build now, the build server should now be able to successfully resolve the package references…

More details here… http://docs.nuget.org/docs/workflows/using-nuget-without-committing-packages

Tags:

ASP.Net | TFS | Visual Studio

Issue with ASP.Net Identity from Default Local File System Database to SQL Server Based Database

by 22. December 2014 10:12

I created a new MVC 5 web application with the "Individual User Account" option that configures the ASP.Net Identity model.  By default this uses Entity Framework Code First model with a local file system database created in the local App_Data folder.  I tried moving this to an actual SQL Server database location by creating the five dbo.aspnet_... tables and changing the web.config to point to the new location.  However, in doing so I received the error below.  Turns out to solve this I just needed to change the connection string in the web.config from the Entity Framework style to the basic style (see old vs. new connection strings below).

 <connectionStrings>

    <addname="NewSimpleConnection"connectionString="Data Source=SERVER_NAME_HERE;initial catalog=DB_NAME_HERE;user id=USER_ID_HERE;password=PASSWORD_HERE;Integrated Security=False"providerName="System.Data.SqlClient" />

    <addname="OldEtityFConnection"connectionString="metadata=res://*/MyModel.csdl|res://*/MyModel.ssdl|res://*/MyModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=SERVER_NAME_HERE;initial catalog=DB_NAME_HERE;user id=USER_ID_HERE;password=PASSWORD_HERE;MultipleActiveResultSets=True;App=EntityFramework&quot;"providerName="System.Data.EntityClient" />

  </connectionStrings> 

Server Error in '/' Application.


 

Unable to load the specified metadata resource.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Data.Entity.Core.MetadataException: Unable to load the specified metadata resource.

Source Error:

 

Line 114:      {
Line 115:        var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
Line 116:        IdentityResult result = await UserManager.CreateAsync(user, model.Password);
Line 117:        if (result.Succeeded)
Line 118:        {

Tags:

ASP.Net | C# | SQL Server

Forcing HTTPS Over HTTP

by 7. July 2014 15:32

The <rewrite> section below will force HTTPS over HTTP:

  <system.webServer>
    <modules>
      <remove name="FormsAuthenticationModule" />
    </modules>
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="OPTIONSVerbHandler" />
      <remove name="TRACEVerbHandler" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
    <rewrite>
      <rules>
        <rule name="Redirect to https">
          <match url="(.*)"/>
          <conditions>
            <add input="{HTTPS}" pattern="Off"/>
            <add input="{REQUEST_METHOD}" pattern="^get$|^head$" />
          </conditions>
          <action type="Redirect" url="https://{HTTP_HOST}/{R:1}"/>
        </rule>
      </rules>
    </rewrite>
  </system.webServer>

Tags:

ASP.Net

ASP.Net MVC Bind Boolean to DropDownList Using @Html.DropDownListFor

by 25. June 2014 07:41

I was having difficulty binding a bool property to a DropDownList using the @Html.DropDownListFor() method.  What ended up working well was to do the following:

 

Add this to the View Controller class:

private void Select_YN_Rebind()
{
  List<SelectListItem> Select_YN = new List<SelectListItem>();
  Select_YN.Add(new SelectListItem
  {
    Text = "No",
    Value = bool.FalseString
  });
  Select_YN.Add(new SelectListItem
  {
    Text = "Yes",
    Value = bool.TrueString
  });
  ViewData["Select_YN"] = Select_YN;
}


Add call to the new method in the View Controller Create method (or whatever other method is appropriate) before returning the view:

public ActionResult Create()
{
  Select_YN_Rebind();
  return View();
}

Call the @Html.DropDownListFor() as shown below:

 @Html.DropDownListFor(model => model.YourBoolPropertyHere
, (IEnumerable<SelectListItem>)ViewData["Select_YN"])

 

Tags:

ASP.Net | MVC

ASP.Net MVC 5 Issue Validating Numbers (Decimal) when Number Contains Commas

by 20. June 2014 11:37

ASP.Net MVC 5 number (decimal) validation failing when user entered data contains commas.  To get around this issue I created a custom

Create the following object and bind it in the Global.asax as shown below.

GLOBAL.ASAX

ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());

NEW .CS FILE

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MyApp.Models.Binders
{
  public class DecimalModelBinder : IModelBinder
  {
    public object BindModel(ControllerContext controllerContext,
        ModelBindingContext bindingContext)
    {
      ValueProviderResult valueResult = bindingContext.ValueProvider
          .GetValue(bindingContext.ModelName);
      ModelState modelState = new ModelState { Value = valueResult };
      object actualValue = null;
      try
      {
        //Check if this is a nullable decimal and a null or empty string has been passed
        var isNullableAndNull = (bindingContext.ModelMetadata.IsNullableValueType &&
                                 string.IsNullOrEmpty(valueResult.AttemptedValue));

        //If not nullable and null then we should try and parse the decimal
        if (!isNullableAndNull)
        {
          actualValue = Convert.ToDecimal(valueResult.AttemptedValue, CultureInfo.CurrentCulture);
        }
      }
      catch (FormatException e)
      {
        modelState.Errors.Add(e);
      }

      bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
      return actualValue;
    }
  }
}

Tags:

ASP.Net

ASP.Net MVC 5 Issue Validating Numbers (Decimal) when Number Contains Commas

by 20. June 2014 11:37

ASP.Net MVC 5 number (decimal) validation failing when user entered data contains commas.  To get around this issue I created a custom

Create the following object and bind it in the Global.asax as shown below.  NOTE: For javascript client side validations create a new *.js file and make sure to include it AFTER the jquery.validate* in the BundleConfig.cs.

JAVASCRIPT OVERRIDE

$.validator.methods.range = function (value, element, param) {
  var globalizedValue = value.replace(",", ".");
  return this.optional(element) || (globalizedValue >= param[0] && globalizedValue <= param[1]);
}
$.validator.methods.number = function (value, element) {
  return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:[\s\.,]\d{3})+)(?:[\.,]\d+)?$/.test(value);
};

GLOBAL.ASAX ADDITION

ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());

NEW .CS FILE

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MyApp.Models.Binders
{
  public class DecimalModelBinder : IModelBinder
  {
    public object BindModel(ControllerContext controllerContext,
        ModelBindingContext bindingContext)
    {
      ValueProviderResult valueResult = bindingContext.ValueProvider
          .GetValue(bindingContext.ModelName);
      ModelState modelState = new ModelState { Value = valueResult };
      object actualValue = null;
      try
      {
        //Check if this is a nullable decimal and a null or empty string has been passed
        var isNullableAndNull = (bindingContext.ModelMetadata.IsNullableValueType &&
                                 string.IsNullOrEmpty(valueResult.AttemptedValue));

        //If not nullable and null then we should try and parse the decimal
        if (!isNullableAndNull)
        {
          actualValue = Convert.ToDecimal(valueResult.AttemptedValue, CultureInfo.CurrentCulture);
        }
      }
      catch (FormatException e)
      {
        modelState.Errors.Add(e);
      }

      bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
      return actualValue;
    }
  }
}

Tags:

ASP.Net | MVC

jQuery $(document).ready() Not Working with ASP.Net UpdatePanel PostBack

by 27. September 2013 20:21

When using jQuery and $(document).ready() event along with an ASP.Net UpdatePanel the $(document).ready() event will only fire on the initial page load and will not fire on subsequent AJAX postbacks via the UpdatePanel.  There are a couple of common solutions posted to deal with this (shown below).  My preference is the first option which seems cleaner and has proven more reliable for me.

Solution 1:  Wrap the $(document).ready() event inside a pageLoad() function since the AJAX postback will always fire the pageLoad() event function.

function pageLoad() {
  $(document).ready(function () {
    DoPageInitialization();
  });
}

Solution 2:  Add a reference to the PageRequestManager and register your function to the add_endrequest() event.

$(document).ready(function() {
  // Still needed as the PageRequestManager below only fires on the postback...
  DoPageInitialization();
});
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(function() {
  // Secondary binding for all AJAX postbacks through the UpdatePanel
  DoPageInitialization();
});

function DoPageInitialization() {
  ... Document Ready Code Here ...
}


Here is a good post that goes into more details on both $(document).ready() and pageLoad()
http://encosia.com/document-ready-and-pageload-are-not-the-same/

Tags:

ASP.Net | JavaScript | JQuery

jQuery Document.Ready Doesn't Work with ASP.Net UpdatePanel

by 27. July 2013 21:47

When using an ASP.Net UpdatePanel you may need to use the PageRequestManager to bind functions that would otherwise be bound on the page load or jQuery Document.Ready function (see below).  Link to blog article on this topic: http://stackoverflow.com/questions/256195/jquery-document-ready-and-updatepanels 

$(document).ready(function() {
    // bind your jQuery events here initially
});

var prm = Sys.WebForms.PageRequestManager.getInstance();

prm.add_endRequest(function() {
    // re-bind your jQuery events here
});

Tags:

ASP.Net | JQuery

A Guide to Designing and Building RESTful Web Services with WCF 3.5

by 22. July 2013 07:09

Excerpt from this article:  http://msdn.microsoft.com/en-us/library/dd203052.aspx

From the point of view of REST, a “real” Web service is a service designed to embrace the “Web” from the ground up. The Web has become the most successful distributed computing platform ever developed, thanks to years of investment and widespread usage. The Web platform includes concepts that you’re probably already familiar with like URIs, HTTP, and common data formats like XML, RSS/ATOM, and JSON. Ultimately, services that embrace these core concepts will immediately derive the valuable benefits they provide.

REST defines an architectural style based on a set of constraints for building things the “Web” way. REST is not tied to any particular technology or platform – it’s simply a way to design things to work like the Web. People often refer to services that follow this philosophy as“RESTful services.” In this whitepaper, we’ll cover the fundamental REST design principles and show you how to build RESTful services with Windows Communication Foundation (WCF), the WCF REST Starter Kit, and ADO.NET Data Services.

Tags:

ASP.Net | REST

Sharepoint 2013 Development Using Visual Studio 2012

by 16. July 2013 13:14

Here is a good article on setting up Visual Studio 2012 to allow development of Sharepoint 2013 applications.

http://blogs.msdn.com/b/timquin/archive/2013/01/22/setting-up-visual-studio-2012-for-sharepoint-2013-development-offline.aspx

Tags:

ASP.Net | Sharepoint

Team Foundation Server (TFS) Building Project with External Assemblies

by 27. June 2013 08:47

This approach to fix the issue with using external assemblies (i.e. assemblies not in the project folder structure).  In this example the project references the "Encompass" assemblies from the "C:\Program Files (x86)\Encompass" folder.  I wanted to use the program file reference so it’s easy to upgrade SDK; however, Visual Studio Project always have relative paths which doesn’t get resolved in TFS Build.  To resolve this I have add new registry key for the Encompass folder path on the TFS build server so that it looks into this location when it compiles any project.  Now I don’t need to change the project file. 

Registry key: HKEY_LOCAL_MACHINE > SOFTWARE > Microsoft > .NET Framework > AssemblyFolders

Add new key (in this case "Encompass") and then update the "(default)" string value to be the path to the assemblies (in this case "C:\Program Files (x86)\Encompass").

Tags:

ASP.Net | Visual Studio | TFS

ASP.Net Security Create New User Account Based on Existing User Account

by 1. February 2013 07:39

This SQL script will create a new ASP.Net user account using an existing user account as the basis.  The new user account will have the same password and roles as the existing user account.  Also included at the end of the main script is a script to change a user account password to a known defualt (e.g. Password1234).


Script to replicate user:

declare 
  @ExisitingUserName varchar(50)
, @ExisitingUserEmail
, @OldUserID uniqueidentifier
, @UserID uniqueidentifier
, @Email varchar(50)
, @UserName varchar(50)
, @DeleteIfExists bit

select
  @UserID = newid()
, @Email = 'new.user@email.com'
, @UserName = 'new.user@email.com'
, @DeleteIfExists = 0
, @ExisitingUserName = 'EXISTING_USER_NAME_HERE'
select
  @UserID
, @Email
, @UserName

select @OldUserID = UserID 
from dbo.aspnet_Users 
where UserName = @UserName

if @DeleteIfExists = 1 begin

delete from dbo.aspnet_UsersInRoles
where UserID = @OldUserID

delete from dbo.aspnet_Membership
where UserID = @OldUserID

delete from dbo.aspnet_Users 
where UserName = @UserName

end

insert into dbo.aspnet_Users
select
  ApplicationId
, @UserID
, @UserName
, lower(@UserName) /* LoweredUserName */
, null /* MobileAlias */
, 0 /* IsAnonymous */ 
, getdate() /* LastActivityDate */
from dbo.aspnet_Users
where UserName = @ExisitingUserName

insert into dbo.aspnet_Membership
select
  ApplicationId
, @UserID
, Password
, PasswordFormat
, PasswordSalt
, MobilePIN
, @Email
, @Email
, PasswordQuestion
, PasswordAnswer
, IsApproved
, IsLockedOut
, getdate() /* CreateDate */
, getdate() /* LastLoginDate */
, getdate() /* LastPasswordChangedDate */
, '1/1/1754 12:00:00 AM' /* LastLockoutDate */
, 0 /* FailedPasswordAttemptCount */
, '1/1/1754 12:00:00 AM' /* FailedPasswordAttemptWindowStart */
, 0 /* FailedPasswordAnswerAttemptCount */
, '1/1/1754 12:00:00 AM' /* FailedPasswordAnswerAttemptWindowStart */
, Comment
from aspnet_Membership
where Email = @ExisitingUserEmail

insert into dbo.aspnet_UsersInRoles
select @UserID, RoleID
from dbo.aspnet_Roles

Script to change password:

update dbo.aspnet_Membership 
set PasswordSalt = 'dArJQD4e4upwMyqCSl9iNA=='
, Password = '1mAUj77CySFAtqlfInt4UFOznAc=' /* Password1234 */
where UserId = 
(
  select UserId 
  from dbo.aspnet_Users 
  where UserName = 'USER_NAME_HERE'
)

Tags:

ASP.Net | SQL Server

ASP.Net Membership Provider and Identity Login Password Hash with Salt/Security

by 9. January 2013 14:12

Here is a password for ASP.Net MembershipProvider
password: password1234
password salt: dArJQD4e4upwMyqCSl9iNA==
password: 5Xv212dMJiOQP2X+zhOZw66L+xU=

Here is a password for ASP.Net Identity
password: #%ND84yq
hash: AGRf+1XUhLtQsQnaT39V4sQegMmKvA7gsLBVtJ4YFLdvavNQFcqeHZ+ViuRoO9SkRg==
security: 86232225-81da-4d56-84ea-ded079d64384

Tags:

ASP.Net

Sys Undefined Exception in ASP.Net Web Form Menu Control Generated JS with URL Routing

by 16. October 2012 08:07

Although the exception only seems to occur when using URL routing it is not consistent (i.e. doesn't always happen).

The page generates this line at the end of the Form tag:

new Sys.WebForms.Menu({ element: 'Navigation Menu', disappearAfter: 500, orientation: 'horizontal', tabIndex: 0, disabled: false });

Throws JS runtime exception:

'Sys' is undefined

Solution:

void Application_Start(object sender, EventArgs e)
{
  RouteTable.Routes.Ignore("{resource}.axd");
}

Or:

  
<system.web>
  <pages controlRenderingCompatibilityVersion="3.5"/>
</system.web>

Tags:

ASP.Net | C# | JavaScript

HttpRuntime MaxRequestLength vs. Security RequestFiltering RequestLimits MaxAllowedContentLength

by 24. May 2012 15:11

Below is illustration of setting the maximum file upload size to 150MB in IIS7.  It requires two separate settings, one specifically for ASP.Net and the other specifically for IIS (as outlined below).

This section applies to the maximum allowed file size that ASP.Net will allow.  The value is specified in KBytes.

<system.web>
  <httpRuntime maxRequestLength="153600" executionTimeout="900" />
</system.web>

This section applies to the maximum length of content that IIS will allow.  The value is specified in Bytes.

<system.webServer>
   <security>
     <requestFiltering>
       <requestLimits maxAllowedContentLength="157286400" />
    </requestFiltering>
  </security>
</system.webServer>

Tags:

ASP.Net

ASP.Net Compile File System Website

by 6. January 2012 16:10

Below is an example of command program to compile an ASP.Net file system web using .Net framework v4 (the rem comment lines show the value for compiling as v2 as well).

rem v2.0.50727
rem v4.0.30319
set frmwk=C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319

set src=C:\vss\MyWebSiteFolder
set dest=C:\vss\MyCompiledWebSiteFolder

del /F /Q %dest%\*.*

%frmwk%\aspnet_compiler -v /VwdSite -p %src% %dest% -c

Tags:

ASP.Net

ASP.Net Compile File System Website

by 6. January 2012 15:53

Below is an example of command program to compile an ASP.Net file system web using .Net framework v4 (the rem comment lines show the value for compiling as v2 as well).

rem v2.0.50727
rem v4.0.30319
set frmwk=C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319

set src=C:\vss\MyWebSiteFolder
set dest=C:\vss\MyCompiledWebSiteFolder

del /F /Q %dest%\*.*

%frmwk%\aspnet_compiler -v /VwdSite -p %src% %dest% -c

Tags:

ASP.Net

ASP.Net .ASMX WebService Invocation Test Page Remote Access

by 18. October 2011 07:32

The ASP.Net .ASMX webservice page comes with an automatic invocation test page for each of the available operations defined in the service.  By default the invocation is limited to the service running on a local machine (e.g. if you are running the service on your local machine and browse to it you can invoke it;).  However, if the service is running on a server and you browse from your local machine you will probably see this message "The test form is only available for requests from the local machine" in the spot where the invocation button would normally be.  To enable remote invocation, make sure to add the web config section shown below.

<configuration>
  <system.web>
    <webServices>
      <protocols>
        <add name="HttpGet"/>
        <add name="HttpPost"/>
      </protocols>
    </webServices>
  </system.web>
</configuration>

Tags:

ASP.Net | Web Service