Add Property Value in Message Handler - c#

I want to add property value in message handler of asp.net webapi.
scenario is I implement webapi in which authenticationToken is part of different requests but client ask me to accept token in request header. but some applications are already using this api is sending token in request body.
so i don't want to change implementation. I want to read request header and if it contains header then assign that value to property of request.
is it possible?

Yes, it's possible to modify your request uri & formdata in message handler. Here is an example to do that, you can try this. I've added a class APIKeyHandler to handle this, and added handler in my WebApiConfig. I've uploaded a solution in my Git you can check it from Example WebAPI with Modify Request Body in MessageHandler
Code of WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
System.Web.Routing.RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//add the handler class in WebApiConfig
config.MessageHandlers.Add(new APIKeyHandler());
}
}
Code of APIKeyHandler:
public class APIKeyHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var formData = await request.Content.ReadAsFormDataAsync();
if (request.Method.Method.Equals("POST"))
{
request.Content = new FormUrlEncodedContent(ModifyYourFormData(formData, request));
}
request.RequestUri = new Uri(ModifyYourURI(request.RequestUri.ToString(), request), UriKind.Absolute);
return await base.SendAsync(request, cancellationToken);
}
public IEnumerable<KeyValuePair<string, string>> ModifyYourFormData(NameValueCollection source, HttpRequestMessage request)
{
//Add your logic here
string Authorized = "";
try
{
Authorized = request.Headers.GetValues("AuthorizedKey").FirstOrDefault();
}
catch (Exception ex)
{
}
List<KeyValuePair<string, string>> formData;
formData = source.AllKeys.SelectMany(
source.GetValues,
(k, v) => new KeyValuePair<string, string>(k, v)).ToList();
if (!string.IsNullOrEmpty(Authorized))
{
formData.Insert(0, new KeyValuePair<string, string>("AuthorizedKey", Authorized));
}
return formData;
}
public string ModifyYourURI(string uri, HttpRequestMessage request)
{
//Add your logic here
string Authorized = "";
try
{
Authorized = request.Headers.GetValues("AuthorizedKey").FirstOrDefault();
}
catch (Exception ex)
{
}
if (!string.IsNullOrEmpty(Authorized))
{
return uri + "?AuthorizedKey="+ Authorized;
}
else
{
return uri;
}
}
}
Code of ExampleAPIController:
public class ExampleAPIController : ApiController
{
public async Task<string> Post()
{
string returnValue = "OK";
var formKeyValueData = await Request.Content.ReadAsFormDataAsync();
string AuthorizedKey = formKeyValueData["AuthorizedKey"];
return returnValue;
}
public string Get(string AuthorizedKey)
{
string returnValue = "OK";
return returnValue;
}
}
POST Request Calling Example:
GET Request Calling Example:

one more solution I figure out to add dynamic property in request
if (request.Headers.Contains(HeaderName) && request.Method.Method.Equals("POST"))
{
var formData = await request.Content.ReadAsStringAsync();
dynamic data = JsonConvert.DeserializeObject(formData);
data.authenticationToken = request.Headers.GetValues(HeaderName).FirstOrDefault();
request.Content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
}

Related

Debugging Web API returns Resource no match error

When I try to debug the application by calling like
http://localhost:5/api/GetEmployeesDEV
It returns error like
No HTTP resource was found that matches the request URI 'http://localhost:57764/api/GetEmployeesDEV'. No action was found on the controller 'GetEmployeesDEV' that matches the request.
The ASP.NET Web API which makes a Call to the Odata endpoint and returns the response received by the call. And I have the below code for the Controller
public class GetEmployeesDEVController : ApiController
{
[HttpGet]
private async Task<EmployeeDTO.RootObject> Get()
{
string userName_Core = ConfigurationManager.AppSettings["core_Username"];
string password_Core = ConfigurationManager.AppSettings["core_Password"];
string BaseURL_Core = ConfigurationManager.AppSettings["BaseURL_Core"];
var byteArray_Core = Encoding.ASCII.GetBytes(userName_Core + ":" + password_Core);
EmployeeDTO.RootObject returnObj = new EmployeeDTO.RootObject();
try
{
// GET
using (var client_Core = new HttpClient())
{
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
Uri uri = new Uri(BaseURL_Core);
client_Core.BaseAddress = uri;
client_Core.DefaultRequestHeaders.Accept.Clear();
client_Core.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client_Core.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray_Core));
string core_URL = BaseURL_Core;
var response = client_Core.GetAsync(core_URL).Result;
var responsedata = await response.Content.ReadAsStringAsync();
returnObj = JsonConvert.DeserializeObject<EmployeeDTO.RootObject>(responsedata);
}
}
catch (Exception ex)
{
throw ex;
}
return returnObj;
}
Not sure if I am missing something here. I didnot change anything with RouteConfig file
Please change the scope of the function
From private
private async Task<EmployeeDTO.RootObject> Get()
To public
public async Task<EmployeeDTO.RootObject> Get()
Action need to be public in order to be available for routing.

How to add header in IHttpActionResult in Web API 2?

Hi i am developing API's using Web API 2. I know how to add header when using HttpResponseMessage. Now I am using IHttpActionResult.
Below is my sample current code.
return Content(HttpStatusCode.OK, LoginResponse);
How can I add a header when I am returning content?
Whenever I use HttpResponseMessage I will be having request object and I can add header.
Below code I tried in HttpResponseMessage.
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response.Headers.AddCookies(new[] { cookie });
return response;
In this case where can I add header values?
You can continue to use the HttpResponseMessage as you are accustom to and update the header. After which you can use the IHttpActionResult ResponseMessage(HttpResponseMessage) method to convert to IHttpActionResult
Simple example
public class MyApiController : ApiController {
public IHttpActionResult MyExampleAction() {
var LoginResponse = new object();//Replace with your model
var cookie = new CookieHeaderValue("name", "value");//Replace with your cookie
//Create response as usual
var response = Request.CreateResponse(System.Net.HttpStatusCode.OK, LoginResponse);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response.Headers.AddCookies(new[] { cookie });
//Use ResponseMessage to convert it to IHttpActionResult
return ResponseMessage(response);
}
}
You can create a custom IHttpActionResult which decorates a real one but exposes a way to manipulate the response:
public class CustomResult : IHttpActionResult
{
private readonly IHttpActionResult _decorated;
private readonly Action<HttpResponseMessage> _response;
public CustomResult(IHttpActionResult decorated, Action<HttpResponseMessage> response)
{
_decorated = decorated;
_response = response;
}
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = await _decorated.ExecuteAsync(cancellationToken);
_response(response);
return response;
}
}
Then use this in your action:
return new CustomResult(Content(HttpStatusCode.OK, loginResponse), res => res.Headers.AddCookies(new []{ cookie}));
You can add header by using this code:
HttpContext.Current.Response.AppendHeader("Some-Header", value);
or this
response.Headers.Add("Some-Header", value);

HttpContext.Current is null during test

I have a .NET C# Web API application. I have a single url controller endpoint which receives a POST message. When I run the app and I use an external tool to send the POST message it works perfectly fine.
However, when I trigger the controller from my unit test I get a null ref. exception because for some reason HttpContext.Current is null
This is my current controller (which works in a real scenario):
[HttpPost]
public async Task<IHttpActionResult> Post()
{
await Request.Content.ReadAsStringAsync();
if (Request.Content.IsFormData())
{
var stuff = HttpContext.Current.Request["stuff"];
}
return Ok();
}
}
This is my unit test file:
[TestFixture]
public class AnnotationsControllerTest : BaseIntegrationTest
{
private const string Uri = "http://localhost:2622/api/annotations";
[Test]
public async void TestHistoriesPost()
{
var form = new List<KeyValuePair<string, string>>();
form.Add(new KeyValuePair<string, string>("stuff", "123456"));
using (var request = new HttpRequestMessage(HttpMethod.Post, Uri))
using (var config = new HttpConfiguration())
{
var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
using (var content = new FormUrlEncodedContent(form))
{
request.Content = content;
var mockDataService = GetDataServices();
var controller = new AnnotationsController(mockDataService.Object, ApiTestConfiguration());
SetupController(route, controller, config, request);
var actionResult = await controller.Post();
var httpResponseMessage = await actionResult.ExecuteAsync(CancellationToken.None);
Assert.AreEqual(HttpStatusCode.OK, httpResponseMessage.StatusCode);
}
}
}
private static void SetupController(
IHttpRoute route,
ApiController controller,
HttpConfiguration configuration,
HttpRequestMessage request)
{
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "Annotations" } });
controller.ControllerContext = new HttpControllerContext(configuration, routeData, request);
configuration.Services.Replace(typeof(IExceptionHandler), new UnhandledExceptionHandler());
controller.Request = request;
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = configuration;
}
private Mock<IDataServices> GetDataServices()
{
return new Mock<IDataServices>();
}
}

404 Error when using HttpClient to connect to WebApi with GET

I'm getting a 404 error when trying to use an HttpClient to connect to a WebApi service using GET. However, POST works without any problem. In the code below, I have a CreditCard class that I use throughout.
Here's my routing configuration:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Here's my code that calls the async operation:
Task task1 = RegisterCard(card, false);
Task task2 = FetchCard(cardid, false);
Here's my code that contains the the async operations:
private async Task RegisterCard(CreditCard card, bool runAsync)
{
try
{
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:63801/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = runAsync
? await client.PostAsJsonAsync("api/card", card)
: client.PostAsJsonAsync("api/card", card).Result;
response.EnsureSuccessStatusCode();
}
}
catch (HttpRequestException ex)
{
throw new HttpRequestException(ex.Message, ex.InnerException);
}
}
private async Task FetchCard(int cardid, bool runAsync)
{
CreditCard card = new CreditCard();
try
{
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:63801/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = runAsync
? await client.GetAsync("api/card/" + cardid)
: client.GetAsync("api/card/" + cardid).Result;
response.EnsureSuccessStatusCode();
}
}
}
catch (HttpRequestException ex)
{
throw new HttpRequestException(ex.Message, ex.InnerException);
}
}
Here's my code for the apiController:
[HttpPost]
public HttpResponseMessage Register(CreditCard card)
{
HttpResponseMessage result;
try
{
RegisterResponse response = _cardRepository.Register(card);
result = Request.CreateResponse(HttpStatusCode.Created, response);
}
catch (Exception ex)
{
// TODO: add logging
result = Request.CreateErrorResponse(HttpStatusCode.BadRequest, "failed to register card");
}
finally
{
// TODO: add audit logging of what attempted and who attempted it
}
return result;
}
[HttpGet]
public CreditCard Fetch(int cardid)
{
CreditCard card = new CreditCard();
try
{
card = _cardRepository.Fetch(cardid);
}
catch (Exception ex)
{
// TODO: add logging
}
finally
{
// TODO: add audit logging of what attempted and who attempted it
}
return card;
}
And my code for the CardRepository:
public RegisterResponse Register(Models.CreditCard card)
{
using (CreditCardContext ccContext = new CreditCardContext())
{
card.MaskedNumber = "XXXXXXXXXXXX" + card.Number.Substring(card.Number.Length - 4, 4);
card.Number = Crypto.EncryptData_Aes(card.Number, KeyType.CardNumberKey);
card.CardGuid = Guid.NewGuid().ToString();
ccContext.CreditCards.Add(card);
ccContext.SaveChanges();
}
card.ResetSensitive();
RegisterResponse response = new RegisterResponse
{
IsSuccess = true,
Message = "successfully registered card",
CreditCard = card
};
return response;
}
public CreditCard Fetch(int cardid) // , bool masked
{
CreditCard card;
using (CreditCardContext ccContext = new CreditCardContext())
{
card = ccContext.CreditCards.SingleOrDefault(x => x.Card_ID == cardid);
}
return card;
}
QUESTION: Why am I getting a 404 error when using an HttpClient object to connect to my WebApi service using HttpGet, but when I use HttpPost, it works correctly?
The problem is the parameter naming in your Fetch method.
If you change it to id as per the route specified it should work:
[HttpGet]
public CreditCard Fetch(int id) // , bool masked
{
...
}
Or, alternatively, you could call the api with the named param (e.g. api/card/?cardid=2)
In Web API 2 you can use Attribute Routing
[Route("api/card/{cardid:int}")]
[HttpGet]
public CreditCard Fetch(int cardid)
{
...
}

MVC4 WebApi adding ETag in Response Header

We have a REST Service created in Mvc4
I am trying to add ETag Header in the Response from my WebApi method. It is added in the Header collection without any error but when I check the response header in the Fiddler it is not there.
Here is the method that I used to write header in the response:
internal static HttpResponseMessage<T> GetResponse<T>(Tuple<T, Dictionary<string, string>> response)
{
HttpResponseMessage<T> httpResponse = new HttpResponseMessage<T>(response.Item1, HttpStatusCode.OK);
if (response.Item2 != null)
{
foreach (var responseHeader in response.Item2)
{
if (string.Compare(responseHeader.Key, "ETAG", StringComparison.OrdinalIgnoreCase) == 0)
{
httpResponse.Headers.ETag = new System.Net.Http.Headers.EntityTagHeaderValue("\"" + responseHeader.Value + "\"");
}
else
{
httpResponse.Headers.Add(responseHeader.Key, responseHeader.Value);
}
}
}
return httpResponse;
}
You can do it 2 ways, you can either set the ETag in an ActionFilter.OnActionExecuted method like this:
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) {
actionExecutedContext.ActionContext.Response.Headers.ETag = new EntityTagHeaderValue(...);
}
But there's no way to easily pass the desired value from your controller to the ActionFilter. The second way is to change your WebAPI Action. Instead of returning a model type, return an HttpResponseMessage:
[HttpGet]
public HttpResponseMessage MyActionMethod() {
var result = // response data
var response = Request.CreateResponse<MyType>(HttpStatusCode.OK, result);
response.Headers.Add("Last Modified", result.Modified.ToString("R"));
response.Headers.ETag = new System.Net.Http.Headers.EntityTagHeaderValue(CreateEtag(result));
return response;
}

Categories