I am writing a WCF client that reads a firmware file, copies it to byte array sends it to the WCF server. The server flashes it to another device connected to it. Both the server and the client are written by me.
I have created a WCF service inline in code without using the service model configuration section in the configuration.xml. Since the server might take a minute to flash, I have implemented the contract as asynchronous so that the client need not wait till the flashing is complete. Both server and client share the same IFirmwareUpgrade interface.
Howver when I run the client and the server, am getting the ServiceModel.ActionNotSupportedException exception. Here is the exception details.
The message with Action 'ServiceContractNS/UpdatorContract/UpgradeFirmware' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher.This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).
Here is the Server code
namespace Updator
{
partial class UpdatorWCFService : IUpdator, IFirmwareUpgrade
{
public bool UpgradeFirmware(string deviceIPAddress, byte[] buffer)
{
m_RFM220Manager.UpgradeFirmware(deviceIPAddress, buffer);
return true;
}
public System.IAsyncResult BeginUpgradeFirmware(string DeviceIPAddress, byte[] buffer, System.AsyncCallback callback, object asyncState)
{
Console.WriteLine("BeginServiceAsyncMethod called with: \"{0}\"", DeviceIPAddress);
//Do something
return new CompletedAsyncResult<bool>(true);
}
public bool EndUpgradeFirmware(System.IAsyncResult r)
{
CompletedAsyncResult<bool> result = r as CompletedAsyncResult<bool>;
Console.WriteLine("EndServiceAsyncMethod called with: \"{0}\"", result.Data);
//return result.Data;
return false;
}
}
// Simple async result implementation.
class CompletedAsyncResult<T> : IAsyncResult
{
T data;
public CompletedAsyncResult(T data)
{ this.data = data; }
public T Data
{ get { return data; } }
#region IAsyncResult Members
public object AsyncState
{ get { return (object)data; } }
public WaitHandle AsyncWaitHandle
{ get { throw new Exception("The method or operation is not implemented."); } }
public bool CompletedSynchronously
{ get { return true; } }
public bool IsCompleted
{ get { return true; } }
#endregion
}
}
This is how created the service
NetTcpBinding binding = new NetTcpBinding();
binding.MaxConnections = (int)m_Configuration.wcftcpchannel.maxconnection;
binding.TransferMode = TransferMode.Buffered;
binding.MaxBufferSize = 20 * 1024 * 1024;
binding.MaxReceivedMessageSize = binding.MaxBufferSize;
binding.ReaderQuotas.MaxArrayLength = 2147483647;
binding.Security.Mode = SecurityMode.None;
binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;
binding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.None;
binding.Security.Message.ClientCredentialType = MessageCredentialType.None;
// Step 3 of the hosting procedure: Add a service endpoint.
m_UITCPHost.AddServiceEndpoint(
typeof(IUpdator),
binding,
"UpdatorWCFService");
// Step 5 of the hosting procedure: Start (and then stop) the service.
m_UITCPHost.Open(new TimeSpan(0, 0, 0, m_Configuration.wcftcpchannel.timeoutinms));
Here is the contract
[ServiceContract(Name = "UpdatorContract", Namespace = "ServiceContractNS", ConfigurationName = "UpdatorContract")]
public interface IFirmwareUpgrade
{
[OperationContract]
bool UpgradeFirmware(string DeviceIPAddress, byte[] buffer);
//[System.ServiceModel.OperationContractAttribute(AsyncPattern = true, Action = "ServiceContractNS/UpdatorContract/UpgradeFirmware", ReplyAction = "ServiceContractNS/UpdatorContract/UpgradeFirmwareResponse")]
[OperationContract(AsyncPattern=true)]
System.IAsyncResult BeginUpgradeFirmware(string DeviceIPAddress, byte[] buffer, System.AsyncCallback callback, object asyncState);
// Note: There is no OperationContractAttribute for the end method.
bool EndUpgradeFirmware(System.IAsyncResult result);
}
This is the configsection in the client
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_UpdatorContract" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard" listenBacklog="10"
maxBufferPoolSize="20971520" maxBufferSize="20971520" maxConnections="10"
maxReceivedMessageSize="20971520">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="2147483647"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="None">
<transport clientCredentialType="None" protectionLevel="None" />
<message clientCredentialType="None" />
</security>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://192.158.96.120:8000/Updator/UpdatorWCFService"
binding="netTcpBinding" bindingConfiguration="NetTcpBinding_UpdatorContract"
contract="UpdatorContract" name="NetTcpBinding_UpdatorContract" />
</client>
</system.serviceModel>
This is the Client class for Asynchronous implementation
public class FirmwareUpgradeClient : System.ServiceModel.ClientBase<IFirmwareUpgrade>, IFirmwareUpgrade
{
private BeginOperationDelegate onBeginUpgradeFirmwareDelegate;
private EndOperationDelegate onEndUpgradeFirmwareDelegate;
private System.Threading.SendOrPostCallback onUpgradeFirmwareCompletedDelegate;
public event System.EventHandler<UpgradeFirmwareCompletedEventArgs> UpgradeFirmwareCompleted;
public bool UpgradeFirmware(string DeviceIPAddress, byte[] buffer)
{
return base.Channel.UpgradeFirmware(DeviceIPAddress, buffer);
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
public System.IAsyncResult BeginUpgradeFirmware(string DeviceIPAddress, byte[] buffer, System.AsyncCallback callback, object asyncState)
{
return base.Channel.BeginUpgradeFirmware(DeviceIPAddress, buffer, callback, asyncState);
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
public bool EndUpgradeFirmware(System.IAsyncResult result)
{
return base.Channel.EndUpgradeFirmware(result);
}
private System.IAsyncResult OnBeginUpgradeFirmware(object[] inValues, System.AsyncCallback callback, object asyncState)
{
string DeviceIPAddress = ((string)(inValues[0]));
byte[] buffer = ((byte[])(inValues[1]));
return this.BeginUpgradeFirmware(DeviceIPAddress, buffer, callback, asyncState);
}
private object[] OnEndUpgradeFirmware(System.IAsyncResult result)
{
bool retVal = this.EndUpgradeFirmware(result);
return new object[] {
retVal};
}
private void OnUpgradeFirmwareCompleted(object state)
{
if ((this.UpgradeFirmwareCompleted != null))
{
InvokeAsyncCompletedEventArgs e = ((InvokeAsyncCompletedEventArgs)(state));
this.UpgradeFirmwareCompleted(this, new UpgradeFirmwareCompletedEventArgs(e.Results, e.Error, e.Cancelled, e.UserState));
}
}
public void UpgradeFirmwareAsync(string DeviceIPAddress, byte[] buffer)
{
this.UpgradeFirmwareAsync(DeviceIPAddress, buffer, null);
}
public void UpgradeFirmwareAsync(string DeviceIPAddress, byte[] buffer, object userState)
{
try
{
if ((this.onBeginUpgradeFirmwareDelegate == null))
{
this.onBeginUpgradeFirmwareDelegate = new BeginOperationDelegate(this.OnBeginUpgradeFirmware);
}
if ((this.onEndUpgradeFirmwareDelegate == null))
{
this.onEndUpgradeFirmwareDelegate = new EndOperationDelegate(this.OnEndUpgradeFirmware);
}
if ((this.onUpgradeFirmwareCompletedDelegate == null))
{
this.onUpgradeFirmwareCompletedDelegate = new System.Threading.SendOrPostCallback(this.OnUpgradeFirmwareCompleted);
}
base.InvokeAsync(this.onBeginUpgradeFirmwareDelegate, new object[] {
DeviceIPAddress,
buffer}, this.onEndUpgradeFirmwareDelegate, this.onUpgradeFirmwareCompletedDelegate, userState);
}
catch (Exception e)
{
}
}
}
Related
I need to add a Request Header to a WCF Request when using ConfigurationChannelFactory.CreateChannel.
I have already tried using OperationContextScope.
I have a function which is as shown below:
public O Execute<O>(Func<T, O> action, string configFilePath, string endpoint, StringDictionary headers)
{
bool closed = false;
T channel = default(T);
O output = default(O);
try
{
channel = this.GetChannel(configFilePath, endpoint);
if (headers != null && headers.Count > 0)
{
(channel as IClientChannel).Open();
using (new OperationContextScope(channel as IClientChannel))
{
HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty();
foreach (DictionaryEntry header in headers)
{
requestMessage.Headers[header.Key.ToString()] = header.Value.ToString();
}
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage;
output = action(channel);
}
(channel as IClientChannel).Close();
}
else
{
(channel as IClientChannel).Open();
output = action(channel);
(channel as IClientChannel).Close();
}
closed = true;
}
finally
{
if (!closed && channel != null)
{
(channel as IClientChannel).Abort();
}
}
return output;
}
private T GetChannel(string configFilePath, string endpoint)
{
//Get the ChannelFactoryObject
ConfigurationChannelFactory<T> wcfClientFactory = null;
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap { ExeConfigFilename = configFilePath };
wcfClientFactory = new ConfigurationChannelFactory<T>(endpoint, ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None), null);
return wcfClientFactory.CreateChannel();
}
Configuration file entry:
<security mode="Transport">
<transport clientCredentialType="None" proxyCredentialType="None" realm="" />;clientCredentialType="Windows" negotiateServiceCredential="true" />
</security>
The above function is called from another .cs file, as shown below, passing Func<T,O> as an argument:
Execute<MyService.InformationResponse[]>=>IMyService.GetInformation(Request), ConfigPath, myServiceEndPoint, headers);
I am getting 400, BadRequest as the Service is expecting "Authorization" in the Request header, which it is not able to find.
We could use the WebOperationContext class to alter and add HTTP header, please refer to the below code segments.
IService service = factory.CreateChannel();
using (OperationContextScope scope = new OperationContextScope((IContextChannel)service))
{
WebOperationContext.Current.OutgoingRequest.ContentType = "application/json; charset=utf-8";
WebOperationContext.Current.OutgoingRequest.Headers.Add("Authorization", "bearer xxxxxxxx");
service.GetData();
}
Result.
For details,
https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.web.weboperationcontext?redirectedfrom=MSDN&view=netframework-4.8
Feel free to let me know if there is anything I can help with.
code:
#Aspect
#Order(Integer.MAX_VALUE)
public class KeySortedAspect {
#Pointcut(value = "#annotation(com.le.bigdata.convertor.KeySortedRule)")
public void pointCut(){}
#Around(value = "pointCut()")
public Object keySorted(ProceedingJoinPoint joinPoint) throws Throwable {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Object result = joinPoint.proceed();
if(methodSignature.getMethod().isAnnotationPresent(KeySortedRule.class)){
KeySortedRule keySortedRule = methodSignature.getMethod().getAnnotation(KeySortedRule.class);
String path = keySortedRule.path();
//String rule = keySortedRule.rule();
try {
Map<String, String[]> keyMap = getRule(path);
Class<?> type = methodSignature.getMethod().getReturnType();
Object a = sortedUseKeyList(result, keyMap, type);
return a;
} catch (IOException e) {
logger.error("Key sorted Error");
logger.error("reason ", e);
}
}
return result;
}
}
spring mvc configuration mvc-dispatcher-servlet.xml:
<context:annotation-config />
<!-- controller层 内部ctrl与lebi对接的ctrl -->
<context:component-scan base-package="com.le.bigdata.controller, com.le.bigdata.api.controller"/>
<bean id="keySortedAspect" class="com.le.bigdata.convertor.KeySortedAspect"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
<mvc:default-servlet-handler/>
controller code:
#RestController
#RequestMapping("cp")
public class ContentPortraitController {
#Autowired
private IContentPortraitService service;
#RequestMapping(value = "usertop", method = RequestMethod.GET)
#ApiOperation(value = "获取默认的cv排名前10名的专辑")
private CommonResponseDTO getUserTop(#RequestParam(defaultValue = "10") int size){
List<Map<String, Object>> list = service.getDefaultPid(size);
return new CommonResponseDTO(true, list);
}
#RequestMapping(value = "/album", method = RequestMethod.GET)
#ApiOperation(value = "获取专辑画像", httpMethod = "GET")
#KeySortedRule(path="content_portrait")
public JSONObject getAlbum(#RequestParam(value="dt",required = false) String dt,
#RequestParam(value="start_dt",required = false) String start,
#RequestParam(value="end_dt",required = false) String end,
#RequestParam(value="product",required = false) List<String> product,
#RequestParam(value="pid") String pid){
JSONObject result = new JSONObject();
JSONObject data = null;
try {
if(dt != null && !dt.isEmpty()){
//
data = service.getAlbumPortrait(pid, dt, product);
} else if(start != null && end != null && !start.isEmpty() && !end.isEmpty()){
//
data = service.getAlbumPortrait(pid, start, end, product);
}
} catch (Exception e) {
return null;
}
result.put("result", data);
return result;
}
}
When I request /cp/album it works and response data.
But I request /cp/usertop it throw an exception: NullPointException
reference service is null! service could not be null, IContentPortraitService is an interface and has only one implementation. And I absolutely sure I have scan service package and use #Service annotation on it's implementation.
Debug information:
When I request /cp/usertop:
ContentProtrationController instance is ContentPortraitServiceImpl$$EnhancerBySprinigCGLIB$bdfd5678#7591 and service is null
When I request /cp/album:
ContentProtrationController instance is ContentPortraitServiceImpl#7591 and service is not null, it works.
What's wrong with my code? This problem has troubled me for many days.Who knows how to solve this problem?
I am trying to wrap my server and client console apps in a class so I can initialize it. When I try to code in the Client side in AppStart it won't let me call the StartClient method. It works just fine for my server, but not the client.
Here is the server class:
namespace Server
{
public class RunServer
{
// State object for reading client data asynchronously
public class StateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public class AsynchronousSocketListener
{
// Thread signal.
public static ManualResetEvent allDone = new ManualResetEvent(false);
public AsynchronousSocketListener()
{
}
public static void StartListening()
{
// Data buffer for incoming data.
byte[] bytes = new Byte[1024];
// Establish the local endpoint for the socket.
// The DNS name of the computer
// running the listener is "host.contoso.com".
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);
// Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
// Bind the socket to the local endpoint and listen for incoming connections.
try
{
listener.Bind(localEndPoint);
listener.Listen(100);
while (true)
{
// Set the event to nonsignaled state.
allDone.Reset();
// Start an asynchronous socket to listen for connections.
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
// Wait until a connection is made before continuing.
allDone.WaitOne();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.WriteLine("\nPress ENTER to continue...");
Console.Read();
}
public static void AcceptCallback(IAsyncResult ar)
{
// Signal the main thread to continue.
allDone.Set();
// Get the socket that handles the client request.
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
public static void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(
state.buffer, 0, bytesRead));
// Check for end-of-file tag. If it is not there, read
// more data.
content = state.sb.ToString();
if (content.IndexOf("<EOF>") > -1)
{
// All the data has been read from the
// client. Display it on the console.
Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
content.Length, content);
Random rand = new Random();
content = rand.ToString();
// Echo the data back to the client.
Send(handler, content);
}
else {
// Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
private static void Send(Socket handler, String data)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), handler);
}
private static void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket handler = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = handler.EndSend(ar);
Console.WriteLine("Sent {0} bytes to client.", bytesSent);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public static int Main(String[] args)
{
StartListening();
return 0;
}
}
}
}
And here is the program that initializes it:
namespace AppStart
{
class Program
{
static void Main(string[] args)
{
Server.RunServer.AsynchronousSocketListener.StartListening();
Client.RunClient.AsynchronousClient. //Won't let me call StartClient method
}
}
}
Here is the Client side:
namespace Client
{
public class RunClient
{
// State object for receiving data from remote device.
public class StateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 256;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public class AsynchronousClient
{
// The port number for the remote device.
private const int port = 11000;
// ManualResetEvent instances signal completion.
private static ManualResetEvent connectDone =
new ManualResetEvent(false);
private static ManualResetEvent sendDone =
new ManualResetEvent(false);
private static ManualResetEvent receiveDone =
new ManualResetEvent(false);
// The response from the remote device.
private static String response = String.Empty;
private static void StartClient()
{
// Connect to a remote device.
try
{
// Establish the remote endpoint for the socket.
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);
// Create a TCP/IP socket.
Socket client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
// Connect to the remote endpoint.
client.BeginConnect(localEndPoint,
new AsyncCallback(ConnectCallback), client);
connectDone.WaitOne();
// Send test data to the remote device.
Send(client, "This is a test<EOF>");
sendDone.WaitOne();
// Receive the response from the remote device.
Receive(client);
receiveDone.WaitOne();
// Write the response to the console.
Console.WriteLine("Response received : {0}", response);
// Release the socket.
//client.Shutdown(SocketShutdown.Both);
//client.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void ConnectCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
Console.WriteLine("Socket connected to {0}",
client.RemoteEndPoint.ToString());
// Signal that the connection has been made.
connectDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void Receive(Socket client)
{
try
{
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void ReceiveCallback(IAsyncResult ar)
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
else {
// All the data has arrived; put it in response.
if (state.sb.Length > 1)
{
response = state.sb.ToString();
}
// Signal that all bytes have been received.
receiveDone.Set();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void Send(Socket client, String data)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), client);
}
private static void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
// Signal that all bytes have been sent.
sendDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public static int Main(String[] args)
{
StartClient();
return 0;
}
}
}
}
I am trying to validate an assertion signature received from an IDP. It results in a failure with following error :
Verification failed for URI "#_7e59add4-11a0-415f-85a3-6f493110d198"
Expected Digest: PgSvwq0Jn6GLMHID20j1fT40VlhvdavKxEM3PtNUfLM=
Actual Digest: mDcfPO26UwGV/tt/JM20ADDDkGGODjd2CZn7dqqR5LM=
org.opensaml.xml.signature.SignatureValidator(SignatureValidator.java:77) -
Signature did not validate against the credential's key
following is the code I am using to validate :
public class SamlTest {
public static void main(String[] args) throws Exception {
// read the file
File file = new File("d://a.xml");
BufferedReader bf = new BufferedReader(new FileReader(file));
String str = null;
String samlStr = "";
while ((str = bf.readLine()) != null) {
samlStr += str;
}
Assertion assertion = SamlTest.unmarshall(samlStr);
//Always do Profile Validation before cryptographically verify the Signature
SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();
try {
profileValidator.validate(assertion.getSignature());
} catch (ValidationException e) {
System.out.println("ErrorString [Error in SAMLSignatureProfilValidation]");
}
Certificate certificate = SamlTest.getCertificate(assertion.getSignature());
BasicCredential verificationCredential = new BasicCredential();
verificationCredential.setPublicKey(certificate.getPublicKey());
SignatureValidator sigValidator = new SignatureValidator(verificationCredential);
try {
sigValidator.validate(assertion.getSignature());
} catch (ValidationException e) {
System.out.println("unable to validate");
}
}
private static Assertion unmarshall(String samlStr) throws Exception {
DefaultBootstrap.bootstrap();
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder docBuilder = null;
docBuilder = documentBuilderFactory.newDocumentBuilder();
ByteArrayInputStream is = new ByteArrayInputStream(samlStr.getBytes());
Document document = null;
document = docBuilder.parse(is);
Element element = document.getDocumentElement();
UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory();
Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(element);
return (Assertion) unmarshaller.unmarshall(element);
}
private static Certificate getCertificate(Signature signature) {
try {
X509Certificate certificate = signature.getKeyInfo().getX509Datas().get(0).getX509Certificates().get(0);
if (certificate != null) {
//Converts org.opensaml.xml.signature.X509Certificate to java.security.cert.Certificate
String lexicalXSDBase64Binary = certificate.getValue();
byte[] decoded = DatatypeConverter.parseBase64Binary(lexicalXSDBase64Binary);
try {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(decoded));
return cert;
} catch (CertificateException e) {
//this should never happen
System.out.println("SAML Signature issue");
return null;
}
}
return null; // TODO Auto-generated method stub
} catch (NullPointerException e) {
//Null certificates
return null;
}
}}
below is the assertion xml received : `
<?xml version="1.0" encoding="UTF-8" standalone="no"?><saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns="http://docs.oasis-open.org/ws-sx/ws-trust/200512" xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns10="urn:oasis:names:tc:SAML:2.0:conditions:delegation" xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ns3="http://www.rsa.com/names/2009/12/std-ext/WS-Trust1.4/advice" xmlns:ns4="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns5="http://www.w3.org/2000/09/xmldsig#" xmlns:ns6="http://www.rsa.com/names/2009/12/std-ext/SAML2.0" xmlns:ns7="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:ns8="http://docs.oasis-open.org/ws-sx/ws-trust/200802" xmlns:ns9="http://www.w3.org/2005/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_7e59add4-11a0-415f-85a3-6f493110d198" IssueInstant="2015-06-16T19:38:03.664Z" Version="2.0"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://localhost/websso/SAML2/Metadata/vsphere.local</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#_7e59add4-11a0-415f-85a3-6f493110d198"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs xsi"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>PgSvwq0Jn6GLMHID20j1fT40VlhvdavKxEM3PtNUfLM=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>ovoMj6mUzEnhayptgu3MwQOiBEs47GO8Xs/H02SgO8/881X5m7anAmS8yIjHiOTu3Q0kNJH1K2cQ
uBNxKQG75jPHbM3wF6XVKLbcyjAWHjtg3Ndz6F2spIP13LZ7LM2KUBcwGh9YWBnybJWxwr70+qj0
7xHO5wEnV3RpkQPCjMgAfnesEAEHoCGpnQNQu0twSffWzKLKZcg6PHS2g49WY1r65Sw5Jcy9/VdN
4/mtEuNa4fb0wNbaKcpPxsjUo7dbeMdbZxl5T0E2pOTzGJkRKVfw1P6Vd2qIFrORVpfni5LAYkET
GJA40iY7wfVLJflIX7+9QcIEtMKsL5rbtxvQpQ==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIDcDCCAligAwIBAgIJAMGuXxNnFfBZMA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNVBAMMAkNBMRcw
FQYKCZImiZPyLGQBGRYHdnNwaGVyZTEVMBMGCgmSJomT8ixkARkWBWxvY2FsMQswCQYDVQQGEwJV
UzEQMA4GA1UECgwHcnVjaGF2YzAeFw0xNTAyMTcxNjMzNTFaFw0yNTAyMTExNjQzNDJaMBgxFjAU
BgNVBAMMDXNzb3NlcnZlclNpZ24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqV+/l
kSS9U2y6RBsLiqxwdiLjJJFCw/3iFu/Fmpu8vltMNPE5ryZsT87HJGzK8jDgGoTcD0DbbUk7+Sbe
XGVj7n5ZsBXiTt8nbpWQkUfBcNxKimqkGm3WgRgF3UjtNt4enC+mOLw4/aicBvvuscd8ur1QyJxK
zTUVOtkKFYg1FuKaelkSA2GrScLBzjaU99L0K2YrWncKG2T+1yIK5Md4TPr4X3GwhEqlBn7YK2sJ
43ILrEu43BCGyhkawp3bOHhnMVzMUHi2eY4NLXj0ZNTUFRrl8LKpDlSqFwL7ChNuhfLJOlncDwvD
20gOa6TWEC8qr3hXo4u5vUx9j2e5PS/pAgMBAAGjeTB3MAsGA1UdDwQEAwIF4DAoBgNVHREEITAf
gh1ydWNoYXZjLnRlc3RsYWIuY29tbXZhdWx0LmNvbTAdBgNVHQ4EFgQUjBP2wdHo83NDTsksTBtf
/1+EwA4wHwYDVR0jBBgwFoAU++fsPhJCQ4XETaWO1bQCjDDAgM8wDQYJKoZIhvcNAQELBQADggEB
AD4WqxL4+y4Uz/IzrKljq8mpU+dZNqpni8u5RaPUa4z/abfpB/vgSD08WGo7FHOYKDVJK6ScE8wB
+cuUV0/rL+4/L1sUVj4hixH/fUVS6jO6/SZerHEZ0ubO/X5zZAyfWXOKvxa6llgNFYjKGqd74+Lh
LCB2w84/VOOOJlaBJFFbh/9AY8cwtd8jFnMAYmQE7YQSLEagIKoeQSiVO1H8Kbhs4EQtLVmEQjR9
Pt1/H8VsRtPs+/0vAbzq8DJ6FTMz+OuhpyJHmIdP2Xw8T/2LGpGFSTVzbeGKGW3h7cCHA0MEHQ2J
ags26hB/IvRy2PxLgA9yRUroro9dbW8jIGch4UM=</ds:X509Certificate><ds:X509Certificate>MIIDgDCCAmigAwIBAgIJAP828FCXHTizMA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNVBAMMAkNBMRcw
FQYKCZImiZPyLGQBGRYHdnNwaGVyZTEVMBMGCgmSJomT8ixkARkWBWxvY2FsMQswCQYDVQQGEwJV
UzEQMA4GA1UECgwHcnVjaGF2YzAeFw0xNTAyMTQxNjQzNDJaFw0yNTAyMTExNjQzNDJaMFwxCzAJ
BgNVBAMMAkNBMRcwFQYKCZImiZPyLGQBGRYHdnNwaGVyZTEVMBMGCgmSJomT8ixkARkWBWxvY2Fs
MQswCQYDVQQGEwJVUzEQMA4GA1UECgwHcnVjaGF2YzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALgcEKO2qkQobx4vGXNG2D6HqnHNwiqEBs+cbrAGRwVtT2AxavMu4aUL9kDO8yyrqXT2
UF5W5B2jFEWr413h8MmV4v+F/+MqVW7UXQ6C0f6bsaBLdmQNa69b4EAj0UGvvohogObglvP9Du0n
qXwDTt3NMg2aJefHtLsyAXA6A1IR85g/AdImBezM0ZgUALpf1Jaq3XjZvR9XqRiu/VZHDEJacxep
/Csw9AuLA5D2U8bWBV8URoBIfFzyho+3dYG8zS1l9Ym5CvSP98nryWSH1LwsEBVunoZpVE+TLGsz
A4uui1/y31gO04y44DxZp1Bh/HfIT4woOIOIlBqOGd5Rz+kCAwEAAaNFMEMwHQYDVR0OBBYEFPvn
7D4SQkOFxE2ljtW0AowwwIDPMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMA0G
CSqGSIb3DQEBCwUAA4IBAQBcRs/wwAfkWVbwgVKkucSMnRNb7kPS7qERcUykMGA/46QXhDcDShd1
j2aXbZKx0DNLKpGYuIN4N6uGdHZu0jABaSMOtVYdXCAHdXGPYJB6vV/l3jOOVvOPULwaf8lbBrmM
AuR6F6J1DBiXH+XMuOPB6/Tp9YYSoFJkPqhxqxyns3tjjTXmCIcoEUuPqACniLk6aUzlKFzDUt2N
hp34Qzj4BdH7QepHjR/mcDkVVaMjY597d2f/kAJm0D/l01W3nyKCbDb3yq3w8f6gj1WIIB6o8w9R
HsZwm4eVFYhJWWvi9N2wci8X5PMdDi/abUxhOT7EYEQGk39dfc/VTEQoMKrE</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2:Subject><saml2:NameID Format="http://schemas.xmlsoap.org/claims/UPN">Administrator</saml2:NameID><saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:holder-of-key"><saml2:NameID Format="http://schemas.xmlsoap.org/claims/UPN">vsphere-webclient-21665f80-b6c4-11e4-b9fe-005056a638d3#vsphere.local</saml2:NameID><saml2:SubjectConfirmationData xsi:type="saml2:KeyInfoConfirmationDataType"><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificate>MIID5TCCAs2gAwIBAgIJAMk0TrGWNX/vMA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNVBAMMAkNBMRcw
FQYKCZImiZPyLGQBGRYHdnNwaGVyZTEVMBMGCgmSJomT8ixkARkWBWxvY2FsMQswCQYDVQQGEwJV
UzEQMA4GA1UECgwHcnVjaGF2YzAeFw0xNTAyMTcxNjM1MDhaFw0yNTAyMTExNjQzNDJaMIGMMRow
GAYDVQQDDBF2c3BoZXJlLXdlYmNsaWVudDEXMBUGCgmSJomT8ixkARkWB3ZzcGhlcmUxFTATBgoJ
kiaJk/IsZAEZFgVsb2NhbDELMAkGA1UEBhMCVVMxMTAvBgNVBAsMKG1JRC0yMTY2NWY4MC1iNmM0
LTExZTQtYjlmZS0wMDUwNTZhNjM4ZDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDA
zkp7RK+aOZqq4+yyp/gfsLr4jQnOiLCNGdvEeLXVhUPWogYl0MkHEt3DY6i2HqL0xmmPeRjmOJ1T
62eR3Nc8ugrapKUy7bYgCTT6rzvjU7KtzHg/SncuwncrB53//lSndJ41UtTWNxZSqqja3tmfg3pT
4EQkv0YiyEeayKJhfNz6XiuL12wdBvai0SIEFIsZTq92hNlTs4W58tT8ov6408BEMtRcTVHrOSAS
BS2waelqHAt141PWos3ynz4MUsxRs2p0T77K+wh2Mj/eWQgJJnVVuc4oVA1uLOQHjP777QV/gEkd
p6v42q8b+24LtTWJssMIVvmsmvoEtItDbpApAgMBAAGjeTB3MAsGA1UdDwQEAwIF4DAoBgNVHREE
ITAfgh1ydWNoYXZjLnRlc3RsYWIuY29tbXZhdWx0LmNvbTAdBgNVHQ4EFgQUHR0Ta1eFnWxSD37T
ZFPQncCZYlswHwYDVR0jBBgwFoAU++fsPhJCQ4XETaWO1bQCjDDAgM8wDQYJKoZIhvcNAQELBQAD
ggEBAETECKs16qfadNvLwNysQq5F9Y9pAhnss6PniRLdQ2D7dbKgLNjgi4CIEV3SuaDXaqONV9IV
+IjAg6N+yMqGghc64MyAzDS0Rkp2R7hfNjyYUcG9lNTSpsKSZE0iNb9RWaqrPKu4RsnPvjIStx43
EytkF63Q7ktYxFCXlnB9AVeMa6nfOzFZS+SXHrd+zWs62Hp/9mBHLoHKEYYQawpJlbBnAkg8WZxq
uVE/Ky5Gv8ni3eAovM2g0Ot7gqqbfPH09Yk4L9pBUPw/lT2icBvZ6yHgWxmEnZuHBKUF5B8F0smI
TSCwNY2lUghkxxCdTEaqsthPGb9uYEB6JFJDgblgEBg=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></saml2:SubjectConfirmationData></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore="2015-06-16T19:38:51.295Z" NotOnOrAfter="2015-07-16T19:38:51.295Z"><saml2:ProxyRestriction Count="9"/><saml2:Condition xmlns:del="urn:oasis:names:tc:SAML:2.0:conditions:delegation" xsi:type="del:DelegationRestrictionType"><del:Delegate DelegationInstant="2015-06-16T19:36:37.101Z"><saml2:NameID Format="http://schemas.xmlsoap.org/claims/UPN">vsphere-webclient-21665f80-b6c4-11e4-b9fe-005056a638d3#vsphere.local</saml2:NameID></del:Delegate></saml2:Condition><saml2:Condition xmlns:rsa="http://www.rsa.com/names/2009/12/std-ext/SAML2.0" Count="9" xsi:type="rsa:RenewRestrictionType"/></saml2:Conditions><saml2:AuthnStatement AuthnInstant="2015-06-16T19:38:03.662Z"><saml2:AuthnContext><saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession</saml2:AuthnContextClassRef></saml2:AuthnContext></saml2:AuthnStatement><saml2:AttributeStatement><saml2:Attribute FriendlyName="givenName" Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xsi:type="xs:string">Administrator</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="Groups" Name="http://rsa.com/schemas/attr-names/2009/01/GroupIdentity" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xsi:type="xs:string">SophosAdministrator</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">Administrators</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.localAdministrators</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.localEveryone</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="Subject Type" Name="http://vmware.com/schemas/attr-names/2011/07/isSolution" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xsi:type="xs:string">false</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="surname" Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xsi:type="xs:string"/></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion>
`could someone please help me find the issue here.
Using the default Visual Studio 2013 Web API project template with individual user accounts, and posting to the /token endpoint with an Accept header of application/xml, the server still returns the response in JSON:
{"access_token":"...","token_type":"bearer","expires_in":1209599}
Is there a way to get the token back as XML?
According to RFC6749 the response format should be JSON and Microsoft implemented it accordingly. I found out that JSON formatting is implemented in Microsoft.Owin.Security.OAuth.OAuthAuthorizationServerHandler internal class with no means of extension.
I also encountered the need to have token response in XML.
The best solution I came up with was to implement HttpModule converting JSON to XML when stated in Accept header.
public class OAuthTokenXmlResponseHttpModule : IHttpModule
{
private static readonly string FilterKey = typeof(OAuthTokenXmlResponseHttpModule).Name + typeof(MemoryStreamFilter).Name;
public void Init(HttpApplication application)
{
application.BeginRequest += ApplicationOnBeginRequest;
application.EndRequest += ApplicationOnEndRequest;
}
private static void ApplicationOnBeginRequest(object sender, EventArgs eventArgs)
{
var application = (HttpApplication)sender;
if (ShouldConvertToXml(application.Context.Request) == false) return;
var filter = new MemoryStreamFilter(application.Response.Filter);
application.Response.Filter = filter;
application.Context.Items[FilterKey] = filter;
}
private static bool ShouldConvertToXml(HttpRequest request)
{
var isTokenPath = string.Equals("/token", request.Path, StringComparison.InvariantCultureIgnoreCase);
var header = request.Headers["Accept"];
return isTokenPath && (header == "text/xml" || header == "application/xml");
}
private static void ApplicationOnEndRequest(object sender, EventArgs eventArgs)
{
var context = ((HttpApplication) sender).Context;
var filter = context.Items[FilterKey] as MemoryStreamFilter;
if (filter == null) return;
var jsonResponse = filter.ToString();
var xDocument = JsonConvert.DeserializeXNode(jsonResponse, "oauth");
var xmlResponse = xDocument.ToString(SaveOptions.DisableFormatting);
WriteResponse(context.Response, xmlResponse);
}
private static void WriteResponse(HttpResponse response, string xmlResponse)
{
response.Clear();
response.ContentType = "application/xml;charset=UTF-8";
response.Write(xmlResponse);
}
public void Dispose()
{
}
}
public class MemoryStreamFilter : Stream
{
private readonly Stream _stream;
private readonly MemoryStream _memoryStream = new MemoryStream();
public MemoryStreamFilter(Stream stream)
{
_stream = stream;
}
public override void Flush()
{
_stream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
return _stream.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count)
{
_memoryStream.Write(buffer, offset, count);
_stream.Write(buffer, offset, count);
}
public override string ToString()
{
return Encoding.UTF8.GetString(_memoryStream.ToArray());
}
#region Rest of the overrides
public override bool CanRead
{
get { throw new NotImplementedException(); }
}
public override bool CanSeek
{
get { throw new NotImplementedException(); }
}
public override bool CanWrite
{
get { throw new NotImplementedException(); }
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override long Length
{
get { throw new NotImplementedException(); }
}
public override long Position
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
#endregion
}
Ok I had such a fun time trying to figure this out using OWIN I thought I would share my solution with the community, I borrowed some insight from other posts https://stackoverflow.com/a/26216511/1148288 and https://stackoverflow.com/a/29105880/1148288 along with the concepts Alexei describs in his post. Nothing fancy doing with implementation but I had a requirement for my STS to return an XML formatted response, I wanted to keep with the paradigm of honoring the Accept header, so my end point would examine that to determine if it needed to run the XML swap or not. This is what I am current using:
private void ConfigureXMLResponseSwap(IAppBuilder app)
{
app.Use(async (context, next) =>
{
if (context.Request != null &&
context.Request.Headers != null &&
context.Request.Headers.ContainsKey("Accept") &&
context.Request.Headers.Get("Accept").Contains("xml"))
{
//Set a reference to the original body stream
using (var stream = context.Response.Body)
{
//New up and set the response body as a memory stream which implements the ability to read and set length
using (var buffer = new MemoryStream())
{
context.Response.Body = buffer;
//Allow other middlewares to process
await next.Invoke();
//On the way out, reset the buffer and read the response body into a string
buffer.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(buffer))
{
string responsebody = await reader.ReadToEndAsync();
//Using our responsebody string, parse out the XML and add a declaration
var xmlVersion = JsonConvert.DeserializeXNode(responsebody, "oauth");
xmlVersion.Declaration = new XDeclaration("1.0", "UTF-8", "yes");
//Convert the XML to a byte array
var bytes = Encoding.UTF8.GetBytes(xmlVersion.Declaration + xmlVersion.ToString());
//Clear the buffer bits and write out our new byte array
buffer.SetLength(0);
buffer.Write(bytes, 0, bytes.Length);
buffer.Seek(0, SeekOrigin.Begin);
//Set the content length to the new buffer length and the type to an xml type
context.Response.ContentLength = buffer.Length;
context.Response.ContentType = "application/xml;charset=UTF-8";
//Copy our memory stream buffer to the output stream for the client application
await buffer.CopyToAsync(stream);
}
}
}
}
else
await next.Invoke();
});
}
Of course you would then wire this up during startup config like so:
public void Configuration(IAppBuilder app)
{
HttpConfiguration httpConfig = new HttpConfiguration();
//Highly recommend this is first...
ConfigureXMLResponseSwap(app);
...more config stuff...
}
Hope that helps any other lost souls that find there way to the this post seeking to do something like this!
take a look here i hope it can help how to set a Web API REST service to always return XML not JSON
Could you retry by doing the following steps:
In the WebApiConfig.Register(), specify
config.Formatters.XmlFormatter.UseXmlSerializer = true;
var supportedMediaTypes = config.Formatters.XmlFormatter.SupportedMediaTypes;
if (supportedMediaTypes.Any(it => it.MediaType.IndexOf("application/xml", StringComparison.InvariantCultureIgnoreCase) >= 0) ==false)
{
supportedMediaTypes.Insert(0,new MediaTypeHeaderValue("application/xml"));
}
I normally just remove the XmlFormatter altogether.
// Remove the XML formatter
config.Formatters.Remove(config.Formatters.XmlFormatter);
Add the line above in your WebApiConfig class...
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
// Remove the XML formatter
config.Formatters.Remove(config.Formatters.XmlFormatter);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}