Returning 'Allow' Entity Header with HTTPResponseMessage in WCF Web API - wcf

I'm trying to return the 'Allow' entity header within the response message and I keep getting the following message:
{"The header cannot be added. Make sure to add request headers to HttpRequestMessage, response headers to HttpResponseMessage, and content headers to HttpContent objects."}
Here's the code snippet:
[WebInvoke(UriTemplate = "{id}", Method = "DELETE")]
public HttpResponseMessage<Order> DeleteOrder(int id)
{
HttpResponseMessage<Order> response = null;
try
{
if (id <= 0)
{
response = new HttpResponseMessage<Order>(HttpStatusCode.BadRequest);
}
else
{
// For brevity, I'm assuming that order - 123456 was already served and logged. Hence it cannot
// be deleted. Order 12345, however, can be deleted.
// Note: The code doesn't actual delete anything. This is just a demonstration of
// the DELETE verb
if (id == 12345)
{
return new HttpResponseMessage<Order>(HttpStatusCode.NoContent);
}
if (id == 123456)
{
response = new HttpResponseMessage<Order>(HttpStatusCode.MethodNotAllowed);
response.Headers.AddWithoutValidation("Allow", "PUT");
}
// return '404 - Not Found' status code
response = new HttpResponseMessage<Order>(HttpStatusCode.NotFound);
}
return response;
}
catch (Exception ex)
{
return response = new HttpResponseMessage<Order>(HttpStatusCode.InternalServerError);
}
}
Any advice would be very helpful.
Thanks,
dorman

Try response.Content.Headers instead.

Related

How to implement an identical POST like the regular PUT in an odata controller to update an entity

Given the following typical implementation of an ODataController's PUT method, how would I make the exact same method ALSO be available as a POST?
I am developing an OData end-point that will be called from an external system that I have no control over. It appears that that system implements the Update semantics (to tell my system to update an entity) wrongly by sending a POST with a uri key instead of using a PUT.
public async Task<IHttpActionResult> Put([FromODataUri] int key, Product update)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (key != update.Id)
{
return BadRequest();
}
db.Entry(update).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(update);
}
My first guess was to annotate the method with [AcceptVerbs("PUT", "POST")] to make the same exact method implementation be available as a POST, but that doesn't work. It's probably that the ODataConventionModelBuilder default setup doesn't know about this...
Ideally I'd like to keep the standards based PUT and the regular POST for inserts, but add a special post that is identical to the put but differs only in the verb.
Thanks
After finding some not so evident documentation on salesforce.com on odata endpoint implementation for External Data Source/External Objects, it became evident to me that salesforce.com tries to call a POST for Update semantics on the external object but also adds the X-HTTP-METHOD set as PATCH.
So, the solution was to implement the following class:
public class MethodOverrideHandler : DelegatingHandler
{
readonly string[] _methods = { "DELETE", "HEAD", "PUT", "PATCH", "MERGE" };
const string _header1 = "X-HTTP-Method-Override";
const string _header2 = "X-HTTP-Method";//salesforce special behavior???
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// Check for HTTP POST with the X-HTTP-Method-Override header.
if (request.Method == HttpMethod.Post && request.Headers.Contains(_header1))
{
// Check if the header value is in our methods list.
var method = request.Headers.GetValues(_header1).FirstOrDefault();
if (_methods.Contains(method, StringComparer.InvariantCultureIgnoreCase))
{
// Change the request method.
request.Method = new HttpMethod(method);
}
}
else if (request.Method == HttpMethod.Post && request.Headers.Contains(_header2))
{
// Check if the header value is in our methods list.
var method = request.Headers.GetValues(_header2).FirstOrDefault();
if (_methods.Contains(method, StringComparer.InvariantCultureIgnoreCase))
{
// Change the request method.
request.Method = new HttpMethod(method);
}
}
return base.SendAsync(request, cancellationToken);
}
}
and register it in WebApiConfig.Register(HttpConfiguration config) as such:
config.MessageHandlers.Add(new MethodOverrideHandler());
Now, the non-odata compliant POST for salesforce update operations on the External Object will get delegated to the standards compliant odata implementation (in the ODataController) of PUT method I originally posted.
I hope that this helps someone in the future...
My approach would be to throw some more logic into the method to check and see if a record already exists in the database using update.Id then checking whether or not the data is null.
public async Task<IHttpActionResult> Put([FromODataUri] int key, Product update)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
//might have to get rid of this condition for the sake of new entries
//if (key != update.Id)
//{
//return BadRequest();
//}
try
{
//not sure what the name of your table is so I'm going to call it ProductTable
var foo = db.ProductTable.Where(p => p.Id == update.Id).FirstOrDefault();
if(foo == null)
{
db.Entry(update).State = EntityState.Added;
await db.SaveChangesAsync();
return StatusCode(HttpStatusCode.Accepted);
}
else
{
db.Entry(update).State = EntityState.Modified;
await db.SaveChangesAsync();
return Updated(update);
}
}
catch (DbUpdateConcurrencyException ex)
{
if (!ProductExists(key))
{
return NotFound();
}
else
{
throw new DbUpdateConcurrencyException(ex.Message);
}
}
}
EDIT
Just noticed the ProductExists method... I would take that out of the catch block and throw it into the try
//for Post, pass in a 0 for key's argument
public async Task<IHttpActionResult> Put([FromODataUri] int key, Product update)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
//might have to get rid of this condition for the sake of new entries
//if (key != update.Id)
//{
//return BadRequest();
//}
try
{
if (!ProductExists(key))
{
db.Entry(update).State = EntityState.Added;
await db.SaveChangesAsync();
return StatusCode(HttpStatusCode.Accepted);
}
else
{
db.Entry(update).State = EntityState.Modified;
await db.SaveChangesAsync();
return Updated(update);
}
}
catch (DbUpdateConcurrencyException ex)
{
throw new DbUpdateConcurrencyException(ex.Message);
}
}

ServiceStack doesn't populate the response DTO when throwing HttpErrors

ServiceStack doesn't populate the original response in the WebServiceException's responseDTO property.
I'm running the code below which should always return a 404 response code with the ResponseStatus property of the TestResponse populated with "Some bad request" but it also seems like should return the original good response with it's output property populated from the request's input property. However I get null when I look at the WebServiceException responseDTO
public TestResponse Post(Test request)
{
var response = new TestResponse() { Output = request.Input };
throw new HttpError(response, (int)HttpStatusCode.BadRequest, "Some bad request");
}
public TestResponse Get(Test request)
{
try
{
using (var client = new JsonServiceClient("http://localhost:5000"))
{
var response = client.Post(request);
return response;
}
}
catch (WebServiceException ex)
{
throw;
}
}
In general I was expecting that the responseDTO property in the WebServiceException will contain the endpoint's DTO as long as it's passed in when throwing the HttpError but that doesn't seem to be the case. I see only default values and nulls for each property in the responseDTO.
When an Exception is thrown only the ResponseStatus is preserved, you can add any additional info to its Meta dictionary.
Alternatively you can return a failed HTTP Response:
public TestResponse Post(Test request)
{
var response = new TestResponse() { Output = request.Input };
base.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return response;
}

Get missing auditlog from Management Activity API in Office365

Our application calls out-of-the-box Office 365 Management API to retrieve activities and events on files stored in SharePoint Online. However per our experiment, the application can’t seem to retrieve not enough logs.
Example: We upload 1000 files to document library in Sharepoint Online. We receive 8 subscriptiona. Each subscription, we only get maximum 100 logs. Total call API get logs to retrieve 600 logs. Not enough!
Here my code to get subscription
List<SubscriptionsContent> GetSubscriptionsContents(AuthenticationResult authenticationResult, ManagementAPI m, DateTime startDate, DateTime endDate, bool proxyRequired = false)
{
try
{
string jsonSubscription = string.Empty;
string url = string.Empty;
string logType = "Audit.SharePoint";
if (authenticationResult != null)
{
url = string.Format(UrlFormat, m.TenantId, string.Format("subscriptions/content?contentType={0}&startTime={1}&endTime={2}", logType, startDate.ToUniversalTime().ToString(DateFormat), endDate.ToUniversalTime().ToString(DateFormat)));
jsonSubscription = ExecuteRequest(url, HttpMethod.Get, authenticationResult);
//Log.Info("jsonSubscription:");
//Log.Info(jsonSubscription);
}
var listContent = Common.GetListSubscriptionsContent(jsonSubscription);
Log.Info("Common.GetListSubscriptionsContent(jsonSubscription); Count: " + (listContent != null ? listContent.Count.ToString() : "IS NULL"));
return listContent;
}
catch (Exception ex)
{
Log.Error(ex);
return new List<SubscriptionsContent>();
}
}
Here my code to execute Request
public string ExecuteRequest(string url, HttpMethod method, AuthenticationResult token)
{
var responseStr = "";
try
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpRequestMessage request = new HttpRequestMessage(method, url);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);
HttpResponseMessage response = client.SendAsync(request).Result;
Log.Info("ExecuteRequest(string url, HttpMethod method, AuthenticationResult token): response.StatusCode: " + response.StatusCode + " ; response.ReasonPhrase: " + response.ReasonPhrase + " ; response.RequestMessage: " + response.RequestMessage);
if (response.IsSuccessStatusCode)
{
responseStr = response.Content.ReadAsStringAsync().Result;
}
}
catch (Exception ex)
{
Log.Error(ex);
}
return responseStr;
}
Here my code to get audit log from each subscription
List<AuditLog> listAudit = new List<AuditLog>();
foreach (var item in listSubscription)
{
var jsonAudit = ExecuteRequest(item.ContentUri.ToString(), HttpMethod.Get, authenticationResult);
if (string.IsNullOrEmpty(jsonAudit))
continue;
var listAuditLog = Common.GetListAuditLog(jsonAudit);
}
Here my code to parser JsonString
public static List<AuditLog> GetListAuditLog(string jsonString)
{
try
{
return JsonConvert.DeserializeObject<List<AuditLog>>(jsonString);
}
catch (Exception ex)
{
Log.Error("public static List<AuditLog> GetListAuditLog(string jsonString)", ex.InnerException);
return new List<AuditLog>();
}
}
I think that you need to use the pagination header.
If the amount of data is too big, the API will return a header entry named NextPageUrl containing an address to be used to request the next page of results. This link (representing the query) will be available for 24 hours.
Ex.
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
NextPageUrl:https://manage.office.com/api/v1/{tenant_id}/activity/feed/subscriptions/content?contentType=Audit.SharePoint&startTime=2015-10-01&endTime=2015-10-02&nextPage=2015101900R022885001761
So, if the response contains this header entry, just use the value of NextPageUrl to request more data.
Repeat the process until this header entry doesn't exists anymore.
You can find more information in the Office 365 Management API reference

How to receive a response package from GET request for OneNote API

I'm getting a acknowledgement but no response message (details) i.e. list of notebooks from the OneNote API. Below is my code. I am able to receive the header and JSON details from a POST message but not the GET. I have tried to convert the POST code in order to submit a GET request.
private async void getRequestClick(object sender, RoutedEventArgs e)
{
await GetRequests(true, "test");
}
async public Task<StandardResponse> GetRequests(bool debug, string sectionName)
{
Uri PagesEndPoint1 = new Uri("https://www.onenote.com/api/v1.0/notebooks");
var client = new HttpClient();
//// Note: API only supports JSON return type.
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//// This allows you to see what happens when an unauthenticated call is made.
if (IsAuthenticated)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authClient.Session.AccessToken);
}
HttpResponseMessage response;
HttpRequestMessage createMessage = new HttpRequestMessage(HttpMethod.Get, PagesEndPoint1);
response = await client.SendAsync(createMessage);
tbResponse.Text = response.ToString();
return await TranslateResponse(response);
}
private async static Task<StandardResponse> TranslateResponse(HttpResponseMessage response)
{
StandardResponse standardResponse;
if (response.StatusCode == HttpStatusCode.Created)
{
dynamic responseObject = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync());
standardResponse = new CreateSuccessResponse
{
StatusCode = response.StatusCode,
OneNoteClientUrl = responseObject.links.oneNoteClientUrl.href,
OneNoteWebUrl = responseObject.links.oneNoteWebUrl.href
};
}
else
{
standardResponse = new StandardErrorResponse
{
StatusCode = response.StatusCode,
Message = await response.Content.ReadAsStringAsync()
};
}
// Extract the correlation id. Apps should log this if they want to collcet the data to diagnose failures with Microsoft support
IEnumerable<string> correlationValues;
if (response.Headers.TryGetValues("X-CorrelationId", out correlationValues))
{
standardResponse.CorrelationId = correlationValues.FirstOrDefault();
}
return standardResponse;
}
My POST messages are working OK. I can create a new page etc.
I think you need to change the expected status code from HttpStatusCode.Created to HttpStatusCode.OK for Get requests, since they return a 200 and not a 201. Try doing that in your TranslateResponse method.

Restlet: Get response body on failed get()

I am trying to debug a particular connection that responds with a Unauthorized (401). In Restlet this causes Respresentation.get() to throw an error.
What I want to do is get the response body as this particular api gives you greater error information in the body.
Any suggestions?
Thanks,
Luke
I would be reluctant to reccomend it for production but certainly for debugging you can override handleInbound in ClientResource to change the conditions under which an error is thrown. Restlet will then return the body, in the usual way.
ClientResource clientResource = new ClientResource("http://your.url") {
#Override
public Representation handleInbound(Response response) {
Representation result = null;
if (response.getRequest().isSynchronous()) {
if (response.getStatus().isError()
&& !Status.CLIENT_ERROR_UNAUTHORIZED.equals(response.getStatus())) {
doError(response.getStatus());
} else {
result = (response == null) ? null : response.getEntity();
}
}
return result;
}
};
Representation response = clientResource.get();
System.out.println(response.getText());