I have a code
[HttpGet]
public IHttpActionResult Search()
{
return Ok(Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(db.view_users.Take(1)))));
}
I want send to client in base64, but it hard if i must convert manually in every return.
if there a way so i can automatically convert every return to base 64, how to do that?
so i can just use natural code like this :
[HttpGet]
public IHttpActionResult Search()
{
return Ok(db.view_users.Take(1));
}
but still return the same result in base64
thanks
You can create ActionFilter like this:
public class Base64FilterAttribute : ActionFilterAttribute
{
public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{
if (actionExecutedContext.Exception == null)
{
var bytes = await actionExecutedContext.Response.Content.ReadAsByteArrayAsync();
var base64 = Convert.ToBase64String(bytes);
actionExecutedContext.Response.Content = new StringContent(base64);
}
await base.OnActionExecutedAsync(actionExecutedContext, cancellationToken);
}
}
and then mark you controllers with this attribute or register as global filter.
If you are using OWIN You can also create OWIN midelware for that https://learn.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/.
Related
I have a ASP.NET Core Web API and I'm having problems receiving my parameter in my controller method. I do receive the request parameter in the RetrieveMultipleEmployees method, but the Where property is null.
The sequence is as follows:
Create the StandardRequest<Employee> with the Where property defined.
Call the RetrieveMultipleEmployeesAsync method and pass the created StandardRequest<Employee>.
The RetrieveMultipleEmployeesAsync calls the RetrieveMultipleEmployeesRoute method and passes the request along.
The RetrieveMultipleEmployees controller method gets hit, the parameter is not null but the Where property is null.
Here is what I have:
Base controller:
[ApiController]
[Route("data/v{version:apiVersion}/[controller]/{action}")]
public class BaseController<TController> : ControllerBase
{
private IMediator _mediatorInstance;
protected IMediator _mediator => _mediatorInstance ??= HttpContext.RequestServices.GetService<IMediator>();
private ILogger<TController> _loggerInstance;
protected ILogger<TController> _logger => _loggerInstance ??= HttpContext.RequestServices.GetService<ILogger<TController>>();
}
EmployeesController:
public class EmployeesController : BaseController<EmployeesController>
{
[HttpGet]
[ActionName("retrievemultipleemployees")]
public async Task<IActionResult> RetrieveMultipleEmployees([FromQuery] StandardRequest<Employee> request)
{
var response = await _mediator.Send(new EmployeeQueries.RetrieveMultipleQuery() { Request = request });
return Ok(response);
}
}
StandardRequest:
public class StandardRequest<TEntity>
{
public Expression<Func<TEntity, bool>> Where { get; set; }
}
Url:
public static string RetrieveMultipleEmployeesRoute(StandardRequest<Employee> request)
{
var url = $"data/v1/employees/retrievemultipleemployees?{request}";
return url;
}
Request:
public async Task<StandardResult<List<EmployeeModel>>> RetrieveMultipleEmployeesAsync(StandardRequest<Employee> request)
{
var response = await _httpClient.GetAsync(EmployeeRoutes.RetrieveMultipleEmployeesRoute(request));
return await response.ToStandardResultAsync<List<EmployeeModel>>();
}
Where am I going wrong? Might it be something in my API setup?
Some advise on this would be greatly appreciated.
This bit of code looks suspect:
public static string RetrieveMultipleEmployeesRoute(StandardRequest<Employee> request)
{
var url = $"data/v1/employees/retrievemultipleemployees?{request}";
return url;
}
That is simply going to call ToString() on request, resulting in something like this being returned (assuming you haven't overridden it to create an actual query string):
data/v1/employees/retrievemultipleemployees?StandardRequest`[Employee]
Which is clearly bogus. You're going to need to convert that incoming request into a proper query string using something like QueryString.Create for example.
Is recommended to put your ComplexObject into a Class Library common to both projects the Client and the API
REQUESTOR
using Newtonsoft.Json;
using System.Net.Http.Headers;
private readonly string ExtractEpridDbcisinfo = "http://localhost:5281/domething";
public void consumeAPI(){
Uri uri = new Uri(*yourBaseURI*);
client.BaseAddress = uri;
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
string sParam1= JsonConvert.SerializeObject(complexobject,typeof(ComplexObject) ,null);
HttpResponseMessage response = client.GetAsync($"?paramComplexObject={sParam1}").Result;
.
.
.
GET REST API
[HttpGet]
[Route("domething")]
public IActionResult Index(string paramComplexObject){
ComplexObject complexObject = JsonConvert.DeserializeObject<ComplexObject>(paramComplexObject);
.
.
.
Is there a way to extend IActionResult so that all controller methods return same response object (ex: CommonActionResult) with
HttpResponseCode, Message and Response object (ex: ObjectOne, ObjectTwo based on the methods initiated)?
[HttpGet]
public ActionResult<ObjectOne> Get(string id)
{
}
[HttpGet]
public ActionResult<ObjectTwo> Get(string name)
{
}
[HttpPost]
public IActionResult Post(AppPost app)
{
}
How do I extend this?
public class CommonActionResult : IActionResult
{
}
If you just want a generic return type, use IActionResult. That will accept any action result type, such as OkResult, OkObjectResult, BadRequestResult, NotFoundResult, etc.
ActionResult<T> is just a helper that wraps objects in a result, allowing you to return an object directly, instead of having to wrap it in a result first (i.e. return obj vs return Ok(obj). However, when you use that, then you must return that specific object type: the type passed into the T generic type param. If you need to return different types, then use IActionResult as the return in your method signature, and then just wrap the object in something like Ok.
public IActionResult FooOrBar()
{
...
if (something)
return Ok(foo);
else
return Ok(bar);
}
you can implement IHttpAction based on you requirements
it's a simple sample :
public class CustomResult<T> : IHttpActionResult
{
private readonly HttpRequestMessage _request;
private readonly T _content;
public CustomResult(HttpRequestMessage request, T content)
{
_request = request;
_content = content;
}
public CustomResult(HttpRequestMessage request)
{
_request = request;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
if (_content==null)
{
var response =_request.CreateResponse(HttpStatusCode.NoContent, JsonMediaTypeFormatter.DefaultMediaType);
}else
{
var jsonData = JsonConvert.SerializeObject(_content, _content.GetType(), new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Formatting = Formatting.Indented
});
var response = _request.CreateResponse(HttpStatusCode.OK, jsonData, JsonMediaTypeFormatter.DefaultMediaType);
response.Content = new StringContent(jsonData, Encoding.UTF8, "application/json");
}
return Task.FromResult(response);
}
}
also you can add extra information to response header look at this sample for more information
https://www.strathweb.com/2013/06/ihttpactionresult-new-way-of-creating-responses-in-asp-net-web-api-2/
Until now, I had a GET method that looked like the following:
protected override async Task<IHttpActionResult> GetAll(QueryData query)
{
// ... Some operations
//LINQ Expression based on the query parameters
Expression<Func<Entity, bool>> queryExpression = BuildQueryExpression(query);
//Begin to count all the entities in the repository
Task<int> countingEntities = repo.CountAsync(queryExpression);
//Reads an entity that will be the page start
Entity start = await repo.ReadAsync(query.Start);
//Reads all the entities starting from the start entity
IEnumerable<Entity> found = await repo.BrowseAllAsync(start, queryExpression);
//Truncates to page size
found = found.Take(query.Size);
//Number of entities returned in response
int count = found.Count();
//Number of total entities (without pagination)
int total = await countingEntities;
return Ok(new {
Total = total,
Count = count,
Last = count > 0 ? GetEntityKey(found.Last()) : default(Key),
Data = found.Select(e => IsResourceOwner(e) ? MapToOwnerDTO(e) : MapToDTO(e)).ToList()
});
}
This worked like a charm and it was good. However, I was told recently to send the response metadata (that is, Total, Count and Last properties) as response custom headers instead of the response body.
I cannot manage to access the Response from the ApiController. I thought of a filter or attribute, but how would I get the metadata values?
I can keep all this information on the response and then have a filter that will deserialize the response before being sent to the client, and create a new one with the headers, but that seems troublesome and bad.
Is there a way to add custom headers directly from this method on an ApiController?
You can explicitly add custom headers in a method like so:
[HttpGet]
[Route("home/students")]
public HttpResponseMessage GetStudents()
{
// Get students from Database
// Create the response
var response = Request.CreateResponse(HttpStatusCode.OK, students);
// Set headers for paging
response.Headers.Add("X-Students-Total-Count", students.Count());
return response;
}
For more information read this article: http://www.jerriepelser.com/blog/paging-in-aspnet-webapi-http-headers/
I have entered comments, here is my complete answer.
You will need to create a custom filter and apply that to your controller .
public class CustomHeaderFilter : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var count = actionExecutedContext.Request.Properties["Count"];
actionExecutedContext.Response.Content.Headers.Add("totalHeader", count);
}
}
In your Controller
public class AddressController : ApiController
{
public async Task<Address> Get()
{
Request.Properties["Count"] = "123";
}
}
Simple solution is to write just this:
HttpContext.Current.Response.Headers.Add("MaxRecords", "1000");
What you need is:
public async Task<IHttpActionResult> Get()
{
var response = Request.CreateResponse();
response.Headers.Add("Lorem", "ipsum");
return base.ResponseMessage(response);
}
I hope this answers your question.
Alternatively, it’s better to leverage DelegatingHandler if it is something you need to perform on every response. Because it will work on the request/response pipeline and not on the controller/action level. In my case I must add some headers with every response, so I did what I described. See code snippet below
public class Interceptor : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
response.Headers.Add("Access-Control-Allow-Origin", "*");
response.Headers.Add("Access-Control-Allow-Methods", "GET,POST,PATCH,DELETE,PUT,OPTIONS");
response.Headers.Add("Access-Control-Allow-Headers", "Origin, Content-Type, X-Auth-Token, content-type");
return response;
}
}
And you would be requiring to add this handler in WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MessageHandlers.Add(new Interceptor());
}
}
You can use a custom ActionFilter that will allow you to send custom headers and access the HttpContext:
public class AddCustomHeaderFilter : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
actionExecutedContext.Response.Content.Headers.Add("name", "value");
}
}
I'm looking at the documentation of WebAPI 2, and i'm severely disappointed with the way the action results are architected. I really hope there is a better way.
So documentation says I can return these:
**void** Return empty 204 (No Content)
**HttpResponseMessage** Convert directly to an HTTP response message.
**IHttpActionResult** Call ExecuteAsync to create an HttpResponseMessage, then convert to an HTTP response message.
**Other type** Write the serialized return value into the response body; return 200 (OK).
I don't see a clean way to return an array of items with custom HTTP status code, custom headers and with auto negotiated content though.
What I would like to see is something like
public HttpResult<Item> Post()
{
var item = new Item();
var result = new HttpResult<Item>(item, HttpStatusCode.Created);
result.Headers.Add("header", "header value");
return result;
}
This way I can glance over a method and immediately see whats being returned, and modify status code and headers.
The closest thing I found is NegotiatedContentResult<T>, with weird signature (why does it need an instance of controller?), but there's no way to set custom headers?
Is there a better way ?
The following code should give you everything you want:
[ResponseType(typeof(Item))]
public IHttpActionResult Post()
{
var item = new Item();
HttpContext.Current.Response.AddHeader("Header-Name", "Header Value");
return Content(HttpStatusCode.Created, item);
}
... if you really need to return an array of items ...
[ResponseType(typeof(List<Item>))]
public IHttpActionResult Post()
{
var items = new List<Item>();
// Do something to fill items here...
HttpContext.Current.Response.AddHeader("Item-Count", items.Count.ToString());
return Content(HttpStatusCode.Created, items);
}
I don't think the designers of the web-api intended for controller methods to be fiddling with the headers.
The design pattern seems to be to use DelegatingHandler, ActionFilterAttribute and the ExecuteAsync overridable method of ApiController to handle authentication and response formatting.
So perhaps your logic for message content negotiation should be handled there ?
However if you definitely need to control headers from within your controller method you can do a little set-up to make it work.
To do so you can create your own DelegationHandler that forwards selected headers from your "Inner" response headers:
public class MessageHandlerBranding : DelegatingHandler {
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
//If we want to forward headers from inner content we can do this:
if (response.Content != null && response.Content.Headers.Any())
{
foreach (var hdr in response.Content.Headers)
{
var keyUpr = hdr.Key.ToUpper(); //Response will not tolerate setting of some header values
if ( keyUpr != "CONTENT-TYPE" && keyUpr != "CONTENT-LENGTH")
{
string val = hdr.Value.Any() ? hdr.Value.FirstOrDefault() : "";
response.Headers.Add(hdr.Key, val);
}
}
}
//Add our branding header to each response
response.Headers.Add("X-Powered-By", "My product");
return response;
}
}
Then you register this handler in your web-api configuration, this is usually in the GlobalConfig.cs file.
config.MessageHandlers.Add(new MessageHandlerBranding());
You could also write your own custom class for the response object like this:
public class ApiQueryResult<T> : IHttpActionResult where T : class
{
public ApiQueryResult(HttpRequestMessage request)
{
this.StatusCode = HttpStatusCode.OK; ;
this.HeadersToAdd = new List<MyStringPair>();
this.Request = request;
}
public HttpStatusCode StatusCode { get; set; }
private List<MyStringPair> HeadersToAdd { get; set; }
public T Content { get; set; }
private HttpRequestMessage Request { get; set; }
public void AddHeaders(string headerKey, string headerValue)
{
this.HeadersToAdd.Add(new MyStringPair(headerKey, headerValue));
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = this.Request.CreateResponse<T>(this.StatusCode, this.Content);
foreach (var hdr in this.HeadersToAdd)
{
response.Content.Headers.Add(hdr.key, hdr.value);
}
return Task.FromResult(response);
}
private class MyStringPair
{
public MyStringPair(string key, string value)
{
this.key = key;
this.value = value;
}
public string key;
public string value;
}
}
And use it like this in your controller:
[HttpGet]
public ApiQueryResult<CustomersView> CustomersViewsRow(int id)
{
var ret = new ApiQueryResult<CustomersView>(this.Request);
ret.Content = this.BLL.GetOneCustomer(id);
ret.AddHeaders("myCustomHkey","myCustomValue");
return ret;
}
I have a DelegatingHandler in my web API for authentification (HMAC).
I would like to add a GET parameter to the request to return the user's id to my controller.
In my handler, I tried adding it like so:
public class SecurityHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
string apikey = request.Headers.GetValues(AuthConfig.ApiKeyHeader).First();
UserDTO user = UserRepo.GetUser(apikey);
if (user == null)
{
return SendResponseText("Invalid API key");
}
// Validate signature ...
// Add user Id to the URI
request.RequestUri = new Uri(request.RequestUri.OriginalString + "&UserId=" + user.Id);
return base.SendAsync(request, cancellationToken);
}
}
In my controller, I'm able to get the newly added parameter from the request uri, however the parameter binding is not working
public class MyModel
{
public int UserId { get; set; }
...
}
public string Get([FromUri] MyModel model)
{
// model.UserId has not been set (= 0)
// Request.RequestUri contains "&UserId=5"
}
Update
I'm guessing the binding is being done from the Params in the HttpContext. I tried something like this, but Params collection is readonly.
var newContext = request.Properties["MS_HttpContext"] as HttpContextWrapper;
newContext.Request.Params.Add("UserId", "8");
request.Properties.Remove("MS_HttpContext");
request.Properties.Add("MS_HttpContext", newContext);
I have tried at my side and it working.
Here is my sample url.
http://localhost:50382/api/values?UserId=10
Here is controller action.
public class ValueController : ApiController
{
public IEnumerable<string> Get([FromUri]Model my)
{
return new string[] { "value1", "value2" , my.UserId.ToString() };
}
}
As per your comment here I created delegate handler.
public class MyMessageHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var myTempObj = new { id = 20 };
/* You have to check this and remove this key. If key present that FromUri use cached properties*/
if (request.Properties.ContainsKey("MS_QueryNameValuePairs"))
{
request.Properties.Remove("MS_QueryNameValuePairs");
}
// Now prepare or update uri over here. It will surely work now.
if (!string.IsNullOrEmpty(request.RequestUri.Query) && request.RequestUri.Query.Contains('?'))
{
request.RequestUri = new Uri(request.RequestUri + "&UserID=" + myTempObj.id);
}
else
{
request.RequestUri = new Uri(request.RequestUri + "?UserID=" + myTempObj.id);
}
return base.SendAsync(request, cancellationToken);
}
}
It is work as expected.
If your original request contain userid then it get duplicated and it will not work. It return 0.
I also have doubt that you are using request.RequestUri.OriginalString. Just try to use request.RequestUri. It might possible that OriginalString has encoded value.
I found the following solution to my problem
request.RequestUri = new Uri(request.RequestUri + "&UserId=8");
var newFormDataCollection = new FormDataCollection(request.RequestUri);
request.Properties.Remove("MS_QueryNameValuePairs");
request.Properties.Add("MS_QueryNameValuePairs", newFormDataCollection);
return base.SendAsync(request, cancellationToken);
[FromURI] seems to use the values in the FormDataCollection, so you simply need to update the "MS_QueryNameValuePairs" property.