Method not allowed in WCF Rest service - ajax

[OperationContract]
[WebInvoke(UriTemplate = "createinvoice", Method = "POST", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
public Invoice CreateInvoice(string instance)
{
// TODO: Add the new instance of SampleItem to the collection
try
{
string icode = instance;
//decimal paid = instance.AmountPaid;
return new Invoice() {InvoiceCode = icode };
}
catch( Exception )
{
throw new NotImplementedException();
}
}
Everytime i run it on the browser it says:
Method not allowed. Please see the service help page for constructing valid requests to the service.
Any ideas? Also when i go and do this on the browser. it says Endpoint not found. (Mobile) is a virtual directory while (POS) is a registered route for service1.cs

Posting to the URL from browser will not work. You need your custom code or use fiddler(use Composer and select POST) Another link with solution.

The answer is under "Everytime i run it on the browser it says:"
Your web browser request is a GET request .You can change WebInvoke to WebGet and remove POST Method attribute or build a POST request using a tool.

Related

Access TempData within custom middleware

I have custom middleware that provides global error handling. If an exception is caught it should log the details with a reference number. I then want to redirect the user to an error page and only show the reference number. My research shows that TempData should be ideal for this but it only seems to be accessible from within a controller context. I tried adding the reference number to HttpContext.Items["ReferenceNumber"] = Guid.NewGuid();
But this value is lost through the redirect.
How can middleware pass information through a redirect? Do I just have to put the number in a querystring?
Inside the middleware class you need to add a reference to get access to the required interfaces. I have this middleware in a separate project and needed to add this NuGet package.
using Microsoft.AspNetCore.Mvc.ViewFeatures;
This then allows you to request the correct services within the middleware.
//get TempData handle
ITempDataDictionaryFactory factory = httpContext.RequestServices.GetService(typeof(ITempDataDictionaryFactory)) as ITempDataDictionaryFactory;
ITempDataDictionary tempData = factory.GetTempData(httpContext);
After you have ITempDataDictionary you can use it like you would use TempData within a controller.
//pass reference number to error controller
Guid ReferenceNum = Guid.NewGuid();
tempData["ReferenceNumber"] = ReferenceNum.ToString();
//log error details
logger.LogError(eventID, exception, ReferenceNum.ToString() + " - " + exception.Message);
Now when I get the the controller after a redirect I have no issues pulling out the reference number and using it in my view.
//read value in controller
string refNum = TempData["ReferenceNumber"] as string;
if (!string.IsNullOrEmpty(refNum))
ViewBag.ReferenceNumber = refNum;
#*display reference number if one was provided*#
#if (ViewBag.ReferenceNumber != null){<p>Reference Number: #ViewBag.ReferenceNumber</p>}
Once you put this all together, you give users a reference number that they can give you to help troubleshoot the problem. But, you are not passing back potentially sensitive error information which could be misused.
You can register an ITempDataProvider yourself and use it in your middleware. Here is a small sample I got working between two simple paths. If you are already using MVC the ITempDataProvider is probably already registered. The issue I faced was the path of the cookie that was written. It was /page1 so /page2 did not have access to the cookie. So I had to override the options as you can see in code below.
I hope this will help you :)
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IDataProtectionProvider>(s => DataProtectionProvider.Create("WebApplication2"));
services.Configure<CookieTempDataProviderOptions>(options =>
{
options.Path = "/";
});
services.AddSingleton<ITempDataProvider, CookieTempDataProvider>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ITempDataProvider tempDataProvider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Map("/page1", (app1) =>
{
app1.Run(async context =>
{
tempDataProvider.SaveTempData(context, new Dictionary<string, object> { ["Message"] = "Hello from page1 middleware" });
await context.Response.WriteAsync("Hello World! I'm page1");
});
});
app.Map("/page2", (app1) =>
{
app1.Run(async context =>
{
var loadTempData = tempDataProvider.LoadTempData(context);
await context.Response.WriteAsync("Hello World! I'm page2: Message from page1: " + loadTempData["Message"]);
});
});
}
This led me in the right direction: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state#cookie-based-tempdata-provider
Happy coding! :)

Session id issue: Different id for vf and developer console

I am facing following issue:
On load of visualforce page I am making http callout to internal salesforce page but here I facing authentication problem.
If I am running same http callout from developer console then I am getting successful response but that same code is not working with visualforce page. Reason for not working is my session id in developer console and visualforce domain is different.
For fetching session id I am using "UserInfo.getSessionId()"
I have also tried {!$Api.Session_ID} but not working
My controller:
public with sharing class HttpRequestForPage
{
public HttpRequestForPage()
{
requestForPage('https://ap1.salesforce.com/home/home.jsp');
}
public void requestForPage(String pageUrl)
{
HttpResponse responseOfPage;
String responseString;
HttpRequest request = new HttpRequest();
request.setMethod('GET');
request.setEndpoint('https://ap1.salesforce.com/home/home.jsp');
request.setHeader('Cookie', 'sid='+UserInfo.getSessionId());
try
{
responseOfPage = new Http().send(request);
}
catch(Exception e)
{
system.debug(e);
}
responseString = responseOfPage.getBody();
System.debug(responseString=='+responseString);
}
}
Rather than setting a cookie for the session try using the authorization header.
request.setHeader('Authorization','Bearer '+UserInfo.getSessionId());
You will also need to set ap1.salesforce.com as an endpoint in the remote site settings.
If you are requesting pages from within Salesforce you can just use the PageReference getContent() method.
PageReference home = new PageReference('https://ap1.salesforce.com/home/home.jsp');
blob homeblob = home.getContent();
string homeContent = homeblob.toString();

WebApi Odata Windows Store App EndSaveChanges exception

I am trying to create a Windows Store App using a WebApi Odata controller. After some effort I have all the Get requests working, I am now moving onto the CRUD methods, and am getting the following Exception on the EndSaveChanges of the Data Service Context.
<?xml version="1.0" encoding="utf-8"?>
<m:error xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<m:code />
<m:message xml:lang="en-US">No HTTP resource was found that matches the request URI 'http://localhost:56317/odata/ESFClients(guid'f04ad636-f896-4de4-816c-388106cd39ce')'.</m:message>
<m:innererror>
<m:message>No routing convention was found to select an action for the OData path with template '~/entityset/key'.</m:message>
<m:type></m:type>
<m:stacktrace></m:stacktrace>
</m:innererror>
</m:error>
Now I think this is a bug in WebApi from this http://aspnetwebstack.codeplex.com/workitem/822 and its hiding the actual error. To make sure it wasn't my Odata Endpoint I created a quick console app to get an entry, update it and Patch it back, which worked all ok. My WebApi Odata Controller derives from ODataController with
public HttpResponseMessage Patch([FromODataUri] Guid key, Delta<ESFClient> patch)
As the method.
In my windows application I have a extension method on the DataServiceContext for the Save Changes.
public static async Task<DataServiceResponse> SaveChangesAsync(this DataServiceContext context, SaveChangesOptions options)
{
var queryTask = Task.Factory.FromAsync<DataServiceResponse>(context.BeginSaveChanges(options, null, null),
queryAsyncResult =>
{
var results = context.EndSaveChanges(queryAsyncResult);
return results;
});
return await queryTask;
}
And calling the update like so from a blank Windows Store XAML page.
public async Task UpdateWeekNo()
{
var container = new ESFOdataService.Container(new Uri("http://localhost:56317/odata/"));
var clients = (DataServiceQuery<ESFClient>)from p in container.ESFClients where p.UserID == new Guid("f04ad636-f896-4de4-816c-388106cd39ce") select p;
var result = await clients.ExecuteAsync();
var updatedClient = result.Single();
if (updatedClient != null)
{
updatedClient.WeekNo = 19;
container.UpdateObject(updatedClient);
await container.SaveChangesAsync(SaveChangesOptions.PatchOnUpdate); // Use PATCH not MERGE.
}
}
So does anyone come across the same issue, or know how I can find out the actual error. One interesting point is that if I debug the controller while running the Windows App, the patch method does not get called.
Ok, so I have finally solved this. Just a recap for those who could experience the same issue. I have an Odata WebApi controller, Windows 8 Store Application using WCF Client Library, with the reference created from Add Service Reference. When trying to update (patch) a record an exception was being thrown at the EndSaveChanges. This is because for some reason Post Tunneling is enabled by default on my context. Setting this to false allowed everything to work.
Context.UsePostTunneling = false;
Context.IgnoreResourceNotFoundException = true;

Serialization error in service stack when using client library

I have a ServiceStack REST service (PUT and POST) which I have tested with fiddler and if no errors are raised I return
new HttpResult(HttpStatusCode.OK);
Now I am testing the same REST service with the service stack client, I have:
var client = new XmlServiceClient("url"));
client.Post<ChangeServerLicenseDto>("", new ChangeServerLicenseDto()
{ServerName = model.ServerName});
and I get the exception on the REST service when I do
return new HttpResult(HttpStatusCode.OK)
and the error raised is :
500 (Error in line 1 position 76. Expecting element 'ChangeServerLicense'
from namespace ''.. Encountered 'Element' with name 'HttpStatusCode',
namespace 'http://schemas.datacontract.org/2004/07/System.Net'.)
My client code is in a MVC action method (POST).
My datacontract for the RestService is :
[DataContract(Name = "ChangeServerLicense", Namespace = "")]
[RestService("url", "POST", "application/xml")]
public class ChangeServerLicenseDto
{
[DataMember(Name = "ServerName", Order = 1)]
public string ServerName { get; set; }
}
The convention of signalling a successful response is to return an empty Response DTO (which by default returns a 200 OK). Also Send<TResponse>(...) does a POST so if you don't want to include the url in the request, use Send which will POST the request to the automatic pre-defined routes:
var client = new XmlServiceClient("url"));
client.Send<ChangeServerLicenseDtoResponse>(
new ChangeServerLicenseDto {ServerName = model.ServerName});
Otherwise if you still want to use .Post<T>(...) include the URL for the custom route where your services is mounted.
Note: I generally dislike using Dto suffixes on DTOs which are the most important API in your service - I explain in a bit more detail why here.

Windows Service Hosting WCF Objects over SSL (https) - Custom JSON Error Handling Doesn't Work

I will first show the code that works in a non-ssl (http) environment. This code uses a custom json error handler, and all errors thrown, do get bubbled up to the client javascript (ajax).
// Create webservice endpoint
WebHttpBinding binding = new WebHttpBinding();
ServiceEndpoint serviceEndPoint = new ServiceEndpoint(ContractDescription.GetContract(Type.GetType(svcHost.serviceContract + ", " + svcHost.assemblyName)), binding, new EndpointAddress(svcHost.hostUrl));
// Add exception handler
serviceEndPoint.Behaviors.Add(new FaultingWebHttpBehavior());
// Create host and add webservice endpoint
WebServiceHost webServiceHost = new WebServiceHost(svcHost.obj, new Uri(svcHost.hostUrl));
webServiceHost.Description.Endpoints.Add(serviceEndPoint);
webServiceHost.Open();
I'll also show you what the FaultingWebHttpBehavior class looks like:
public class FaultingWebHttpBehavior : WebHttpBehavior
{
public FaultingWebHttpBehavior()
{
}
protected override void AddServerErrorHandlers(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Clear();
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(new ErrorHandler());
}
public class ErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
// Build an object to return a json serialized exception
GeneralFault generalFault = new GeneralFault();
generalFault.BaseType = "Exception";
generalFault.Type = error.GetType().ToString();
generalFault.Message = error.Message;
// Create the fault object to return to the client
fault = Message.CreateMessage(version, "", generalFault, new DataContractJsonSerializer(typeof(GeneralFault)));
WebBodyFormatMessageProperty wbf = new WebBodyFormatMessageProperty(WebContentFormat.Json);
fault.Properties.Add(WebBodyFormatMessageProperty.Name, wbf);
}
}
}
[DataContract]
public class GeneralFault
{
[DataMember]
public string BaseType;
[DataMember]
public string Type;
[DataMember]
public string Message;
}
The AddServerErrorHandlers() method gets called automatically, once webServiceHost.Open() gets called. This sets up the custom json error handler, and life is good :-)
The problem comes, when we switch to and SSL (https) environment. I'll now show you endpoint creation code for SSL:
// Create webservice endpoint
WebHttpBinding binding = new WebHttpBinding();
ServiceEndpoint serviceEndPoint = new ServiceEndpoint(ContractDescription.GetContract(Type.GetType(svcHost.serviceContract + ", " + svcHost.assemblyName)), binding, new EndpointAddress(svcHost.hostUrl));
// This exception handler code below (FaultingWebHttpBehavior) doesn't work with SSL communication for some reason, need to resarch...
// Add exception handler
serviceEndPoint.Behaviors.Add(new FaultingWebHttpBehavior());
//Add Https Endpoint
WebServiceHost webServiceHost = new WebServiceHost(svcHost.obj, new Uri(svcHost.hostUrl));
binding.Security.Mode = WebHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
webServiceHost.AddServiceEndpoint(svcHost.serviceContract, binding, string.Empty);
Now, with this SSL endpoint code, the service starts up correctly, and wcf hosted objects can be communicated with just fine via client javascript. However, the custom error handler doesn't work. The reason is, the AddServerErrorHandlers() method never gets called when webServiceHost.Open() is run.
So, can anyone tell me what is wrong with this picture? And why, is AddServerErrorHandlers() not getting called automatically, like it does when I'm using non-ssl endpoints?
Thanks!
I will refer you to MSDN docs
If the Transport value is specified by
the
WebHttpBinding(WebHttpSecurityMode),
then the settings provided by the
Transport property become effective
for the service endpoint. The value of
WebHttpSecurityMode can only be set in
the WebHttpBinding constructor that
takes it as an explicit parameter and
its value cannot be set again after
the binding instance is created.
see : http://msdn.microsoft.com/en-us/library/bb348328.aspx
So you need to pass this value
binding.Security.Mode = WebHttpSecurityMode.Transport;
into your .ctor() like that
WebHttpBinding binding = new WebHttpBinding(WebHttpSecurityMode.Transport);
I have never used this before as I always declare my bindings into web.config file but according to MSDN, this is what you should do.

Resources