Getting the HttpActionExecutedContext Result values - asp.net-web-api

I have created a filter which inherits the System.Web.Http.Filters.ActionFilterAttribute in the asp.net web api and would like to access some of the data inside the HttpActionExecutedContext result object.
At what stage/when does this object get populated? As I looked at it when overriding the OnActionExecuted method and its always null?
Any ideas?
Edit:
for example here in my custom filter:
public override OnActionExecuted(HttpActionExecutedContext context)
{
//context.Result.Content is always null
base.OnActionExecuted(context);
}

Use this function to get body of request in web api
private string GetBodyFromRequest(HttpActionExecutedContext context)
{
string data;
using (var stream = context.Request.Content.ReadAsStreamAsync().Result)
{
if (stream.CanSeek)
{
stream.Position = 0;
}
data = context.Request.Content.ReadAsStringAsync().Result;
}
return data;
}

Ended up using ReadAsStringAsync on the content result.
I was trying to access the property before the actual request had finished.

While the awarded answer referred to ReadAsStringAsync, the answer had no example. I followed the advice from gdp and derived a somewhat working example...
I created a single class called MessageInterceptor. I did nothing more than derive from ActionFilterAttribute and it immediately started to intercept webAPI method calls prior to the controller getting it, and after the controller finished. Here is my final class. This example uses the XML Serializer to get both the request and response into an XML string. This example finds the request and response as populated objects, this means deserialization has already occurred. Collecting the data from a populated model and serializing into an XML string is a representation of the request and response - not the actual post request and response sent back by IIS.
Code example - MessageInterceptor
using System.IO;
using System.Linq;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Xml.Serialization;
namespace webapi_test
{
public class MessageInterceptor : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
base.OnActionExecuting(actionContext);
var headers = actionContext.Request.Content.Headers.ToString();
var request = actionContext.ActionArguments.FirstOrDefault().Value;
var xml = SerializeXMLSerializer(request, "");
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
var headers = actionExecutedContext.Response.Content.Headers.ToString();
var response = actionExecutedContext.Response.Content.ReadAsStringAsync().Result;
var xml = SerializeXMLSerializer(response, "");
}
public static string SerializeXMLSerializer(object o, string nameSpace)
{
string serializedValue;
var writer = new StringWriter();
XmlSerializer serializer = new XmlSerializer(o.GetType(), nameSpace);
serializer.Serialize(writer, o);
serializedValue = writer.ToString();
return serializedValue;
}
}
}

Use below to read Response string:
public static string GetResponseContent(HttpResponseMessage Response)
{
string rawResponse = string.Empty;
try
{
using (var stream = new StreamReader(Response.Content.ReadAsStreamAsync().Result))
{
stream.BaseStream.Position = 0;
rawResponse = stream.ReadToEnd();
}
}
catch (Exception ex) { throw; }
return rawResponse;
}

Related

Freemarker - compose language switch url

I am using freemarker template engine with Spring and I want to add or replace lang=xxx parameter in the href attribute of link element when rendering page.
The closest solution I have found is following:
<a href="${springMacroRequestContext.getRequestUri()}?lang=en'/>English</a>
But this is not sufficient when I have URL with parameters and fragments because I will miss them. For example http://localhost/sports/search?query=Volleyball&lang=cs#some-fragment results in http://localhost/sports/search?lang=en
How to compose the URL with added or changed lang parameter in freemarker and do not miss any part of requested URL?
I preferred to do it in Java, this simple implementation does not take into account hashes (#) or the presence of the given parameter in the url..
public static String addParamToUrl(String url, String paramName, String paramValue){
StringBuffer buffer = new StringBuffer(url);
//adding separator
if(url.indexOf('?') == -1){
buffer.append('?');
}else if(!url.endsWith("?") && !url.endsWith("&")){
buffer.append('&');
}
buffer.append(paramName);
if(paramValue != null){
buffer.append("=");
buffer.append(URLEncoder.encode(paramValue, "UTF-8"));
}
return buffer.toString();
}
Put this method in a Class (i.e:Utils.java) that can be staticly accessed by Freemarker engine and than:
<#assign url = Utils.addParamToUrl(springMacroRequestContext.getRequestUri(), "lang", "en") />
English
To expose your Utils class you have to customize FreemarkerManager
public class MyFreemarkerManager extends FreemarkerManager {
public MyFreemarkerManager(){
super();
}
#Override
protected void populateContext(ScopesHashModel model, ValueStack stack, Object action, HttpServletRequest request, HttpServletResponse response) {
super.populateContext(model, stack, action, request, response);
try {
BeansWrapper beansWrapper = new BeansWrapperBuilder(Configuration.VERSION_2_3_24).build();
TemplateHashModel staticModels = beansWrapper.getStaticModels();
TemplateHashModel utils = (TemplateHashModel)staticModels.get("com.package.Utils");
model.put("Utils", utils);
} catch (TemplateModelException e) {
//...
}
}
}
Finally you have to tell Spring you are using MyFreemarkerManager as manager for Freemarker.

Web API Validation for Model Bound in GET request

I have created a custom Model Binder to read the data from the URI in a specific format
public ResponseObject Get([FromUri(BinderType = typeof(CustomModelBinder)]ProductFilter product
{...}
public class ProductFilter
{
[Required(ErrorMessage = #"Name is required")]
public string Name { get; set; }
}
public class CustomModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
//Code to convert the uri parameters to object
return true;
}
}
In the above example, i need the name to be passed from the client before executing the Action.
But, I am unable to run the in-built validations on the Product class using this?
Any ideas?
I wrote in a custom action filter and I registered this action filter in the GlobalConfiguration for all the services. The action filter hooks on to onActionExecuting, looks for the validation in the bound arguments.
bool isValid;
foreach (var item in actionContext.ActionArguments)
{
var parameterValue = item.Value;
var innerContext = new ValidationContext(parameterValue);
if(parameterValue != null)
{
var innerContext = new ValidationContext(parameterValue);
isValid = Validator.TryValidateObject(parameterValue, innerContext, results, true);
}
}
//If not valid, throw a HttpResponseException
if(!isValid)
throw new HttpResponseException(HttpStatusCode.BadRequest);
else
base.onActionExecuting(actionContext);
With more tuning, the exact validation message can be retrieved from the validation context and sent as the response message.
I was also able to extend this to having validation attributes on the parameters themselves, thereby giving more flexibility to my Api

In a WebAPI Formatter, how can you get the URL from HttpContent?

In a WebAPI service, we are using a Formatter to read a content parameter on a request. We need access to the URL in order to transform the content parameter correctly. HttpRequestMessage isn't available, and we can't use HttpContext.Current.Request because HttpContext.Current is null. Accessing the HttpRequestMessage on a Read was requested at http://aspnetwebstack.codeplex.com/workitem/82, but this issue was closed because HttpContent is available on a Read. However, I don't know how to get the URL from HttpContent, or even if it's possible.
There is a method called GetPerRequestFormatterInstance on the formatter which you can override to create a new instance of the formatter with the stateful information about the request in it. By the way, this method GetPerRequestFormatterInstance is only called during the request's deserialization stage. Example below:
public class TextPlainFormatter : BufferedMediaTypeFormatter
{
public TextPlainFormatter()
{
this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain"));
}
public HttpRequestMessage CurrentRequest
{
get;
private set;
}
public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType)
{
TextPlainFormatter frmtr = new TextPlainFormatter();
frmtr.CurrentRequest = request;
//Copy from the original formatter instance to the new instance
frmtr.MediaTypeMappings.Clear();
foreach (MediaTypeMapping mediaTypeMapping in this.MediaTypeMappings)
{
frmtr.MediaTypeMappings.Add(mediaTypeMapping);
}
frmtr.RequiredMemberSelector = this.RequiredMemberSelector;
frmtr.SupportedEncodings.Clear();
foreach (Encoding supportedEncoding in this.SupportedEncodings)
{
frmtr.SupportedEncodings.Add(supportedEncoding);
}
frmtr.SupportedMediaTypes.Clear();
foreach (MediaTypeHeaderValue supportedMediaType in this.SupportedMediaTypes)
{
frmtr.SupportedMediaTypes.Add(supportedMediaType);
}
return frmtr;
}

Getting HttpRequest extension method written for MVC to work in Web Api

I'm working with OAuth 2.0 for MVC, a .NET library for Oauth2. I'm building a Web Api project, however, and am hoping to get this library to work with Web Api.
The problem I'm running into is that the library uses two extension methods on the HttpRequestBase that it calls from the controller.
Here are the extension methods:
public static string GetToken(this HttpRequest request)
{
var wrapper = new HttpRequestWrapper(request);
return GetToken(wrapper);
}
public static string GetToken(this HttpRequestBase request)
{
if (request == null)
return String.Empty;
// Find Header
var headerText = request.Headers[OAuthConstants.AuthorzationHeader];
if (!String.IsNullOrEmpty(headerText))
{
var header = new AuthorizationHeader(headerText);
if (string.Equals(header.Scheme, "OAuth", StringComparison.OrdinalIgnoreCase))
return header.ParameterText.Trim();
}
// Find Clean Param
var token = request.Params[OAuthConstants.AuthorzationParam];
return !String.IsNullOrEmpty(token)
? token.Trim()
: String.Empty;
}
In the MVC project, they simply call Request.GetToken() from the controller. Of course, Web Api's request is an HttpRequestMessage. I'm afraid addressing the difference between HttpRequest and HttpRequest message is beyond my capabilities right now.
Can I convert this extension method to work with HttpRequestMessage or somehow make it work in Web Api??
Thanks!
All the properties you used to have are still available (assuming the OAuthConstants.AuthorzationParam is set on the query string?)
using System;
using System.Linq;
using System.Net.Http;
namespace YourApp
{
public static class Extensions
{
public static string GetToken(this HttpRequestMessage request)
{
if (request == null)
return String.Empty;
// Find Header
var headerText = request.Headers.GetValues(OAuthConstants.AuthorzationHeader).SingleOrDefault();
if (!String.IsNullOrEmpty(headerText))
{
//Brevity...
}
// Find Clean Param
var token = request.GetQueryNameValuePairs().SingleOrDefault(x => x.Key == OAuthConstants.AuthorzationParam).Value;
return !String.IsNullOrEmpty(token)
? token.Trim()
: String.Empty;
}
}
}
Controller
using System.Collections.Generic;
using System.Web.Http;
using YourApp;
namespace YourApp.Controllers
{
public class FoosController : ApiController
{
public IEnumerable<string> Get()
{
var token = Request.GetToken();
return null;
}
}
}

How to get HttpRequestMessage instead of HttpContext.Current in WebApi

I have found several sources that say that you should not use HttpContext.Current in WebApi but none that say how you should handle those cases where we used to use HttpContext.Current.
For example, I have a LinkProvider class that creates links for an object. (simplified to stay on topic).
public abstract class LinkProvider<T> : ILinkProvider<T>
{
protected ILink CreateLink(string linkRelation, string routeName, RouteValueDictionary routeValues)
{
var context = System.Web.HttpContext.Current.Request.RequestContext;
var urlHelper = new System.Web.Mvc.UrlHelper(context);
var url = string.Format("{0}{1}", context.HttpContext.Request.Url.GetLeftPart(UriPartial.Authority), urlHelper.RouteUrl(routeName, routeValues));
///...
return new Link(linkRelation, url);
}
}
and this class is used by a MediaTypeFormatter.
This class is expected to build a link using the same host that came from the original request and leveraging any route values that were on the original request.
But... how do I get a hold of the HttpRequestMessage? This will be encapsulated by a MediaTypeFormatter - but it doesn't have one either.
There must be an easy way to get hold of the HttpRequestMessage - what am I overlooking?
thanks
Jon
I ended up creating the following base Formatter which exposes the request, now I will be able to pass it along to the LinkProvider.
public class JsonMediaTypeFormatterBase : JsonMediaTypeFormatter
{
public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, System.Net.Http.HttpRequestMessage request, MediaTypeHeaderValue mediaType)
{
Request = request;
return base.GetPerRequestFormatterInstance(type, request, mediaType);
}
protected HttpRequestMessage Request
{
get;
set;
}
}

Resources