📜 ⬆️ ⬇️

ASMX web services development techniques

In this article I will talk about the various techniques for developing SOAP web services using ASMX technology, as well as about this technology in general. In addition to SOAP, the implementation of AJAX will also be considered. The article will be useful both to those who are already familiar with it, and to those who are just about to create their first web service.



Content


History reference
ASMX and WCF
Introduction
  1. Simplest design
  2. Recommended design
  3. Proxy class using wsdl.exe
  4. Server class for this wsdl
  5. ajax
  6. Request Metadata
  7. Accessing files
  8. web.config
  9. Multiple asmx files
  10. Replacing a webpage
  11. Expansion replacement
  12. Hiding wsdl
  13. Exceptions
  14. soap: Header
  15. Caching
  16. SoapExtension
  17. Debagging x64 in Visual Studio
  18. Depla (publication)
  19. IIS application pools
  20. Development tools


History reference


From the very beginning, Microsoft was one of the main developers of the SOAP standard. In 2002, as part of the very first version of ASP.NET 1.0, it introduced ASMX (Active Server Method Extended) technology, which allowed developers to easily create and consume SOAP web services in the latest Visual Studio 2002. Note that this technology is officially on MSDN called “XML Web Services”. In those years, SOAP only took the first serious steps in the world of web development. The W3C consortium approved SOAP 1.1 in 2000, SOAP 1.2 in 2003 (updated 2007). Therefore, it was very important to make an easy-to-learn technology for the new standard. And this goal was achieved - to work with web services, the developer did not even need to know XML, SOAP and WSDL.

In subsequent years, ASMX technology has received very wide distribution and recognition. Also from the very beginning, Microsoft delivered to it an addon of Web Services Enhancements (WSE), which allowed it to implement various WS- * security specifications such as WS-Security, WS-Policy, WS-ReliableMessaging. The latest version, WSE 3.0, was released in 2005. And in 2007, as part of .NET 3.0, the Windows Communication Foundation (WCF) technology was introduced, which became the official replacement for ASMX. Despite the fact that ASMX technology has not been developed for a long time, it continues to be widely used and supported by the latest versions of the .NET Framework.

ASMX and WCF


It is interesting to compare how many web services of both types Google sees: 314,000 ASMX and 6,280 WCF
Why is ASMX technology still so popular? It's very simple: it is easy to use and perfectly solves the problem in most cases. The advantage of WCF is manifested, for example, in cases where you need high speed transport, duplex, streaming, compliance with modern security standards, REST. By the way, if you only need REST, then instead of WCF you should use ASP.NET Web API technology.
')
We specifically list the advantages of each technology:
Pros ASMX:
  • Ease of development
  • Ease of learning
  • No configuration hell

WCF Pros:
  • Very diverse and flexible transportation options.
  • Current and emerging technology
  • Different hosting options
  • Ability to implement a large variety of standards WS- *

So, WCF is a “Swiss knife” in the field of data transport, and ASMX is a “good-quality screwdriver”. And best of all, of course, be able to use both tools. Since the WCF development techniques on the Internet are described more fully and relevantly, I decided to write an article about ASMX, which is useful for those who have to support old web services and those who continue to use this technology to create new ones.



Introduction


The article describes 20 different practical techniques that can be applied when developing web services using this technology. The scenario for the examples will be as follows. There is a regularly updated database of financial reports. It is necessary to develop a universal mechanism by which various clients will always have relevant data on these reports. Solution: write a SOAP web service with two methods:

  • The first method takes a period in time and returns the identifiers of all reports that appeared in this period.
  • The second method takes the report identifier and returns the data for the report itself.

Consumers of a web service regularly send requests to the first method, indicating the period since their last request, and if there are identifiers in the response, request data through the second method.

Examples are demonstrated based on the code from the “Recommended Construction”, and in order to test them, it is enough to call the GetReportInfo web method as shown in the Proxy class example.

1. The simplest design


Let's start with a description of the simplest web service design. Attention, the example is purely theoretical! Even though he is a worker, never do this in practice. This is just a demonstration of the simplicity of the ASMX technology itself.

Create a new project “ASP.NET Empty Web Application” or “ASP.NET Web Service Application” with the name FinReportWebService in Visual Studio. Add two files to it: FinReport.asmx and FinReportService.cs , and add FinReport.asmx as a Text File, not a Web Service, so that it is a single file.

FinReport.asmx
<% @ Class = "FinReportWebService.FinReportService" %>

FinReportService.cs
using System;
using System.Web.Services;

namespace FinReportWebService {

public class FinReportService {
[ WebMethod ]
public int [] GetReportIdArray ( DateTime dateBegin, DateTime dateEnd) {
int [] array = new int [] {357, 358, 360, 361};
return array;
}

[ WebMethod ]
public FinReport GetReport ( int reportID) {
FinReport finReport = new FinReport () {
ReportID = reportID,
Date = new DateTime (2015, 03, 15),
Info = "Some info"
};

return finReport;
}
}

public class FinReport {
public int ReportID { get ; set ; }
public DateTime Date { get ; set ; }
public string Info { get ; set ; }
}
}

Press F5 to launch the web server and open FinReport.asmx in your browser, you should see



Is done. Now we analyze in order. A web service is represented by one regular class with only one mandatory feature — some of its methods are marked with a special attribute [WebMethod] . Such class methods become web-based web service methods with the appropriate call signature. This class must have a default constructor. With each new IIS request, it instantiates it with the default constructor and calls the appropriate method.

The second mandatory part of the minimal construction is a file with the asmx extension, inside which you must specify this class.

It is interesting to compare this manually created asmx file with the one that Visual Studio will create. Suppose we want to make another web service that returns a currency exchange rate. Add the ExchangeRate.asmx file with the Web Service type through the Add New Item menu.



By pressing F7 once or twice, you can see the following:
<% @ WebService Language = "C #" CodeBehind = "ExchangeRate.asmx.cs"    Class = "FinReportWebService.ExchangeRate" %>

The operator Language = "C #" is rudimentary, and is needed only if you will write the source code directly inside the asmx file. Such code will be compiled dynamically. But I believe that, in general, dynamic compilation of a web service is not a very good practice, and in particular, I do not recommend using the special folder App_Code. And the operator CodeBehind = "ExchangeRate.asmx.cs" simply links the two files at the Visual Studio level.

2. Recommended design


In this example, the same web service is implemented in a more correct way. Although this is a more correct code, it also serves only for demonstration. For example, such important standard things as authorization, exception handling, logging are omitted here. Also this example will be the basis on which other techniques of this article will be demonstrated. In the file FinReportService.cs, replace the content with the following source code:

FinReportService.cs
using System;
using System.Web.Services;
using System.Xml.Serialization;

namespace FinReportWebService {

[ WebServiceBinding (ConformsTo = WsiProfiles .BasicProfile1_1)]
[ WebService (Description = " Fin . Reports " , Namespace = XmlNS)]
public class FinReportService : WebService {
public const string XmlNS = "http://asmx.habrahabr.ru/" ;

[ WebMethod (Description = "Get a list of report IDs by period" )]
public GetReportIdArrayResult GetReportIdArray ( GetReportIdArrayArg arg) {
return new GetReportIdArrayResult () {
ReportIdArray = new int [] {357, 358, 360, 361}
};
}

[ WebMethod (Description = " Getting   report   by id " )]
public GetReportResult GetReport ( GetReportArg arg) {
return new GetReportResult () {
Report = new FinReport {
ReportID = arg.ReportID,
Date = new DateTime (2015, 03, 15),
Info = getReportInfo (arg.ReportID)
}
};
}

private string getReportInfo ( int reportID) {
return "ReportID =" + reportID;
}
}


// [Serializable]
// [XmlType (Namespace = FinReportService.XmlNS)]
public class FinReport {
public int ReportID { get ; set ; }
public DateTime Date { get ; set ; }
public string Info { get ; set ; }
}

public class GetReportIdArrayArg {
public DateTime DateBegin { get ; set ; }
public DateTime DateEnd { get ; set ; }
}

public class GetReportIdArrayResult {
public int [] ReportIdArray { get ; set ; }
}

public class GetReportArg {
public int ReportID { get ; set ; }
}

public class GetReportResult {
public FinReport Report { get ; set ; }
}
}

We will analyze the changes.
The attribute [WebServiceBinding (ConformsTo = WsiProfiles.BasicProfile1_1)] means that the web service is checked for compliance with the WSI Basic Profile 1.1 specification. For example, according to it, overloading the operation name is prohibited, or using the attribute [SoapRpcMethod]. Such violations will result in a web service error “Service„ FinReportWebService.FinReportService “does not meet the Simple SOAP Binding Profile Version 1.0 specification.”. In the absence of this attribute, violations will only result in the warning “This web service does not meet the requirements of WS-I Basic Profile v1.1.”. In general, it is recommended to add this attribute, which provides greater interoperability.

The attribute [WebService (Description = “Fin. Reports”, Namespace = XmlNS)] has only three properties:
Namespace - default CML namespace - be sure to specify
Description - the web service description displayed in the browser
Name - web service name (the class name is taken by default)

Inheriting from the WebService class gives access to HttpContext, HttpSessionState, and several others, which can be useful in some cases.

In the attribute [WebMethod (Description = “Receive report by ID”)] usually indicate only Description, which describes the web method in the browser, other properties are rarely used.

I personally recommend encapsulating incoming parameters and return values ​​in special classes. For example, I call them by adding the suffixes -Arg and -Result to the name of the method, which means the argument and the result. In this example, for simplicity, they are all in the same FinReportService.cs file, but in real projects I place each of them in a separate file in a special folder of the type FinReportServiceTypes. It is also convenient to inherit from common classes.

In theory, all of your own classes in the web methods need to specify the attributes [Serializable] and [XmlType (Namespace = FinReportService.XmlNS)]. However, in this case it is not necessary. After all, if only XML serialization is performed, the [Serializable] attribute is not needed, and the XML namespace is taken from the [WebService] attribute by default. I note that, unlike WCF, ASMX uses the usual XmlSerializer, which allows you to widely manage serialization using standard attributes such as [XmlType], [XmlElement], [XmlIgnore], etc.

3. Proxy class using wsdl.exe


The wsdl.exe utility is an asmx-compliant technique for using SOAP web services. According to the wsdl file or link, it generates a proxy class - a special class that makes it as easy as possible to access this web service. Of course, it does not matter on which technology the web service itself is implemented, it can be anything - ASMX, WCF, JAX-WS or NuSOAP. By the way, WCF has a similar utility called SvcUtil.exe.

The utility is located in the folder C: \ Program Files (x86) \ Microsoft SDKs \ Windows, moreover, it is presented there in different versions, depending on the .net version, bit depth, windows version and visual studio.



Examples of using
wsdl http://192.168.1.101:8080/SomeDir/SomeService?wsdl
wsdl HabraService.wsdl

Let's make a client for FinReportWebService. In the current or new solution, create a new Windows Forms project FinReportWebServiceClient . Add the ProxyClass folder in it, copy the wsdl.exe utility into it and create the GenProxyClass.bat batch file in it:
wsdl /n:FinReportWebServiceClient.ProxyClass http: // localhost: 3500 / FinReport.asmx? wsdl
pause

Using the argument /n:FinReportWebServiceClient.ProxyClass we specify the namespace for the class. Running it, you should get the file FinReportService.cs. Via Solution Explorer - Show All Files, include all three files in the solution.



Add a button to the form, and the following three methods to the form source code:
public static FinReportService GetFinReportService () {
var service = new FinReportService ();
service.Url = "http: // localhost: 3500 / FinReport.asmx" ;
service.Timeout = 100 * 1000;
return service;
}


private void webMethodTest_GetReportIdArray () {
var service = GetFinReportService ();
var arg = new GetReportIdArrayArg ();
arg.DateBegin = new DateTime (2015, 03, 01);
arg.DateEnd = new DateTime (2015, 03, 02);

var result = service.GetReportIdArray (arg);
MessageBox .Show ( "result.ReportIdArray.Length =" + result.ReportIdArray.Length);
}


private void webMethodTest_GetReport () {
var service = GetFinReportService ();
var arg = new GetReportArg ();
arg.ReportID = 45;

var result = service.GetReport (arg);
MessageBox .Show (result.Report.Info);
}

The most important properties of the proxy class are Url and Timeout, and the timeout is specified in milliseconds and 100 seconds is its default value. Now with the help of them you can test the work of the web service. A demonstration of further techniques will be shown by calling the GetReport method and filling in the result.Report.Info field.

In the case of creating a proxy class for a wsdl file that refers to external xsd schemes, all these schemes must be listed in the command:
wsdl / n: MyNamespace HabraService.wsdl Data.xsd Common.xsd Schema.xsd

However, in addition to manually creating a proxy class, Visual Studio allows you to create it automatically. The item “Add Service Reference” allows you to create a proxy class using WCF technology, and there is an “Add Web Reference” button in Advanced that also creates it using ASMX technology.

4. Server class for this wsdl


As you know, wsdl description of a web service in ASMX technology is automatically generated. However, sometimes there is an inverse problem: for a given wsdl file, develop the corresponding web service. It is solved using the same wsdl.exe utility. It can create the necessary skeleton from the classes and all you have to do is implement the program logic of the web methods.

For example, take the wsdl of our web service. Save it from the browser as a FinReport.wsdl file or copy from here:
FinReport.wsdl
<? xml   version = " 1.0 "   encoding = " utf-8 " ?>
< wsdl: definitions   xmlns: tm = " http://microsoft.com/wsdl/mime/textMatching/ "   xmlns: soapenc = " http://schemas.xmlsoap.org/soap/encoding/ "   xmlns: mime = " http://schemas.xmlsoap.org/wsdl/mime/ "   xmlns: tns = " http://asmx.habrahabr.ru/ "   xmlns: soap = " http://schemas.xmlsoap.org/wsdl/soap/ "   xmlns: s = " http://www.w3.org/2001/XMLSchema "   xmlns: soap12 = " http://schemas.xmlsoap.org/wsdl/soap12/ "   xmlns: http = " http://schemas.xmlsoap.org/wsdl/http/ "   targetNamespace = " http://asmx.habrahabr.ru/ "   xmlns: wsdl = " http://schemas.xmlsoap.org/wsdl/ " >
< wsdl: documentation   xmlns: wsdl = " http://schemas.xmlsoap.org/wsdl/ " > Fin. reports </ wsdl: documentation >
< wsdl: types >
< s: schema   elementFormDefault = " qualified "   targetNamespace = " http://asmx.habrahabr.ru/ " >
< s: element   name = " GetReportIdArray " >
< s: complexType >
< s: sequence >
< s: element   minOccurs = " 0 "   maxOccurs = " 1 "   name = " arg "   type = " tns: GetReportIdArrayArg " />
</ s: sequence >
</ s: complexType >
</ s: element >
< s: complexType   name = " GetReportIdArrayArg " >
< s: sequence >
< s: element   minOccurs = " 1 "   maxOccurs = " 1 "   name = " DateBegin "   type = " s: dateTime " />
< s: element   minOccurs = " 1 "   maxOccurs = " 1 "   name = " DateEnd "   type = " s: dateTime " />
</ s: sequence >
</ s: complexType >
< s: element   name = " GetReportIdArrayResponse " >
< s: complexType >
< s: sequence >
< s: element   minOccurs = " 0 "   maxOccurs = " 1 "   name = " GetReportIdArrayResult "   type = " tns: GetReportIdArrayResult " />
</ s: sequence >
</ s: complexType >
</ s: element >
< s: complexType   name = " GetReportIdArrayResult " >
< s: sequence >
< s: element   minOccurs = " 0 "   maxOccurs = " 1 "   name = " ReportIdArray "   type = " tns: ArrayOfInt " />
</ s: sequence >
</ s: complexType >
< s: complexType   name = " ArrayOfInt " >
< s: sequence >
< s: element   minOccurs = " 0 "   maxOccurs = " unbounded "   name = " int "   type = " s: int " />
</ s: sequence >
</ s: complexType >
< s: element   name = " GetReport " >
< s: complexType >
< s: sequence >
< s: element   minOccurs = " 0 "   maxOccurs = " 1 "   name = " arg "   type = " tns: GetReportArg " />
</ s: sequence >
</ s: complexType >
</ s: element >
< s: complexType   name = " GetReportArg " >
< s: sequence >
< s: element   minOccurs = " 1 "   maxOccurs = " 1 "   name = " ReportID "   type = " s: int " />
</ s: sequence >
</ s: complexType >
< s: element   name = " GetReportResponse " >
< s: complexType >
< s: sequence >
< s: element   minOccurs = " 0 "   maxOccurs = " 1 "   name = " GetReportResult "   type = " tns: GetReportResult " />
</ s: sequence >
</ s: complexType >
</ s: element >
< s: complexType   name = " GetReportResult " >
< s: sequence >
< s: element   minOccurs = " 0 "   maxOccurs = " 1 "   name = " Report "   type = " tns: FinReport " />
</ s: sequence >
</ s: complexType >
< s: complexType   name = " FinReport " >
< s: sequence >
< s: element   minOccurs = " 1 "   maxOccurs = " 1 "   name = " ReportID "   type = " s: int " />
< s: element   minOccurs = " 1 "   maxOccurs = " 1 "   name = " Date "   type = " s: dateTime " />
< s: element   minOccurs = " 0 "   maxOccurs = " 1 "   name = " Info "   type = " s: string " />
</ s: sequence >
</ s: complexType >
</ s: schema >
</ wsdl: types >
< wsdl: message   name = " GetReportIdArraySoapIn " >
< wsdl: part   name = " parameters "   element = " tns: GetReportIdArray " />
</ wsdl: message >
< wsdl: message   name = " GetReportIdArraySoapOut " >
< wsdl: part   name = " parameters "   element = " tns: GetReportIdArrayResponse " />
</ wsdl: message >
< wsdl: message   name = " GetReportSoapIn " >
< wsdl: part   name = " parameters "   element = " tns: GetReport " />
</ wsdl: message >
< wsdl: message   name = " GetReportSoapOut " >
< wsdl: part   name = " parameters "   element = " tns: GetReportResponse " />
</ wsdl: message >
< wsdl: portType   name = " FinReportServiceSoap " >
< wsdl: operation   name = " GetReportIdArray " >
< wsdl: documentation   xmlns: wsdl = " http://schemas.xmlsoap.org/wsdl/ " > Getting a list of report IDs by period </ wsdl: documentation >
< wsdl: input   message = " tns: GetReportIdArraySoapIn " />
< wsdl: output   message = " tns: GetReportIdArraySoapOut " />
</ wsdl: operation >
< wsdl: operation   name = " GetReport " >
< wsdl: documentation   xmlns: wsdl = " http://schemas.xmlsoap.org/wsdl/ " > Getting the report by ID </ wsdl: documentation >
< wsdl: input   message = " tns: GetReportSoapIn " />
< wsdl: output   message = " tns: GetReportSoapOut " />
</ wsdl: operation >
</ wsdl: portType >
< wsdl: binding   name = " FinReportServiceSoap "   type = " tns: FinReportServiceSoap " >
< soap: binding   transport = " http://schemas.xmlsoap.org/soap/http " />
< wsdl: operation   name = " GetReportIdArray " >
< soap: operation   soapAction = " http://asmx.habrahabr.ru/GetReportIdArray "   style = " document " />
< wsdl: input >
< soap: body   use = " literal " />
</ wsdl: input >
< wsdl: output >
< soap: body   use = " literal " />
</ wsdl: output >
</ wsdl: operation >
< wsdl: operation   name = " GetReport " >
< soap: operation   soapAction = " http://asmx.habrahabr.ru/GetReport "   style = " document " />
< wsdl: input >
< soap: body   use = " literal " />
</ wsdl: input >
< wsdl: output >
< soap: body   use = " literal " />
</ wsdl: output >
</ wsdl: operation >
</ wsdl: binding >
< wsdl: binding   name = " FinReportServiceSoap12 "   type = " tns: FinReportServiceSoap " >
< soap12: binding   transport = " http://schemas.xmlsoap.org/soap/http " />
< wsdl: operation   name = " GetReportIdArray " >
< soap12: operation   soapAction = " http://asmx.habrahabr.ru/GetReportIdArray "   style = " document " />
< wsdl: input >
< soap12: body   use = " literal " />
</ wsdl: input >
< wsdl: output >
< soap12: body   use = " literal " />
</ wsdl: output >
</ wsdl: operation >
< wsdl: operation   name = " GetReport " >
< soap12: operation   soapAction = " http://asmx.habrahabr.ru/GetReport "   style = " document " />
< wsdl: input >
< soap12: body   use = " literal " />
</ wsdl: input >
< wsdl: output >
< soap12: body   use = " literal " />
</ wsdl: output >
</ wsdl: operation >
</ wsdl: binding >
< wsdl: service   name = " FinReportService " >
< wsdl: documentation   xmlns: wsdl = " http://schemas.xmlsoap.org/wsdl/ " > Fin. reports </ wsdl: documentation >
< wsdl: port   name = " FinReportServiceSoap "   binding = " tns: FinReportServiceSoap " >
< soap: address   location = " http: // localhost: 3500 / FinReport.asmx " />
</ wsdl: port >
< wsdl: port   name = " FinReportServiceSoap12 "   binding = " tns: FinReportServiceSoap12 " >
< soap12: address   location = " http: // localhost: 3500 / FinReport.asmx " />
</ wsdl: port >
</ wsdl: service >
</ wsdl: definitions >

Create a new empty web-project in the solution with the name FinReportWebServiceByWsdl . In it, add the ServerClass folder to which you copy the files FinReport.wsdl and wsdl.exe. Create a GenServerClass.bat batch file in it:
wsdl / server /n:FinReportWebServiceByWsdl.ServerClass FinReport.wsdl
pause

Running it, you should get the file FinReportService.cs. Include all four files in the solution.



So, as you can see, the only difference from the proxy class generation is the server attribute. This creates an abstract class inherited from the WebService with abstractly described web methods. You can inherit from it, but you still need to copy all the attributes, so I suggest doing this as follows. Copy the class definition to the new file and namespace, remove the word abstract and write the implementation of the methods. After formatting the code, I got the following file
using System;
using System.Web.Services;
using System.Web.Services.Description;
using System.Web.Services.Protocols;
using FinReportWebServiceByWsdl.ServerClass;

namespace FinReportWebServiceByWsdl {

[ WebService (Namespace = "http://asmx.habrahabr.ru/" )]
[ WebServiceBinding (Name = "FinReportServiceSoap" , Namespace = "http://asmx.habrahabr.ru/" )]
public class FinReportService : WebService {

[ WebMethod ]
[ SoapDocumentMethod ( "http://asmx.habrahabr.ru/GetReportIdArray" ,
RequestNamespace = "http://asmx.habrahabr.ru/" ,
ResponseNamespace = "http://asmx.habrahabr.ru/" ,
Use = SoapBindingUse .Literal,
ParameterStyle = SoapParameterStyle .Wrapped)]

public GetReportIdArrayResult GetReportIdArray ( GetReportIdArrayArg arg) {
return new GetReportIdArrayResult ();
}


[ WebMethod ]
[ SoapDocumentMethod ( "http://asmx.habrahabr.ru/GetReport" ,
RequestNamespace = "http://asmx.habrahabr.ru/" ,
ResponseNamespace = "http://asmx.habrahabr.ru/" ,
Use = SoapBindingUse .Literal,
ParameterStyle = SoapParameterStyle .Wrapped)]

public GetReportResult GetReport ( GetReportArg arg) {
return new GetReportResult () {
Report = new FinReport {
ReportID = arg.ReportID,
Date = new DateTime (2015, 03, 15),
Info = "ByWSDL"
}
};
}
}
}

In this code, the utility explicitly described using the attributes of those web service parameters that were implicitly determined by default. It remains only to add the file FinReportByWsdl.asmx , which will point to this new class:
<% @ Class = "FinReportWebServiceByWsdl.FinReportService" %>


5. ajax


ASMX web service can receive and return data in JSON format, which allows to implement ajax technique. For the example to work, your web project must have the following three files:

FinReport.asmx - the same as in the first examples, just 1 line
<% @ Class = "FinReportWebService.FinReportService" %>

FinReportService.cs - the code changes to the next
using System;
using System.Text;
using System.Web.Script.Serialization;
using System.Web.Script.Services;
using System.Web.Services;
using Newtonsoft.Json;

namespace FinReportWebService {
[ ScriptService ]
[ WebServiceBinding (ConformsTo = WsiProfiles .BasicProfile1_1)]
[ WebService (Description = " Fin . Reports " , Namespace = "http://asmx.habrahabr.ru/" )]
public class FinReportService : WebService {

[ ScriptMethod (ResponseFormat = ResponseFormat .Json)]
[ WebMethod ]
public GetReportResult Method_1_POST_Objects ( GetReportArg arg) {
return getFinReportResult (arg.ReportID, "Method_1_POST_Objects" );
}


[ ScriptMethod (ResponseFormat = ResponseFormat .Json, UseHttpGet = true )]
[ WebMethod ]
public string Method_2_GET ( int id) {
var result = getFinReportResult (id, "Method_2_GET" );
string text = JsonConvert .SerializeObject (result);
return text;
}


[ ScriptMethod (ResponseFormat = ResponseFormat .Json)]
[ WebMethod ]
public string Method_3_POST ( int id) {
var result = getFinReportResult (id, "Method_3_POST" );
JavaScriptSerializer js = new JavaScriptSerializer ();
return js.Serialize (result);
}


[ ScriptMethod (ResponseFormat = ResponseFormat .Json)]
[ WebMethod ]
public string Method_4_POST_ComplexArg ( string json) {
var arg = JsonConvert .DeserializeObject < GetReportArg > (json);
var result = getFinReportResult (arg.ReportID, arg.Token + " Received ." );
return JsonConvert .SerializeObject (result);
}


[ ScriptMethod (ResponseFormat = ResponseFormat .Json)]
[ WebMethod ]
public DateTime Method_5_TransformDate ( DateTime dateTime) {
Return dateTime.AddYears (-3) .AddDays (-5) .AddHours (-2) .AddMinutes (6);
}


[ ScriptMethod (ResponseFormat = ResponseFormat .Json)]
[ WebMethod ]
public void Method_6_POST_NonStandard ( int id) {
var result = getFinReportResult (id, "Method_6_POST_NonStandard, My   text " );
string text = JsonConvert .SerializeObject (result);
byte [] data = Encoding .UTF8.GetBytes (text);

Context.Response.Clear ();
Context.Response.ContentType = "application / json; charset = utf-8" ;
Context.Response.AddHeader ( "content-length" , data.Length.ToString ());
Context.Response.BinaryWrite (data);
Context.Response.Flush ();
}


private GetReportResult getFinReportResult ( int id, string info) {
return new GetReportResult () {
Report = new FinReport () {
ReportID = id,
Info = info,
Date = new DateTime (2015, 03, 15),
}
};
}
}


public class FinReport {
public DateTime Date { get ; set ; }
public string Info { get ; set ; }
public int ReportID { get ; set ; }
}

public class GetReportArg {
public int ReportID { get ; set ; }
public string Token { get ; set ; }
}

public class GetReportResult {
public FinReport Report { get ; set ; }
}
}

Page.htm - the actual webpage
<! doctype html >
< html >
< head >

< meta charset = utf-8>
< script src = "// ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></ script >

< script >
$ (document) .ready ( function () {
$ ( "# btn1" ) .click ( function () {
var arg = {arg: {ReportID: 1}};

$ .ajax ({
type: "POST" ,
contentType: "application / json; charset = utf-8" ,
url: "/FinReport.asmx/Method_1_POST_Objects" ,
data: JSON.stringify (arg),
dataType: "json" ,
success: function (data, status) {
$ ( "# div1" ) .html (data.d.Report.Info);
},
error: function (request, status, error) {alert ( "Error" ); }
});
});

$ ( "# btn2" ) .click ( function () {
$ .ajax ({
type: "GET" ,
contentType: "application / json; charset = utf-8" ,
url: "/FinReport.asmx/Method_2_GET" ,
data: {id: 2},
dataType: "json" ,
success: function (data, status) {
$ ( "# div2" ) .html (JSON.parse (data.d) .Report.Info);
},
error: function (request, status, error) {alert ( "Error" ); }
});
});

$ ( "# btn3" ) .click ( function () {
$ .ajax ({
type: "POST" ,
contentType: "application / json; charset = utf-8" ,
url: "/FinReport.asmx/Method_3_POST" ,
data: '{"id": 3}' ,
dataType: "json" ,
success: function (data, status) {
$ ( "# div3" ) .html (JSON.parse (data.d) .Report.Info);
},
error: function (request, status, error) {alert ( "Error" ); }
});
});


$ ( "# btn4" ) .click ( function () {
var arg = {ReportID: 4, Token: " Token   method 4. " };
var argObj = {json: JSON.stringify (arg)};

$ .ajax ({
type: "POST" ,
contentType: "application / json; charset = utf-8" ,
url: "/FinReport.asmx/Method_4_POST_ComplexArg" ,
data: JSON.stringify (argObj),
dataType: "json" ,
success: function (data, status) {
$ ( "# div4" ) .html (JSON.parse (data.d) .Report.Info);
},
error: function (request, status, error) {alert ( "Error" ); }
});
});

$ ( "# btn5" ) .click ( function () {
var now = new Date ();
var arg = {dateTime: now};

$ .ajax ({
type: "POST" ,
contentType: "application / json; charset = utf-8" ,
url: "/FinReport.asmx/Method_5_TransformDate" ,
data: JSON.stringify (arg),
dataType: "json" ,
success: function (data, status) {
var date = new Date (parseInt (data.d.replace ( "/ Date (" , "" ) .replace ( ") /" , "" ), 10));
$ ( "# div5" ) .html (date.toString ());
},
error: function (request, status, error) {alert ( "Error" ); }
});
});

$ ( "# btn6" ) .click ( function () {
$ .ajax ({
type: "POST" ,
contentType: "application / json; charset = utf-8" ,
url: "/FinReport.asmx/Method_6_POST_NonStandard" ,
data: '{"id": 6}' ,
dataType: "json" ,
success: function (data, status) {
$ ( "# div6" ) .html (data.Report.Info);
},
error: function (request, status, error) {alert ( "Error" ); }
});
});


});
</ script >

</ head >
< body >

< div id = "div1"> Div 1 </ div >
< div id = "div2"> Div 2 </ div >
< div id = "div3"> Div 3 </ div >
< div id = "div4"> Div 4 </ div >
< div id = "div5"> Div 5 </ div >
< div id = "div6"> Div 6 </ div >
<Br />
< button id = "btn1"> Method 1 </ button >
<Br />
< button id = "btn2"> Method 2 </ button >
<Br />
< button id = "btn3"> Method 3 </ button >
<Br />
< button id = "btn4"> Method 4 </ button >
<Br />
< button id = "btn5"> Method 5 </ button >
<Br />
< button id = "btn6"> Method 6 </ button >

</ body >
</ html >

The example also uses the Json.NET library aka Newtonsoft.Json.

In order for the web service to work with JSON, you need to apply 2 new attributes:
[ScriptService] – , [ScriptMethod], ResponseFormat — JSON XML, UseHttpGet – – GET POST.

- 6 , ajax.

1 . 2 GetReportArg GetReportResult. :
{
" arg ": {
"ReportID": 1
}
}

{
" d ": {
"__type": "FinReportWebService.GetReportResult" ,
"Report": {
"ReportID": 1 ,
"Date": "/Date(1426356000000)/" ,
"Info": "Method_1_POST_Objects"
       }
}
}
, . JSON- - «d». "__type": «FinReportWebService.GetReportResult». "/Date(1426356000000)/" . , 5 , , , .

2 . . GET, , json-, . data: { id: 2 }, URL http://localhost:3500/FinReport.asmx/Method_2_GET?id=2, URL.


{
" d": "{\"Report\":{\"ReportID\":2,\"Date\":\"2015-03-15T00:00:00\",\"Info\":\"Method_2_GET\"}}"
}
, JSON.parse(data.d), :
{
" Report": {
"ReportID": 2 ,
"Date": "2015-03-15T00:00:00" ,
"Info": "Method_2_GET"
    }
}
, Json.NET . contentType: «application/json; charset=utf-8», , GET. CSRF. URL . GET .

3. , POST . :
{
" id ": 3
}
:
{
" d": "{\"Report\":{\"ReportID\":3,\"Date\":\"\\/Date(1426356000000)\\/\",\"Info\":\"Method_3_POST\"}}"
}

4. , var arg = { ReportID: 4, Token: " 4." }; :
{
"json": "{\"ReportID\":4,\"Token\":\"   4\"}"
}
.

5. , . 1 «Date»: "/Date(1426356000000)/". – , 1 1970 UTC (UNIX epoch). , Date new Date(milliseconds), :
var date = new Date(parseInt(data.d.replace( "/Date(" , "" ).replace( ")/" , "" ), 10));

- :
{
" dateTime": "2015-03-25T05:49:13.604Z"
}

6. , . , .
{
" Report": {
"Date": "2015-03-15T00:00:00" ,
"Info": "Method_6_GET_NonStandard,   " ,
       "ReportID": 6
}
}
, «d». GET. , - «application/json;».

maxJsonLength

maxJsonLength web.config:
<? xml   version = " 1.0 " ?>
< configuration >
  
< system.web >
< compilation   debug = " true "   targetFramework = " 4.0 " />
</ system.web >

< system.web.extensions >
< scripting >
< webServices >
< jsonSerialization   maxJsonLength = " 1073741824 " />
</ webServices >
</ scripting >
</ system.web.extensions >

</ configuration >


6.


, , IP- URL. , . , . DNS-, . getReportInfo()
private string getReportInfo() {
var request = this .Context.Request;
// var request = HttpContext.Current.Request;

StringBuilder sb = new StringBuilder ();
sb.Append( "IP = " ).AppendLine(request.UserHostAddress);
sb.Append( "URL = " ).AppendLine(request.Url.OriginalString);
sb.Append( "Header 'Connection' = " ).AppendLine(request.Headers[ "Connection" ]);

DateTime dnsDate = DateTime .Now;
TimeSpan dnsSpan;

try {
// throw new Exception("   .");
var entry = Dns.GetHostEntry(request.UserHostAddress);
dnsSpan = DateTime .Now.Subtract(dnsDate);
sb.Append( "HostName = " ).AppendLine(entry.HostName);

} catch ( Exception ex) {
dnsSpan = DateTime .Now.Subtract(dnsDate);
sb.AppendLine(ex.Message);
}

sb.Append( "dnsSpan = " ).AppendLine(dnsSpan.ToString());
return sb.ToString();
}


7.


, -. getReportInfo :
private string getReportInfo( int reportID) {
string dirRoot = HttpContext .Current.Server.MapPath( "~" );

StringBuilder sb = new StringBuilder ();
sb.AppendLine( "dirRoot = " + dirRoot);

string fileCars = HttpContext .Current.Server.MapPath( "~/bin/MyFiles/Cars.txt" );
sb.AppendLine( "fileCars = " + fileCars);

try {
sb.AppendLine( "Line 1 = " + File .ReadAllLines(fileCars)[0]);
File .AppendAllText(fileCars, Environment .NewLine + DateTime .Now + " ReportID = " + reportID);

} catch ( Exception ex) {
sb.AppendLine(ex.Message);
}

string dirFiles = Path .Combine(( new DirectoryInfo (dirRoot)).Parent.FullName, "FinReportWebService_Files" );
sb.AppendLine( "dirFiles = " + dirFiles);

try {
Directory .CreateDirectory(dirFiles);
string newFile = Path .Combine(dirFiles, Guid .NewGuid() + ".txt" );
File .WriteAllText(newFile, "ReportID = " + reportID);
sb.AppendLine(newFile);

} catch ( Exception ex) {
sb.AppendLine(ex.Message);
}


return sb.ToString();
}
MyFiles Cars.txt Build Action: None / Copy always - :



bin MyFiles , . FinReportWebService_Files, .

, getReportInfo , :

-
Cars.txt


FinReportWebService_Files



- IIS, :
dirRoot = C:\CommonFolder\publish_FinReport
fileCars = C:\CommonFolder\publish_FinReport\bin\MyFiles\Cars.txt
Line 1 = Audi
Access to the path 'C:\CommonFolder\publish_FinReport\bin\MyFiles\Cars.txt' is denied.
dirFiles = C:\CommonFolder\FinReportWebService_Files
Access to the path 'C:\CommonFolder\FinReportWebService_Files' is denied.
. 19. IIS.

8. web.config


web.config ASP.NET . ASP.NET , asmx -. . , , ASP.NET .

web.config

web.config :
<? xml   version = " 1.0 " ?>
< configuration >

< appSettings >
< add   key = " ReportType "   value = " 8 " />
< add   key = " ReportSubject "   value = "   " />
</ appSettings >

< system.web >
< compilation   debug = " true "   targetFramework = " 4.0 " />
   </ system.web >

</ configuration >
compilation.

, . , :
using System;
using System.Collections.Generic;
using System.Configuration;

namespace FinReportWebService {
internal static class WebConfig {

public static int ReportType { get { return getStructureValue < int > ( "ReportType" ); }}
public static string ReportSubject { get { return getTextValue ( "ReportSubject" ); }}
public static string DbLogin { get { return getTextValue ( "DbLogin" , true ); }}
public static string DbPass { get { return getTextValue ( "DbPass" , true ); }}

// =============================================== ==========

private static string getTextValue ( string name, bool getDefaultOnNotFound = false ) {
string value = ConfigurationManager .AppSettings [name];

if (value == null &&! getDefaultOnNotFound) {
throw new KeyNotFoundException ( " In   the web.config file is not   found   setting "" + name + "" " );
}

return value;
}


private static T getStructureValue <T> ( string name, bool getDefaultOnNotFound = false ) where T: struct {
string textValue = getTextValue (name, getDefaultOnNotFound);

if (textValue == null ) {
return default (T);
}

try {
T value = (T) Convert .ChangeType (textValue, typeof (T));
return value;

} catch ( Exception ex) {
string message = "In the web.config file, the setting '{0}' with the value '{1}' could not be parsed as '{2}'" ;
message = string .Format (message, name, textValue, typeof (T) .Name);
throw new InvalidCastException (message, ex);
}
}

}
}

And finally, in the FinReportService.cs file, replace the getReportInfo method with this code:
private static string getReportInfo ( int reportID) {
StringBuilder sb = new StringBuilder ();
try {
sb.Append ( "ReportType =" ) .AppendLine ( WebConfig .ReportType.ToString ());
sb.Append ( "ReportSubject =" ) .AppendLine ( WebConfig .ReportSubject);
sb.Append ( "DbLogin =" ) .AppendLine ( WebConfig .DbLogin);
sb.Append ( "DbPass =" ) .AppendLine ( WebConfig .DbPass);

} catch ( Exception ex) {
sb.AppendLine (ex.Message);
}

return sb.ToString ();
}
Now you can test the read configuration.

web_alpha.config

Go ahead, a very useful feature is the ability to place some of the settings in an additional file. For example, when the test and combat environments differ in the connection string to the database. It also allows you to store the secret part of the settings separately. In this case, the duplicate settings are overridden by this file and new ones can also be declared in it.

Add the web_alpha.config file to the project:
<? xml   version = " 1.0 " ?>
< appSettings >
< add   key = " ReportSubject "   value = " Small   business " />
< add   key = " DbLogin "   value = " reader " />
< add   key = " DbPass "   value = " uYE4_wn7xc5Sp " />
</ appSettings >
And change web.config itself to:
<? xml   version = " 1.0 " ?>
< configuration >

< appSettings   file = " web_alpha.config " >
< add   key = " ReportType "   value = " 8 " />
< add   key = " ReportSubject "   value = " Medium   business " />
</ appSettings >

< system.web >
< compilation   debug = " true "   targetFramework = " 4.0 " />
</ system.web >

</ configuration >

web_beta.config

The third-party file can be located in the parent folder. Create a new web_beta.config file in the parent folder:
<? xml   version = " 1.0 " ?>
< appSettings >
< add   key = " ReportSubject "   value = " Large   business " />
< add   key = " DbLogin "   value = " admin " />
< add   key = " DbPass "   value = " guXu4awewr $ w " />
</ appSettings >
Change the web.config accordingly to:
<? xml   version = " 1.0 " ?>
< configuration >

< appSettings   file = " .. /web_beta.config " >
< add   key = " ReportType "   value = " 8 " />
< add   key = " ReportSubject "   value = " Medium   business " />
</ appSettings >

< system.web >
< compilation   debug = " true "   targetFramework = " 4.0 " />
</ system.web >

</ configuration >

web_gamma.config

There is an alternative way of placing the settings in an additional file. Change web.config to:
<? xml   version = " 1.0 " ?>
< configuration >

< appSettings   configSource = " web_gamma.config " />

< system.web >
< compilation   debug = " true "   targetFramework = " 4.0 " />
</ system.web >

</ configuration >
And create the web_gamma.config file:
<? xml   version = " 1.0 " ?>
< appSettings >
< add   key = " ReportType "   value = " 9 " />
< add   key = " ReportSubject "   value = " Noncommercial " />
< add   key = " DbLogin "   value = " writer " />
< add   key = " DbPass "   value = " hQ5zGPPSrkqqfsb " />
</ appSettings >
This approach is less flexible, as it has a number of major differences from the previous one:
  • All values ​​of the settings section are defined only in the specified third-party file. No override of web.config.
  • The web_gamma.config file must exist, in the case of web_alpha.config it is optional.
  • Its modification will lead to a restart of the pool; in the case of web_alpha.config, it will not.
  • Apply to other sections, not just <appSettings>


web.Debug.config

Now we will describe another useful web.config technique that appeared in Visual Studio 2010. We are talking about the web.Debug.config and web.Release.config files . These files transform web.config when publishing depending on the current build type. Change the contents of these files to the following:
<? xml   version = " 1.0 " ?>
< configuration   xmlns : xdt = " http : // schemas . microsoft . com / XML - Document - Transform " >

   < appSettings >
< add   key = " ReportSubject "   value = " debag "    xdt: Transform = " Replace "   xdt: Locator = " Match (key) " />
</ appSettings >

</ configuration >
 

<? xml   version = " 1.0 " ?>
< configuration   xmlns: xdt = " http://schemas.microsoft.com/XML-Document-Transform " >

< system.web >
< compilation   xdt: Transform = " RemoveAttributes (debug) " />
   </ system.web >
  
</ configuration >

The conversion syntax is described on MSDN .

But even without knowing the syntax, it can be understood that in the case of Debug , the value of the ReportSubject setting is replaced , and in the case of Release , the debug = “true” attribute is deleted. By the way, do not forget to delete the attribute debug = “true” in production or set it to false, this improves performance and security.

In addition, you can create your own web.Habr.config transformation via the Build -> Configuration Manager menu, and the Add Config Transoforms web.config file's context menu.

MaxRequestLength

From the general settings for ASP.NET I want to highlight the limit on the maximum size of the incoming request. It is equal to the smaller of the two parameters. Moreover, maxRequestLength is specified in kilobytes, and maxAllowedContentLength in bytes. Here is an example for setting a limit of 100 megabytes, as well as 30 minutes of execution of the request.
<? xml   version = " 1.0 " ?>
< configuration >

< appSettings >
< add   key = " ReportType "   value = " 8 " />
< add   key = " ReportSubject "   value = " Medium   business " />
</ appSettings >

  
< system.web >
<! - 100 mb   and 30 minutes ( valid   only   with compilation.debug = false) ->
< httpRuntime   maxRequestLength = " 102400 "   executionTimeout = " 1800 " />

< compilation   targetFramework = " 4.0 " >
</ compilation >
</ system.web >

< system.webServer >
< security >
< requestFiltering >
<! - 100 mb ->
< requestLimits   maxAllowedContentLength = " 104857600 " />
</ requestFiltering >
</ security >
</ system.webServer >

</ configuration >


Their default values ​​are 4096 KB and 30000000 bytes, that is, 4 MB and 28.61 MB

Web.config Hierarchy

In fact, this web.config is at the very bottom of the hierarchy of config files.

The top level of configuration is the machine.config file . Its settings act globally on all web applications only if they are not redefined by lower-level config files. For .net 4 pools, it is located in the % windir% \ Microsoft.NET \ Framework \ v4.0.30319 \ Config or % windir% \ Microsoft.NET \ Framework64 \ v4.0.30319 \ Config folder , depending on the bit depth.

The next level is also global - this is the web.config file located there.

The third level is the level of the website, by default it is the folder \ inetpub \ wwwroot , and initially there is not even a web.config file.

The fourth is the web application tier, the standard tier described here.

There is also a fifth level - when web.config settings are applied to subfolders of a web application, but this is true for other types of web applications.

You can read more on MSDN

9. Multiple asmx files


In one web application there can be not one, but several asmx files. Obviously, this way you can develop several different web services that will have some common source code. However, this technique can also be used for a single web service, the example below shows why.

In the FinReportService.cs file, change the code to the following:
using System;
using System.Configuration;
using System.Web.Services;

namespace FinReportWebService {

[ WebServiceBinding (ConformsTo = WsiProfiles .BasicProfile1_1)]
[ WebService (Description = " Fin . Reports v.2" , Namespace = XmlNS)]
public class FinReportService_v2 : FinReportService {
public FinReportService_v2 (): base (2) {
}
}



[ WebServiceBinding (ConformsTo = WsiProfiles .BasicProfile1_1)]
[ WebService (Description = "CHECK: Fin . Reports " , Namespace = FinReportService .XmlNS)]
public class FinReportService_Check {
private FinReportService _service;

public FinReportService_Check () {
_service = new FinReportService ();
}

[ WebMethod (Description = " Enter   identifier   report . " )]
public GetReportResult GetReport ( int reportID) {
GetReportArg arg = new GetReportArg ();
arg.ReportID = reportID;
return _service.GetReport (arg);
}

[ WebMethod (Description = " Period : January 2015. " )]
public GetReportIdArrayResult GetReportIdArray () {
GetReportIdArrayArg arg = new GetReportIdArrayArg ();
arg.DateBegin = new DateTime (2015, 01, 01);
arg.DateEnd = new DateTime (2015, 02, 01);
return _service.GetReportIdArray (arg);
}
}



[ WebServiceBinding (ConformsTo = WsiProfiles .BasicProfile1_1)]
[ WebService (Description = " Fin . Reports " , Namespace = XmlNS)]
public class FinReportService : WebService {
public const string XmlNS = "http://asmx.habrahabr.ru/" ;
private int _version;

public FinReportService () {
_version = 1;
}

public FinReportService ( int version) {
_version = version;
}

[ WebMethod (Description = " Getting   report ID list   by   period " )]
public GetReportIdArrayResult GetReportIdArray ( GetReportIdArrayArg arg) {
return new GetReportIdArrayResult () {
ReportIdArray = new int [] {357, 358, 360, 361}
};
}

[ WebMethod (Description = " Getting   report   by id " )]
public GetReportResult GetReport ( GetReportArg arg) {
return new GetReportResult () {
Report = new FinReport {
ReportID = arg.ReportID,
Date = new DateTime (2015, 03, 15),
Info = "Phone:" + ConfigurationManager .AppSettings [ "phone" ] + "Version:" + _version
}
};
}
}


public class FinReport {
public int ReportID { get ; set ; }
public DateTime Date { get ; set ; }
public string Info { get ; set ; }
}

public class GetReportIdArrayArg {
public DateTime DateBegin { get ; set ; }
public DateTime DateEnd { get ; set ; }
}

public class GetReportIdArrayResult {
public int [] ReportIdArray { get ; set ; }
}

public class GetReportArg {
public int ReportID { get ; set ; }
}

public class GetReportResult {
public FinReport Report { get ; set ; }
}

}
Also in the project should be the following 3 asmx files:
FinReport.asmx - no change:
<% @ Class = "FinReportWebService.FinReportService" %>
FinReport_v2.asmx - new:
<% @ Class = "FinReportWebService.FinReportService_v2" %>
FinReport_CHECK.asmx - new:
<% @ Class = "FinReportWebService.FinReportService_Check" %>
And change web.config to:
<? xml   version = " 1.0 " ?>
< configuration >

< system.web >
< compilation   debug = " true "   targetFramework = " 4.0 " />
</ system.web >

< appSettings >
< add   key = " phone "   value = " nokia " />
</ appSettings >
  

< location   path = " FinReport.asmx " >
< appSettings >
< add   key = " phone "   value = " samsung " />
</ appSettings >
</ location >


< location   path = " FinReport_v2.asmx " >
< appSettings >
< add   key = " phone "   value = " htc " />
</ appSettings >
</ location >

  
< location   path = " FinReport_CHECK.asmx " >
< appSettings >
< add   key = " phone "   value = " apple " />
</ appSettings >

< system.web >
< webServices >
< protocols >
< clear />
< add   name = " Documentation " />
< add   name = " HttpPostLocalhost " />
</ protocols >
</ webServices >
</ system.web >
</ location >

</ configuration >
As you can see, two new asmx files refer to two new classes. First I will explain the purpose of the FinReportService_v2 class. Its only functional difference from the base class is that its default constructor initializes the int _version field with a value of 2, not 1. Thus, the web service has a clone that has exactly the same contract, but there are differences in processing requests. For example, this clone may be intended for testing or actually represent a new version.

The FinReportService_Check class has a completely different purpose. As you know, if you open a web service in a browser from localhost, then for web methods with primitive argument types, you can make a request and see the answer directly in the browser itself. This allows you, the admin and everyone who has access to the server, to easily verify that it is working correctly.




Now I will comment on the config file. Using the design, you can override any settings for a specific file. In this case, different asmx files will have different values ​​for the “phone” custom setting.

Using the <protocols> section, we first clear all the ways to interact with FinReport_CHECK.asmx, and then add the view and call to localhost. This makes remote access impossible.

10. Replacing a web page


By default, the asmx web page of the web service that you see in the browser is created using the DefaultWsdlHelpGenerator.aspx web form, which for x64 is located in the% windir% \ Microsoft.NET \ Framework64 \ v4.0.30319 \ Config folder, I recommend to familiarize yourself with it .

However, using web.config it is easy to specify your own aspx file. Add the FinReportPage.aspx file to the project:
<! doctype html >
< html >
< head >
< meta charset = utf-8>
< title > Welcome </ title >
</ head >
< body >
< p > Welcome! < / p >
</ body >
</ html >
And specify in web.config
<? xml   version = " 1.0 " ?>
< configuration >
< system.web >

< webServices >
< wsdlHelpGenerator   href = " FinReportPage.aspx " />
</ webServices >

< compilation   debug = " true "   targetFramework = " 4.0 " />

</ system.web >
</ configuration >


11. Replacing the extension


Using web.config, you can easily change the asmx extension to any other. Add a file with the habr extension to the project, for example, FinReportClone.habr , with the same content as FinReport.asmx. And change the config to the following:
<? xml   version = " 1.0 " ?>
< configuration >
< system.web >
< compilation   debug = " true "   targetFramework = " 4.0 " >
< buildProviders >
< remove   extension = " .habr " />
< add   extension = " .habr "   type = " System.Web.Compilation.WebServiceBuildProvider " />
</ buildProviders >
</ compilation >
</ system.web >

< system.webServer >
< handlers >
< add   name = " HabraHandler "   verb = " * "   path = " * .habr "   type = " System.Web.Services.Protocols.WebServiceHandlerFactory " />
</ handlers >
</ system.webServer >
  
</ configuration >
I will note that when launched from Visual Studio, FinReportClone.habr will not work, for this you need to make a publication in IIS. By the way, using this technique, you can replace the ASMX web service with a WCF web service while preserving the original URL.

12. Hiding wsdl


By default, a wsdl description of any SOAP web service is available by adding? Wsdl to its URL. This means that anyone who knows and sees this address can easily invoke its web methods. And if it does not have an authorization mechanism, it can be very insecure. But even if such a mechanism exists, it is generally undesirable to show the contract of your web service.

1 way. Add the following setting to web.config:
   < system.web >
< webServices >
< protocols >
< remove   name = " Documentation " />
       </ protocols >
</ webServices >
</ system . web >
This is the standard way to hide web service information. In fact, it simply prohibits GET requests. Attempting to open the address in the browser will result in an exception, while POST requests for web methods work as usual. The uncritical minus of this method is what the browser says about the error.



2 way. GET requests can be intercepted using a custom HTTP handler. Add the following class to the project:
using System.Web;
namespace FinReportWebService {
public class FinReportGetHandler : IHttpHandler {
public void ProcessRequest ( HttpContext context) {
string response =
@ "<! doctype html>
<html>
<head>
<meta charset = utf-8>
</ head>
<body>
<p> {0} </ p>
</ body>
</ html> " ;

bool wsdlRequested = (context.Request.QueryString.ToString (). ToLower () == "wsdl" );

if (wsdlRequested) {
response = string .Format (response, " Refer   to   administrator   for wsdl. " );
} else {
response = string .Format (response, " Web service   financial   reporting . " )
}

context.Response.ContentType = "text / html; charset = utf-8" ;
context.Response.Write (response);

// string filePage = HttpContext.Current.Server.MapPath ("~ / FinReportPage.htm");
// context.Response.WriteFile (filePage);
}

public bool IsReusable {
get { return false ; }
}
}
}
And change the web.config:
<? xml   version = " 1.0 " ?>
< configuration >
  
< system.web >
< compilation   debug = " true "   targetFramework = " 4.0 " >
</ compilation >
</ system.web >

< system.webServer >
< handlers >
< add   name = " FinReportGetHandler "   verb = " GET "   path = " FinReport.asmx "   type = " FinReportWebService.FinReportGetHandler " />
     </ handlers >
</ system.webServer >
  
</ configuration >

Just as in the previous technique, the handler will only work in IIS. By the way, a web page is better not to hardcod, but to read from a file.

13. Exceptions


Handling exceptions in a web service is always very important. Building a successful mechanism for identifying and returning errors will save a lot of time and nerves for developers on both sides. Below are two different ways to inform the web service client that something went wrong.

<soap: Fault>

The <soap: Fault> element is a standard way for SOAP to return an error. It consists of four sub-elements:
  • <faultcode> - error code
  • <faultstring> - human-readable error description
  • <faultactor> - source of error
  • <detail> - arbitrary XML structure for detailed data

<faultcode> and <faultstring> are mandatory, the other two are optional.
To demonstrate it, change the getReportInfo method to the following code:
private string getReportInfo ( int reportID) {
throwException_1 ();
// throwException_2 ();
// throwException_3 ();
return "ReportID =" + reportID;
}


private void throwException_1 () {
int x = 0;
x = 1 / x;
}


private void throwException_2 () {
throw new SoapException ( " Incorrect ReportID" , SoapException .ClientFaultCode, Context.Request.Url.AbsoluteUri);
}


private void throwException_3 () {
XmlDocument xmlDoc = new XmlDocument ();
XmlNode rootNode = xmlDoc. CreateNode ( XmlNodeType. Element, SoapException. DetailElementName. Name, SoapException. DetailElementName. Namespace);

XmlNode descNode = xmlDoc. CreateNode ( XmlNodeType. Element, "Description" , XmlNS);

XmlNode descTypeNode = xmlDoc. CreateNode ( XmlNodeType. Element, "Type" , XmlNS);
descTypeNode.InnerText = "DbConnection" ;
descNode.AppendChild (descTypeNode);

XmlNode descMessageNode = xmlDoc. CreateNode ( XmlNodeType. Element, "Message" , XmlNS);
descMessageNode.InnerText = " Host   not   found . " ;
descNode.AppendChild (descMessageNode);

XmlNode habraNode = xmlDoc. CreateNode ( XmlNodeType. Element, "HabraInfo" , XmlNS);
XmlAttribute habraNodeAttribute = xmlDoc.CreateAttribute ( "User" );
habraNodeAttribute.Value = "capslocky" ;
habraNode.Attributes.Append (habraNodeAttribute);

rootNode.AppendChild (descNode);
rootNode.AppendChild (habraNode);

XmlQualifiedName faultCode = new XmlQualifiedName ( "TempError" , XmlNS);
throw new SoapException ( " Temporary   error " , faultCode, Context.Request.Url.AbsoluteUri, rootNode);
}
Call the GetReport web method with throwException_1 (), which generates an unhandled divide-by-zero error. ASP.NET DevServer (or IIS) in this case will return the http code “500 Internal Server Error” instead of “200 OK” and the following content, which is formatted for convenience:
 
<? xml   version = " 1.0 "   encoding = " UTF-8 " ?>
< soap: Envelope   xmlns: soap = " http://schemas.xmlsoap.org/soap/envelope/ "   xmlns: xsd = " http://www.w3.org/2001/XMLSchema "   xmlns: xsi = " http://www.w3.org/2001/XMLSchema-instance " >
< soap: Body >
< soap: Fault >
< faultcode > soap: Server </ faultcode >
< faultstring > Server was unable to process request. --- & gt; Attempted to divide by zero. < / faultstring >
< detail />
</ soap: Fault >
</ soap: Body >
</ soap: Envelope >
Unhandled exceptions always give soap: Server. In this case, the display of the exception stack depends on the customErrors setting:
 
<? xml   version = " 1.0 " ?>
< configuration >
  
< system.web >
< compilation   debug = " true "   targetFramework = " 4.0 " />
< customErrors   mode = " On " />
</ system.web >
  
</ configuration >
The mode attribute has only three possible values:
  • On - Never show stack
  • Off - Always show
  • RemoteOnly - Only show for localhost requests

With the manual SoapException throw-in, you can completely define the <soap: Fault> structure yourself. Call the throwException_2 method. This code will lead to the following response content
<? xml   version = " 1.0 "   encoding = " UTF -8 " ?>
< soap: Envelope   xmlns: soap = " http://schemas.xmlsoap.org/soap/envelope/ "   xmlns: xsd = " http://www.w3.org/2001/XMLSchema "   xmlns: xsi = " http://www.w3.org/2001/XMLSchema-instance " >
< soap: Body >
< soap: Fault >
< faultcode > soap: Client </ faultcode >
< faultstring > Invalid ReportID </ faultstring >
< faultactor > http: //win2012/custom_folder/FinReport.asmx </ faultactor >
< detail />
</ soap: Fault >
</ soap: Body >
</ soap: Envelope >
Here we changed the error code, wrote our text-explanation, and also indicated, which usually indicate the URL of the request.
In general, there are four standard fault codes that are represented by static fields in the SoapException class:
  • Server - a problem in the web service itself
  • Client - the client sent an incorrect request.
  • MustUnderstand - do not remove the soap: Header required for processing
  • VersionMismatch - incorrect version of SOAP

The throwException_3 () method demonstrates the formation of your own <detail> and error code:
<? xml   version = " 1.0 "   encoding = " UTF-8 " ?>
< soap: Envelope   xmlns: soap = " http://schemas.xmlsoap.org/soap/envelope/ "   xmlns: xsd = " http://www.w3.org/2001/XMLSchema "   xmlns: xsi = " http://www.w3.org/2001/XMLSchema-instance " >
< soap: Body >
< soap: Fault >
< faultcode   xmlns: q0 = " http://asmx.habrahabr.ru/ " > q0: TempError </ faultcode >
< faultstring > Temporary Error </ faultstring >
< faultactor > http: //win2012/custom_folder/FinReport.asmx </ faultactor >
< detail >
< Description   xmlns = " http://asmx.habrahabr.ru/ " >
< Type > DbConnection </ Type >
< Message > Host not found. < / Message >
</ Description >
< HabraInfo   xmlns = " http://asmx.habrahabr.ru/ "   User = " capslocky " />
</ detail >
</ soap: Fault >
</ soap: Body >
</ soap : Envelope >
However, it is recommended not to allow the possibility of unhandled exceptions in the web methods at all. That is, the method should always have a global try-catch, which will catch any unhandled or manually thrown exceptions, and return them to the client in a predetermined format, which can be represented both by <soap: Fault> and in the manner described below.

enum

The idea of ​​this method is to use enums to tell the client about the successful or unsuccessful processing of his request. It is important that all enumeration values ​​are reflected in wsdl, so they are automatically present in the client proxy class.

Add the file FinReport_GetReport.cs to the project:
using System;

namespace FinReportWebService {

public class WebServiceError {
public ErrorType Type { get ; set ; }
public string Message { get ; set ; }
}


public enum ResultType {
Error,
FoundBasicData,
FoundFullData
}


public enum ErrorType {
Undefined
Dbconnection
InvalidArgument,
Forbidden
}


public class WebServiceErrorException : Exception {
public WebServiceError Error { get ; set ; }
}


public class FinReport_GetReport {
private GetReportArg _arg;
private GetReportResult _result;

public GetReportResult GetReport ( GetReportArg arg) {
_arg = arg;
initializeResultWithError ();

try {
checkArg ();
fillResult ();

} catch ( WebServiceErrorException ex) {
setResultType ( ResultType .Error);
_result. Error = ex. Error;

} catch ( Exception ex) {
setResultType ( ResultType .Error);
_result.Error.Type = ErrorType .Undefined;
_result.Error.Message = ex.GetType() + ": " + ex.Message;
}

if (_result.ResultType == ResultType .Error) {
_result.Report = null ;

} else {
_result.Error = null ;
}

return _result;
}


private void setResultType( ResultType type) {
_result.ResultType = type;
}


private void initializeResultWithError() {
_result = new GetReportResult ();
setResultType( ResultType .Error);

_result.Error = new WebServiceError ();
_result.Error.Type = ErrorType .Undefined;
_result.Error.Message = " " ;
}


private void checkArg() {
if (_arg.ReportID <= 0) {
throw new WebServiceErrorException () {
Error = new WebServiceError () {
Type = ErrorType .InvalidArgument,
Message = "     : " + _arg.ReportID
}
};
}
}


private void fillResult() {
_result.Report = new FinReport ();
_result.Report.ReportID = _arg.ReportID;
_result.Report.Date = new DateTime (2015, 03, 15);

// throw new WebServiceErrorException() {
// Error = new WebServiceError() {
// Type = ErrorType.DbConnection,
// Message = "     "
// }
// };

// throw new InvalidOperationException(" ");

_result.Report.Info = "Some info" ;
setResultType( ResultType .FoundFullData);
}

}
}
- GetReport
[ WebMethod (Description = " ID" )]
public GetReportResult GetReport( GetReportArg arg) {
return new FinReport_GetReport ().GetReport(arg);
}
GetReportResult
public class GetReportResult {
public ResultType ResultType { get ; set ; }
public WebServiceError Error { get ; set ; }
public FinReport Report { get ; set ; }
}
, ResultType , - . . . «200 OK» GetReportResult.

:
<? xml   version = " 1.0 "   encoding = " UTF-8 " ?>
< soap:Envelope   xmlns:soap = " http://schemas.xmlsoap.org/soap/envelope/ "   xmlns:xsd = " http://www.w3.org/2001/XMLSchema "   xmlns:xsi = " http://www.w3.org/2001/XMLSchema-instance " >
< soap: Body >
< GetReportResponse   xmlns = " http://asmx.habrahabr.ru/ " >
< GetReportResult >
< ResultType > FoundFullData </ ResultType >
< Report >
< ReportID > 45 </ ReportID >
< Date > 2015-03-15T00:00:00 </ Date >
< Info > Some info </ Info >
</ Report >
</ GetReportResult >
</ GetReportResponse >
</ soap:Body >
</ soap:Envelope >
 
 
<? xml   version = " 1.0 "   encoding = " UTF-8 " ?>
< soap:Envelope   xmlns:soap = " http://schemas.xmlsoap.org/soap/envelope/ "   xmlns:xsd = " http://www.w3.org/2001/XMLSchema "   xmlns:xsi = " http://www.w3.org/2001/XMLSchema-instance " >
< soap: Body >
< GetReportResponse   xmlns = " http://asmx.habrahabr.ru/ " >
< GetReportResult >
< ResultType > Error </ ResultType >
< Error >
< Type > DbConnection </ Type >
< Message > </ Message >
</ Error >
</ GetReportResult >
</ GetReportResponse >
</ soap:Body >
</ soap:Envelope >
 
 
<? xml   version = " 1.0 "   encoding = " UTF-8 " ?>
< soap:Envelope   xmlns:soap = " http://schemas.xmlsoap.org/soap/envelope/ "   xmlns:xsd = " http://www.w3.org/2001/XMLSchema "   xmlns:xsi = " http://www.w3.org/2001/XMLSchema-instance " >
< soap: Body >
< GetReportResponse   xmlns = " http://asmx.habrahabr.ru/ " >
< GetReportResult >
< ResultType > Error </ ResultType >
< Error >
< Type > Undefined </ Type >
< Message > System.InvalidOperationException: </ Message >
</ Error >
</ GetReportResult >
</ GetReportResponse >
</ soap:Body >
</ soap:Envelope >


<? xml   version = " 1.0 "   encoding = " UTF-8 " ?>
< soap:Envelope   xmlns:soap = " http://schemas.xmlsoap.org/soap/envelope/ "   xmlns:xsd = " http://www.w3.org/2001/XMLSchema "   xmlns:xsi = " http://www.w3.org/2001/XMLSchema-instance " >
< soap: Body >
< GetReportResponse   xmlns = " http://asmx.habrahabr.ru/ " >
< GetReportResult >
< ResultType > Error </ ResultType >
< Error >
< Type > InvalidArgument </ Type >
< Message > : -245 </ Message >
</ Error >
</ GetReportResult >
</ GetReportResponse >
</ soap:Body >
</ soap:Envelope >
, http- -, - , soap:Fault .

14. soap:Header


SOAP . , , , - .

- :
using System;
using System.Text;
using System.Web.Services;
using System.Web.Services.Protocols;

namespace FinReportWebService{

[ WebServiceBinding (ConformsTo = WsiProfiles .BasicProfile1_1)]
[ WebService (Description = " . " , Namespace = XmlNS)]
public class FinReportService : WebService {
public const string XmlNS = "http://asmx.habrahabr.ru/" ;

public HabraSoapHeader HabraHeader { get ; set ; }
public ResultTimeSoapHeader ResultTimeHeader { get ; set ; }
public SoapUnknownHeader [] UnknownHeaders { get ; set ; }


[ SoapHeader ( "HabraHeader" )] //    
[ SoapHeader ( "ResultTimeHeader" , Direction = SoapHeaderDirection .Out)]
[ SoapHeader ( "UnknownHeaders" )]
[ WebMethod (Description = "     ID" )]
public GetReportResult GetReport( GetReportArg arg) {
// throw new SoapHeaderException(" ", SoapException.ClientFaultCode);
return new GetReportResult () {
Report = new FinReport {
ReportID = arg.ReportID,
Date = new DateTime (2015, 03, 15),
Info = getReportInfo(arg.ReportID)
}
};
}


private string getReportInfo( int reportID) {
StringBuilder sb = new StringBuilder ();
sb.Append( "ReportID = " ).Append(reportID).AppendLine();

if (HabraHeader != null ) {
sb.Append( "Login = " ).Append(HabraHeader.Login).AppendLine();
sb.Append( "Password = " ).Append(HabraHeader.Password).AppendLine();
}

foreach ( var header in UnknownHeaders) {
sb.Append( " SoapHeader = " ).Append(header.Element.Name).AppendLine();
sb.Append( "MustUnderstand = " ).Append(header.MustUnderstand).AppendLine();

// if (header.Element.Name == "HabraSoapHeader") {
// sb.AppendLine("HabraSoapHeader ");
// sb.Append("Login = ").Append(header.Element["Login"].InnerText).AppendLine();
// sb.Append("Password = ").Append(header.Element["Password"].InnerText).AppendLine();
// header.DidUnderstand = true;
// }
}

ResultTimeHeader = new ResultTimeSoapHeader ();
ResultTimeHeader.ResultTime = DateTime .Now;
return sb.ToString();
}
}


public class HabraSoapHeader : SoapHeader {
public string Login { get ; set ; }
public string Password { get ; set ; }
}


public class ResultTimeSoapHeader : SoapHeader {
public DateTime ResultTime { get ; set ; }
}


public class FinReport {
public int ReportID { get ; set ; }
public DateTime Date { get ; set ; }
public string Info { get ; set ; }
}


public class GetReportArg {
public int ReportID { get ; set ; }
}


public class GetReportResult {
public FinReport Report { get ; set ; }
}
}
- -, wsdl. :
private void webMethodTest_GetReport() {
var service = GetFinReportService();
var arg = new GetReportArg ();
arg.ReportID = 45;

service.HabraSoapHeaderValue = new HabraSoapHeader ();
service.HabraSoapHeaderValue.Login = "neo" ;
service.HabraSoapHeaderValue.Password = "3Ku2kcQfNLOW" ;
service.HabraSoapHeaderValue.MustUnderstand = true ;

var result = service.GetReport(arg);
var resultTimeSoapHeader = service.ResultTimeSoapHeaderValue;

if (resultTimeSoapHeader != null ) {
MessageBox .Show( "ResultTimeSoapHeader = " + resultTimeSoapHeader.ResultTime);
}

MessageBox .Show(result.Report.Info);
}

, asmx - soap, :
  • SoapHeader
  • - [SoapHeader],

- – HabraSoapHeader ResultTimeSoapHeader, , .

:
<? xml   version = " 1.0 "   encoding = " UTF-8 " ?>
< soap:Envelope   xmlns:soap = " http://schemas.xmlsoap.org/soap/envelope/ "   xmlns:xsd = " http://www.w3.org/2001/XMLSchema "   xmlns:xsi = " http://www.w3.org/2001/XMLSchema-instance " >
< soap: Header >
< HabraSoapHeader   xmlns = " http://asmx.habrahabr.ru/ "   soap:mustUnderstand = " 1 " >
< Login > neo </ Login >
< Password > 3Ku2kcQfNLOW </ Password >
</ HabraSoapHeader >
</ soap:Header >
< soap: Body >
< GetReport   xmlns = " http://asmx.habrahabr.ru/ " >
< arg >
< ReportID > 45 </ ReportID >
</ arg >
</ GetReport >
</ soap:Body >
</ soap:Envelope >

<? xml   version = " 1.0 "   encoding = " UTF-8 " ?>
< soap:Envelope   xmlns:soap = " http://schemas.xmlsoap.org/soap/envelope/ "   xmlns:xsd = " http://www.w3.org/2001/XMLSchema "   xmlns:xsi = " http://www.w3.org/2001/XMLSchema-instance " >
< soap: Header >
< ResultTimeSoapHeader   xmlns = " http://asmx.habrahabr.ru/ " >
< ResultTime > 2015-03-24T11:37:00.6135717+06:00 </ ResultTime >
</ ResultTimeSoapHeader >
</ soap:Header >
< soap: Body >
< GetReportResponse   xmlns = " http://asmx.habrahabr.ru/ " >
< GetReportResult >
< Report >
< ReportID > 45 </ ReportID >
< Date > 2015-03-15T00:00:00 </ Date >
< Info >
ReportID = 45
Login = neo
Password = 3Ku2kcQfNLOW
</ Info >
</ Report >
</ GetReportResult >
</ GetReportResponse >
</ soap:Body >
</ soap : Envelope >
 
soap:mustUnderstand=«1», , - . SoapHeader public bool DidUnderstand { set; get; }, , . true, – false. , mustUnderstand=«1», DidUnderstand false, - soap:Fault. [SoapHeader(«HabraHeader»)], :
<? xml   version = " 1.0 "   encoding = " UTF-8 " ?>
< soap:Envelope   xmlns:soap = " http://schemas.xmlsoap.org/soap/envelope/ "   xmlns:xsd = " http://www.w3.org/2001/XMLSchema "   xmlns:xsi = " http://www.w3.org/2001/XMLSchema-instance " >
< soap: Body >
< soap: Fault >
< faultcode > soap:MustUnderstand </ faultcode >
< faultstring > SOAP HabraSoapHeader . </ faultstring >
</ soap:Fault >
</ soap:Body >
</ soap:Envelope >
public SoapUnknownHeader[] UnknownHeaders { get; set; }, foreach.

SoapExtension. -. . - ( -), .

, , , SoapHeaderException, SoapException. , , , SoapException, soap:Fault. .

15.


asmx - . CacheDuration -, HttpContext.Cache . :
[ WebMethod (Description = "     ID" , CacheDuration = 5)]
public GetReportResult GetReport( GetReportArg arg){
return new GetReportResult (){
Report = new FinReport {
ReportID = arg.ReportID,
Date = DateTime .Now,
Info = getReportInfo(arg.ReportID)
}
};
}


private string getReportInfo( int reportID){
string key = "getReportInfo_" + reportID;
string cachedInfo = Context.Cache.Get(key) as string ;

if (cachedInfo != null ){
return cachedInfo;
}

string info = DateTime .Now + " reportID = " + reportID;
this .Context.Cache.Add(key, info, null , DateTime .Now.AddSeconds(10), TimeSpan .Zero, CacheItemPriority .High, null );
return info;
}
soap . , - , .

, .

, Date 5 , Info – 10 .

, , .

16. SoapExtension


SoapExtension – , , -, . , , .

SoapExtension:
  • ( soap MemoryStream)
  • (soap header)
  • 1 : - (- )
  • 2 : - ( ) web.config (app.config) !
  • - -
  • SoapExtension

SoapExtension
  • compression
  • soap header

Links


17. x64 Visual Studio


- Visual Studio 64- -. Platform target: x64, « «FinReportWebService» . , ».

.

1 way. - «Use Local IIS Web server», IIS



2 . - IIS Debug -> Attach to Process .

3 . IIS ASP.NET Development Server - CassiniDev . CassiniDev : https://cassinidev.codeplex.com/SourceControl/latest#ReadMe.htm

, \trunk\src\CassiniDev.sln, trunk\src\packages. x64 :



- C:\Program Files (x86)\Common Files\microsoft shared\DevServer . , \trunk\src\CassiniDev\bin\x64\Debug



18. ()


Publish File System Visual Studio , IIS



wwwroot

- C:\inetpub\wwwroot , -. , IIS inetmgr .





HTTP Error 404.3 — Not Found

HTTP Error 404.3 — Not Found, IIS




%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_regiis.exe –ir


-



- IIS :
HTTP Error 500.19 — Internal Server Error.
The requested page cannot be accessed because the related configuration data for the page is invalid.
or
Access is denied.
Error message 401.3: You do not have permission to view this directory or page using the credentials you supplied
.

19. IIS


- . . FinReportPool -.


ApplicationPoolIdentity :
IIS AppPool\< >

, IIS AppPool\FinReportPool , IIS AppPool\DefaultAppPool

,







, < >\IIS_IUSRS

.



, x86/x64
«FinReportWebService» . , .
Could not load file or assembly 'FinReportWebService' or one of its dependencies. An attempt was made to load a program with an incorrect format.
- Enable 32-Bit Applications .

Identity -.

AppCmd.exe

w3wp.exe. :
AppcmdList.bat
%systemroot%\System32\inetsrv\appcmd list wp
%systemroot%\System32\inetsrv\appcmd list sites
%systemroot%\System32\inetsrv\appcmd list app
%systemroot%\System32\inetsrv\appcmd list appPools
pause



, , -. AppCmd.exe .

20.


-, , .

Source: https://habr.com/ru/post/254165/


All Articles