How to create HttpResponseMessage in a non-APIController - asp.net-web-api

I have a service layer called GatewayService which calls another WebApi to get the product information and manipulate the Inventory in the response and return the same response to the caller.
Here is the code I have. The problem that I have is, I can't use Request.CreateResponse(...) which will give me a compilation error because GatewayService does not inherit ApiController.
Is there a way to update the response and return as HttpResponseMessage?
public class GatewayService
{
// Code Removed for bravity
public HttpResponseMessage Get(SingleProductSearcRequest request)
{
var response = productServiceWebApi.Get(request); // Returns HttpResponseMessage
var p = response.Content.ReadAsAsync<JObject>().Result;
p["Inventory"] = "Not Available";
return Request.CreateResponse(p); // COMPILER ERROR!!!
}
}

Request.CreateResponse() is just an extension method for HttpRequest. You can manually construct an HttpResponseMessage as well as give it content by doing something like:
var p = response.Content.ReadAsAsync<JObject>().Result;
HttpResponseMessage message = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
message.Content = new ObjectContent(p);
You can transfer headers and other information over as well, if necessary. Depending on the need, there's also StringContent, etc.

Related

Posting to WebAPI with ModelClass Object as Parameter

We have web api like below:
[HttpPost]
public CustomAuthenticateModel AuthenticateByUsername(LoginModel model)
{
return employeeService.AuthenticateByUsername(model.Username, model.AdDomain, model.IsAdAuthentication);
}
In my PCL Project I am trying to access via:
try
{
HttpResponseMessage response = null;
LoginModel l = new LoginModel();
l.Username = model.Email;
response = await apiClient.PostAsJsonAsync(uri, l); // Exception is fired at this line
}
catch(exception etc){}
and every time I am getting exception like:
ex = {System.TypeInitializationException: The type initializer for 'System.Net.Http.FormattingUtilities' threw an exception. ---> System.NotImplementedException: The method or operation is not implemented.
at System.Runtime.Serialization.XsdDataContractExporte...
This is an existing project, all API consume Model Class object as parameter. what is the right way to do this? I am trying to use MVVM helper library for this project.
Serialize your object before making a request. Javascript Serializer should work as good as Newtonsoft serializer
var jsonRequest = Newtonsoft.Json.JsonConvert.SerializeObject(argument);
var content = new StringContent(jsonRequest, Encoding.UTF8, "text/json");
//url is the api, l is your object that you are passing
var response = await client.PostAsync(url, l);
if (response.IsSuccessStatusCode)
{
//R is your object type, in this case LoginModel
result = Newtonsoft.Json.JsonConvert.DeserializeObject<R>(await response.Content.ReadAsStringAsync());
}

Execute a simple call to a WebAPI Get using RestRequest and a single string parameter

I have the following code in my receiving controller:
[Route("api/StudentUserId/{string}"), HttpGet]
public StudentDto StudentUserId(string userId)
{
StudentModel sm = new StudentModel(userId);
StudentDto dto = sm.ConvertToDto();
return dto;
}
After running this project, I have another project that I use to test the WebAPI controller calls. I use the following code to read a student record form the database using their userId:
protected T SendRequestToReadItemUsingString<T>(string resource, string userId) where T : new()
{
string resourceString = string.Format("{0}/{{userId}}", resource);
RestRequest request = new RestRequest(resourceString, Method.GET);
request.AddUrlSegment("userId", userId);
RestClient restClient = new RestClient(Service.Location);
var response = restClient.Execute<T>(request);
T retVal = response.Data;
return retVal;
}
Comparible code seems to work if I change the userId to an int Id in both the controller and calling code. I can't seem to get it to work with string. If I place a breakpoint in the controller code it never hits it and the calling code just returns a null.
Thanks for your help
Please note that WebApi works based on reflection this means that your curly braces {vars} must match the same name in your methods.
Therefore to match this api/StudentUserId/{string} your method needs to be declare like this:
[Route("api/StudentUserId/{userId}"), HttpGet]
public StudentDto StudentUserId(string userId)
return userId;
}
Where the parameter {string} was replaced by userId.
If you want to read more about this Routing Rules here is similar post on this;
WebApi Routing Configuration

Referencing Action Parameters from ExceptionLogger

I'm wanting to make use of the new method for globally logging errors. I've written a class that inherits ExceptionLogger and overrides the Log() method. Then registered it as a replacement.
public class TraceExceptionLogger : ExceptionLogger
{
public async override void Log(ExceptionLoggerContext context)
{
// This is always empty string
var content = await context.Request.Content.ReadAsStringAsync();
// This is almost always null
var actionContext = context.ExceptionContext.ActionContext;
}
}
I can dig through the ExceptionLoggerContext object's properties to get pretty much everything I need, EXCEPT for action parameters. There is indeed an ActionContext property but I've only seen it null and this wiki page states that ActionContext and ControllerContext will almost always be null.
Also, I can't get the content stream because its stream is already read before it gets to my logger. So there's no way for me to get any posted json from the request's content.
Is there maybe a way to get the posted data from HttpContext.Current or in some other way?
Ok it looks like I can get the body text from HttpContext by reading InputStream on the Request object like this:
string bodyText = string.Empty;
using (var sr = new StreamReader(HttpContext.Current.Request.InputStream))
{
sr.BaseStream.Seek(0, SeekOrigin.Begin);
bodyText = sr.ReadToEnd();
}
This code has been successful me so far for getting my posted json data.
Here's action parameters for future reference
public class HomeController : ApiController {
public string Get(string id, [FromHeader] Whoever whoever) {
public string Post(Whatever whatever) {
var args = ((ApiController) context.ExceptionContext
.ControllerContext.Controller)).ActionContext.ActionArguments
if (args.ContainsKey("whatever")) {
var whatever = (Whatever)args["whatever"];

How to check attribute of action from HttpRequest

I've followed this Prevent Forms authentication in order to try and handle redirecting from ajax gracefully. However I need to be able to determine if certain attributes are decorating the action that this call was made for as I only want to do this for some occasions. Can I get this information from the HttpRequest object that is accessible within this method?.
Essentially taking the part from the code above that I would like to manipulate:
public class SuppressFormsAuthenticationRedirectModule : IHttpModule {
private void OnPostReleaseRequestState(object source, EventArgs args) {
var context = (HttpApplication)source;
var response = context.Response;
var request = context.Request; // request is HttpRequest
if (response.StatusCode == 401 && request.Headers["X-Requested-With"] ==
"XMLHttpRequest") {
// TODO HERE: Check that the controller action contains a particular attribute
// and if so do not suppress redirect
SuppressAuthenticationRedirect(context.Context);
}
}
}
UPDATE:
It's probably worth noting that this code is held within a compiled DLL project that is then encorporated into a host MVC application (which we don't have access to). In that case I don't really have access to changing default implementations unless I can ensure it doesn't effect the rest of the controllers in the application.
I tried to use as much of the framework as possible, which is why I chose to expose the GetControllerType method from the DefaultControllerFactory. You'll notice that routeData contains the area, controller and action, so with a bit of reflection, you can bypass having to create a derived controller factory.
This is definitely not production ready. It is just a way to get the custom attributes from the requested action.
Edit: instead of setting the current controller factory, create a new DerivedControllerFactory
var httpApplication = (HttpApplication)sender;
var httpContext = new HttpContext(httpApplication.Request, new HttpResponse(new StringWriter()));
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
//var factory = ControllerBuilder.Current.GetControllerFactory() as DerivedControllerFactory;
var factory = new DerivedControllerFactory();
var controllerType = factory.GetControllerType(new RequestContext(new HttpContextWrapper(httpContext), routeData), routeData.Values["controller"].ToString());
var methodInfo = controllerType.GetMethod(routeData.Values["action"].ToString());
var attributes = methodInfo.GetCustomAttributes(true);
public class DerivedControllerFactory : DefaultControllerFactory
{
public new Type GetControllerType(RequestContext requestContext, string controllerName)
{
return base.GetControllerType(requestContext, controllerName);
}
}

httpmessagehandler - reading content

I created a message handler which will log the request and the response. ideally I want to
public class LoggingMessageHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
LogRequest(request);
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
var response = task.Result;
LogResponse(response);
return response;
});
}
private void LogRequest(HttpRequestMessage request)
{
var writer = request.GetConfiguration().Services.GetTraceWriter();
var content = request.Content;
(content ?? new StringContent("")).ReadAsStringAsync().ContinueWith(x =>
{
writer.Trace(request, "request", System.Web.Http.Tracing.TraceLevel.Info, t =>
{
t.Message = x.Result;
});
});
}
private void LogResponse(HttpResponseMessage response)
{
var request = response.RequestMessage;
var writer = request.GetConfiguration().Services.GetTraceWriter();
var content = response.Content;
(content ?? new StringContent("")).ReadAsStringAsync().ContinueWith(x =>
{
writer.Trace(request, "response", System.Web.Http.Tracing.TraceLevel.Info, t =>
{
t.Status = response.StatusCode;
t.Message = x.Result;
});
});
}
}
and here is my client code.
public ActionResult Index()
{
var profile = Client.GetAsync("Vendor").Result.EnsureSuccessStatusCode().Content.ReadAsAsync<VendorProfileModel>().Result;
return View(profile);
}
Logging appears to be working. However, when this handler is registered my client code returns an empty object. If I remove this handler the model is successfully read from the response and displayed on screen.
Is there a way to read the content and display the results on the client?
after a few more days for digging around on the net I finally found the root problem and a solution. First the problem:
everything in webapi is async
my action uses Controller.User which in turn is calling Thread.CurrentPrinciple
I am using ITraceWriter as my logging abstraction
apparently there is a bug in the ITraceWriter mechanicism where the current profile is not propagated across threads. therefore, i loose the principle when i get to my controller action. therefore, my query returns an empty result, rather than a fully populated result.
solution: don't use ITraceWriter to log messages. It would have been nice to use the built in mechanics, but that doesn't work. here is the link to the same issue which provides more detail/context.
https://aspnetwebstack.codeplex.com/workitem/237

Resources