adding swagger to WCF - wcf

I am trying to make a proper nice documentation for a Microsoft WCF.
I added an asp web app to display swagger and installed
Swashbuckle.AspNetCore.5.6.3
I've set the solution to run both the WSF service and the asp web app.
Here is my code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
namespace asp_core_webapp
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Version = "1.0", Description = "my api " });
c.DocInclusionPredicate((_, api) => !string.IsNullOrWhiteSpace(api.GroupName));
c.TagActionsBy(api => api.GroupName);
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
services.AddControllers();
services.AddControllers();
// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
}
}
I do no see anything running on
/swagger/v1/swagger.json
/swagger/v1
/swagger
There does not seem to be any valid documentation out there.
Any ideas how to make swagger work?
If you know another better alternative to Swagger, please feel free to add comments.
thanks
update: this is the main part of my config file:
<system.web>
<compilation debug="true" targetFramework="4.7.2" />
<httpRuntime targetFramework="4.7.2" />
</system.web>
<system.serviceModel>
<services>
<service behaviorConfiguration="serviceBehavior" name="WcfService1.Service1">
<endpoint address="Service1" binding="basicHttpBinding"
bindingConfiguration="" name="serviceEndPoint" contract="WcfService1.IService1" />
<endpoint address="mex" binding="mexHttpBinding"
bindingConfiguration="" name="mex" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="MyEndPointBehavior">
<webHttp helpEnabled="true" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="serviceBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="webHttpBinding" scheme="http" />
</protocolMapping>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true" />
</system.webServer>

Asp.net Core does not fully support WCF, if Swashbuckle.AspNetCore.5.6.3 is a package in AspNetCore, it may not be supported in WCF. In fact, WCF also has its own help document, you only need to enable it in the configuration file:
<endpointBehaviors>
<behavior name="ESEndPointBehavior">
<webHttp helpEnabled="true"/>
</behavior>
</endpointBehaviors>

Related

Multiple Message Inspectors for each service endpoint

If a WCF service exposes three endpoints, then can we have different message inspectors for each endpoint? If yes then how can we apply on each endpoint?
First, you should create an endpoint behavior to apply a formatter you want.
class CustomInspectorEndpointBehavior : IEndpointBehavior
{
public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CustomFormatter());
}
...
}
Or if there is a general logic, you can apply a behavior for each of the endpoints using a service behavior class:
class MultipleInspectorsServiceBehavior : IServiceBehavior
{
public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
foreach (var endpoint in serviceDescription.Endpoints)
endpoint.EndpointBehaviors.Add(new CustomInspectorEndpointBehavior());
}
...
}
Finally, you can apply endpoint behaviors from code...
var host = new ServiceHost(typeof(TheService));
foreach (var endpoint in host.Description.Endpoints)
endpoint.EndpointBehaviors.Add(new CustomInspectorEndpointBehavior(c));
(in case of service behavior)
var host = new ServiceHost(typeof(TheService));
host.Description.Behaviors.Add(new MultipleInspectorsServiceBehavior());
...Or from config:
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="customInspectorEndpointBehavior"
type="CustomInspectorEndpointBehaviorExtensionElement, MyAssembly" />
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior name="behavior1">
<customInspectorEndpointBehavior />
<!--maybe some other behaviors-->
</behavior>
<behavior name="behavior2">
<customInspectorEndpointBehavior />
<!--maybe some other behaviors-->
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service name="TheService">
<endpoint address="address1" ... contract="..." behaviorConfiguration="behavior1" />
<endpoint address="address2" ... contract="..." behaviorConfiguration="behavior1" />
<endpoint address="address3" ... contract="..." behaviorConfiguration="behavior2" />
</service>
</services>
</system.serviceModel>
For service behavior:
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="multipleInspectorsServiceBehavior"
type="MultipleInspectorsServiceBehaviorExtensionElement, MyAssembly" />
</behaviorExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior name="CustomServiceBehavior">
<multipleInspectorsServiceBehavior />
<!--maybe some others-->
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="TheService" behaviorConfiguration="ServiceBehavior">
<endpoint address="address1" ... contract="..." />
<endpoint address="address2" ... contract="..." />
<endpoint address="address3" ... contract="..." />
</service>
</services>
</system.serviceModel>
Here you should create a BehaviorExtensionElement to reference behaviors in the config:
class CustomInspectorEndpointBehaviorExtensionElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get
{
return typeof(CustomInspectorEndpointBehavior);
}
}
protected override object CreateBehavior()
{
return new CustomInspectorEndpointBehavior();
}
}
class MultipleInspectorsServiceBehaviorExtensionElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get
{
return typeof(MultipleInspectorsServiceBehavior);
}
}
protected override object CreateBehavior()
{
return new MultipleInspectorsServiceBehavior();
}
}

WCF web service custom authorization

I have WCF webservice using windows authentication and custom ServiceAuthorizationManager. Everything works fine, but if overridden CheckAccessCore returns false, I get error 500, instead of 401 as I expected. Service does not implement any service level error handling. How can I send 401 instead of 500 header?
Service config:
<!-- App configuration-->
<system.web>
<compilation debug="true" targetFramework="4.0" />
<customErrors mode="Off" />
</system.web>
<appSettings>
<!-- Allowed users divided by comma -->
<add key="allowedUsers" value="DOMAIN\User1, DOMAIN\User2" />
</appSettings>
<!--Webservice-->
<system.serviceModel>
<services>
<service name="WebService.ApiService">
<endpoint binding="basicHttpBinding" bindingConfiguration="AuthenticatedBinding" bindingNamespace="http://namespace.com/customapi" contract="WebService.IApiService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceAuthorization serviceAuthorizationManagerType="WebService.Model.Services.AuthorizationService, WebService" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="AuthenticatedBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
</configuration>
Custom authorization manager:
class AuthorizationService : ServiceAuthorizationManager
{
private List<string> allowedUsers = new List<string>();
public AuthorizationService() : base()
{
Configure();
}
protected override bool CheckAccessCore(OperationContext operationContext)
{
base.CheckAccessCore(operationContext);
return allowedUsers.Contains(operationContext.ServiceSecurityContext.WindowsIdentity.Name);
}
private void Configure()
{
var configRow = ConfigurationManager.AppSettings["allowedUsers"];
var parts = configRow.Split(',');
if (parts.Length > 0)
{
foreach (var part in parts)
allowedUsers.Add(part.Trim());
}
}
}
Result image:
I found on the web that error code 500 is the proper way how to send SOAP fault response. So everything is fine with my webservice (I am getting 'Access denied' fault with error code 500).
SOAP specification about it
Summary on Martin Karpiseks blog

Calling WCF REST Service using JQuery Ajax not working

I am learning WCF service. I am trying to call an RESTful service from Jquery. My Service is as below
Service Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.ServiceModel.Activation;
namespace RESTfulServiceLib
{
[AspNetCompatibilityRequirements(RequirementsMode
= AspNetCompatibilityRequirementsMode.Allowed)]
public class RestFullService : IRestFullService
{
public string Welcome(string Name)
{
return "Welcome to Restful Service " + Name;
}
}
}
Interface
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.ServiceModel.Activation;
namespace RESTfulServiceLib
{
[ServiceContract]
public interface IRestFullService
{
[OperationContract]
[WebInvoke(UriTemplate="/Welcome/{Name}",Method="GET",ResponseFormat=WebMessageFormat.Json)]
string Welcome(string Name);
}
}
I have created a service host and the svc file goes like this
<%# ServiceHost Language="C#" Debug="true" Service="RESTfulServiceLib.RestFullService" Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>
I have the following config settings
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="EndPBhvr">
<webHttp helpEnabled="true" defaultOutgoingResponseFormat="Json"
faultExceptionEnabled="true" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="SvcBhvr">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<services>
<service name="RESTfulServiceLib.RestFullService">
<endpoint address="" behaviorConfiguration="EndPBhvr"
binding="webHttpBinding" bindingConfiguration="" name="EP1"
contract="RESTfulServiceLib.IRestFullService" />
</service>
</services>
</system.serviceModel>
</configuration>
After running the application , when I am browsing the url "http://localhost:2319/RESTFullService.svc/welcome/Mahesh"
it is returning the value as
"Welcome to Restful Service Mahesh"
I have tried to call this service using Jquery. But I am getting
error 200 undefined
The script is as follows
<!DOCTYPE html>
<html>
<head>
<script src="jquery-1.6.4.min.js"></script>
<script >
function ajaxcall()
{
$.ajax({
url: "http://localhost:2319/RESTFullService.svc/welcome/mahesh",
type: "GET",
contentType: "application/json; charset=utf-8",
dataType:"jsonp",
data:{},
processdata : true,
success: function(response)
{
var data= response.d;
alert(data);
},
error: function(e)
{
alert('error '+e.status + ' ' + e.responseText);
}
});
}
$().ready(function(){
$('#btntest').click(ajaxcall);
})
</script>
</head>
<body>
<button id="btntest" >Click Me</button>
</body>
</html>
What is wrong in my coding? Please help me...
Thanks
Mahesh
In the Success of your jQuery you have used
// var data= response.d;
but your are not returning a valid json.
there is no "d" in your response.
It is due to the Cross Domain Issue. I did a couple of changes in config file and it worked fine
The config file after changes. Added new Binding Configuration with crossDomainScriptAccessEnabled="true" and added to the endpoint. And put the aspNetCompatibilityEnabled="false"
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<standardEndpoints />
<bindings>
<webHttpBinding>
<binding name="Bind1" crossDomainScriptAccessEnabled="true" />
</webHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="EndPBhvr">
<webHttp helpEnabled="true" defaultOutgoingResponseFormat="Json"
faultExceptionEnabled="true" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="SvcBhvr">
<serviceMetadata httpGetEnabled="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" />
<services>
<service behaviorConfiguration="SvcBhvr" name="RESTfulServiceLib.RestFullService">
<endpoint address="" behaviorConfiguration="EndPBhvr" binding="webHttpBinding"
bindingConfiguration="Bind1" name="EP1" contract="RESTfulServiceLib.IRestFullService" />
</service>
</services>
</system.serviceModel>
</configuration>
Modified script, i have added "?callback=?" at the end of the URL to get the output in JSON instead of JSONP. JSONP will work without giving the callback in the URL
<!DOCTYPE html>
<html>
<head>
<script src="jquery-1.6.4.min.js"></script>
<script type="text/javascript" language="javascript">
function ajaxcall()
{
$.ajax({
url: "http://localhost:2319/RESTFullService.svc/welcome/mahesh?calback=?",
type: "GET",
dataType: "json",
contentType: "application/json; charset=utf-8",
data: {},
processdata: true,
success: function (response) {
var data = response;
alert(data);
},
error: function (e) {
alert('error ' + e.status + ' ' + e.responseText);
}
});
}
$().ready(function(){
$('#btntest').click(ajaxcall);
})
</script>
</head>
<body>
<button id="btntest" >Click Me</button>
</body>
</html>
Thanks to my colleague Mrs.Poorani in Calsoft Labs for helping me in solving this issue
function CallService(sucessData) {
$.ajax({
// Add code for Cross Domain
headers: getHeaders(),
type: varType, //GET or POST or PUT or DELETE verb
url: varUrl, // Location of the service
data: varData, //Data sent to server
contentType: varContentType, // content type sent to server
dataType: varDataType, //Expected data format from server
processdata: varProcessData, //True or False
crossDomain: true,
cache: varCache,
timeout: 200000,
success: sucessData,
error: function (xhr) {// When Service call fails
fancyAlert("Error: " + xhr.responseText);
//fancyAlert('Error occured in Service Call');
}
});
}

WCF REST service results in 404

I have read most of the WCF REST 404 posts but none that helped me...
I have built a WCF REST service successfully. However, now it is causing issues. I tried just creating a sample WCF REST service and I cannot get this to work without using the .SVC.
This is what I have in my code
[ServiceContract]
public interface IService1
{
[WebGet(UriTemplate="data")]
[OperationContract]
string GetData();
}
public class Service1 : IService1
{
public string GetData()
{
return "1";
}
}
and this is what I have in my web.config
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service behaviorConfiguration="Default" name="RESTService.Service1">
<endpoint address="http://mydomain:8888/Service1.svc"
binding="webHttpBinding"
contract="RESTService.IService1"
behaviorConfiguration="Web" />
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="Default">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="Web">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
</configuration>
When I go to http://mydomain:8888/data is responds in 404. Any ideas why it is not hitting the GetData() function? The following URL works if I remove the Endpoint address
http://mydomain:8888/Service1.svc/data
However, I want the address to be http://mydomain:8888/data
You can try leaving your web config in the version you have working correctly (with the svc in the path) and add route table entries during application start in the global asax file
check this out for more info
http://msdn.microsoft.com/en-us/library/cc668177.aspx

Hosting WCF soap and rest endpoints side by side

I have written a service that I would like expose both via rest and soap. Everything I read about WCF 4.0 says that I just need to expose 2 endpoints with differing behaviors to do this. But I cannot get it to work.
Here is my service contract:
[ServiceContract]
public interface MyService
{
[OperationContract]
[WebGet(UriTemplate="data/{value}")]
string GetData(string value);
}
Here is my web.config:
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service name="MyService">
<endpoint name="mex" address="mex" binding="mexHttpBinding" contract="MyService"/>
<endpoint address="rest" behaviorConfiguration="restBehavior" binding="webHttpBinding" contract="MyService" />
<endpoint address="soap" behaviorConfiguration="soapBehavior" binding="basicHttpBinding" contract="MyService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="restBehavior">
<webHttp automaticFormatSelectionEnabled="true" helpEnabled="true" />
</behavior>
<behavior name="soapBehavior" />
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
</configuration>
I am using routing to define my service url:
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.Add(new ServiceRoute("dns", new ServiceHostFactory(), typeof(MyService)));
}
}
Is there something that I am doing wrong here? I could really use some help.
I never found the "right" way to do this in configuration but was able to use the routing engine to accomplish this.
My global asax file now looks like this:
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.Add(new ServiceRoute("my/soap", new ServiceHostFactory(), typeof(MyService)));
RouteTable.Routes.Add(new ServiceRoute("my/rest", new WebServiceHostFactory(), typeof(MyService)));
}
}
and my config like this: (to enable the rest help pages)
<system.serviceModel>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint automaticFormatSelectionEnabled="true" helpEnabled="true"/>
</webHttpEndpoint>
</standardEndpoints>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
I like that this is in line with the asp.net MVC model more and requires little config. Additionally doing it this way allowed me to remove the .svc files from my project entirely which is also a plus IMO.
How are you hosting your WCF service?? In IIS, you need a virtual directory and a MyService.svc file somewhere to enable service activation.
If you remove the ServiceRoute for now (to simplify matters), you should be able to reach your SOAP service endpoint at:
http://YourServer:Port/YourVirtualDirectory/YourService.svc/soap
and your REST service should be at
http://YourServer:Port/YourVirtualDirectory/YourService.svc/rest/data/{value}
(where you supply some arbitrary value for {value}).
What exactly is not working in your case??
You can try and test your SOAP endpoints using the WCF Test Client, while you should be able to hit the REST url in any browser.
This is possible to do in configuration. From msdn forum thread by user Ladislav Mrnka: http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/4e95575f-1097-4190-80dd-7a0f96d73f6e
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="REST">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="iSell.Prospects.ProspectBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="iSell.Prospects.ProspectBehavior" name="iSell.Prospects.ProspectService">
<endpoint address="" behaviorConfiguration="REST" binding="webHttpBinding" contract="iSell.Prospects.ProspectService" />
<endpoint address="soap" binding="basicHttpBinding" contract="iSell.Prospects.ProspectService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>