Having an issue posting from client to server in a server-side Blazor webapp.
I've created two simple classes in a shared library:
public class CommandRequest
{
public int RequestNumber { get; set; }
}
public class CommandResponse
{
public int ResponseNumber { get; set; }
}
My client side code:
#if (response == null)
{
<p>Loading...</p>
}
else
{
<p>#response.ResponseNumber</p>
}
#functions {
CommandResponse response;
protected override async Task OnInitAsync()
{
var request = new CommandRequest() {RequestNumber = 3};
response = await Http.SendJsonAsync<CommandResponse>(HttpMethod.Post,"api/SampleData/ProcessRequest", request);
}
}
My server side request handler:
[HttpPost("[action]")]
public CommandResponse ProcessRequest(CommandRequest request)
{
return new CommandResponse() { ResponseNumber = request.RequestNumber * 2 };
}
When I debug this the ProcessRequest method is always passed an empty object, request.RequestNumber is 0. I'm new to ASP.NET and Blazor, what am I doing wrong?
Adding [FromBody] attribute to the CommandRequest parameter solved my problem:
[HttpPost("[action]")]
public CommandResponse ProcessRequest([FromBody] CommandRequest request)
{
return new CommandResponse() { ResponseNumber = request.RequestNumber * 2 };
}
A reference here:
What is the function of [FromBody] Attribute in C#?
Related
I have two projects in one solution. The 1st it's library project and the second it's Asp.Net WebApi project. Both using .Net Framework 4.6.1.
I want to make some class that will handling exceptions globally. I found nice solution using ExceptionHandler, however it doesn't work as I expected. I was following this article https://learn.microsoft.com/pl-pl/aspnet/web-api/overview/error-handling/web-api-global-error-handling.
Here is my class where I handle exceptions.
public class GlobalExceptionHandler : ExceptionHandler
{
public override void HandleCore(ExceptionHandlerContext context)
{
context.Result = new TextPlainErrorResult
{
Request = context.ExceptionContext.Request,
Content = context.Exception.Message
};
}
private class TextPlainErrorResult : IHttpActionResult
{
public HttpRequestMessage Request { get; set; }
public string Content { get; set; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response =
new HttpResponseMessage(HttpStatusCode.InternalServerError);
response.Content = new StringContent(Content);
response.RequestMessage = Request;
return Task.FromResult(response);
}
}
}
For instance I have this code in my library project.
public class ThrowSomething
{
public void ThrowSomeException()
{
throw new Exception("Custom exception");
}
}
And in my controller in Asp.Net I have
public IHttpActionResult SomeAction()
{
var throwSomething = new ThrowSomething();
throwSomething.ThrowSomeException();
return Ok();
}
I would like to catch the exception in my GlobalExceptionHandler class and return some result to the Api. Currently the GlobalExceptionHandler is not handling the exceptions.
I also have config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler()); in Register method in WebApiConfig.
I want to try and refactor this method a bit better while I am having some difficulty in understand the async I have a web api2 project which I use my data access layer to share to a phone app. I am not sure if I have the syntax correct I am using a xamrian shared library here in a xamrian forms app.
I will have various methods link get clients which will have the end point API/clients but obv their return type would be different.
How would one make the below work well with a list view say.
How would I consume the below method as well , what is general practise to then store the jobs locally in sql lite.
public string BaseUrl { get; set; }
public string EndPoint { get; set; }
Lets go out to the web service and grab the job list.
public async List<Job> GetJSON()
{
List<Job> rootObject = new List<Job>();
try
{
var client = new System.Net.Http.HttpClient();
var response = await client.GetAsync("http://myinternaliis/api/job");
string json = await response.Content.ReadAsStringAsync();
if (json != "")
{
rootObject = JsonConvert.DeserializeObject< List<Job>>(json);
}
}
catch (InvalidCastException e)
{
throw e;
}
return await rootObject;
}
Thanks for help in improving my understanding.
I guess you are looking for something like:
public async Task<T> GetJson<T>(string url)
{
using (var client = new System.Net.Http.HttpClient())
{
var response = await client.GetAsync(url);
var json = await response.Content.ReadAsStringAsync();
return (T)JsonConvert.DeserializeObject<T>(json);
}
}
Usually I have:
IApi - defines all the API methods
IHttpService - defines methods like Get, Post and etc.
IJsonConverter - defines methods like serialize and deserialize.
Here is an example:
public interface IJsonConverter
{
T Deserialize<T>(string json);
string Serialize<T>(T data);
}
public class JsonConveter : IJsonConverter
{
public T Deserialize<T>(string json) => JsonConvert.DeserializeObject<T>(json);
public string Serialize<T>(T data) => JsonConvert.Serialize(data);
}
public interface IHttpService
{
Task<T> Get<T>(string url);
}
public class HttpService : IHttpService
{
readonly IJsonConverter jsonConverter;
public HttpService(IJsonConverter jsonConverter)
{
this.jsonConverter = jsonConverter;
}
public async Task<T> Get<T>(string url)
{
using (var client = new System.Net.Http.HttpClient())
{
var response = await client.GetAsync(url);
var json = await response.Content.ReadAsStringAsync();
return jsonConverter.Deserialize<T>(json);
}
}
}
public interface IApi
{
Task<List<Job>> GetJobs();
}
public class Api : IApi
{
readonly string url = "http://myinternaliis/api/";
readonly IHttpService httpService;
public Api(IHttpService httpService)
{
this.httpService = httpService;
}
public Task<List<Job>> GetJobs() => httpService.Get<List<Job>>($"{url}job");
}
I am trying to figure out how to implement a Global Exception Handler in .NET Web Api 2.
I tried following the example set out by Microsoft here:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/error-handling/web-api-global-error-handling
But when exception occured, it did nothing.
This is my code:
public class GlobalExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
Trace.WriteLine(context.Exception.Message);
context.Result = new TextPlainErrorResult
{
Request = context.ExceptionContext.Request,
Content = "Oops! Sorry! Something went wrong." +
"Please contact support#testme.com so we can try to fix it."
};
}
private class TextPlainErrorResult : IHttpActionResult
{
public HttpRequestMessage Request { private get; set; }
public string Content { private get; set; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response =
new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(Content),
RequestMessage = Request
};
return Task.FromResult(response);
}
}
}
Is there a better way (or more proper way) to implement a global exception handler?
Try adding this to your WebApiConfig
webConfiguration.Services.Replace(typeof(IExceptionHandler), new MyExceptionHandler()); // You have to use Replace() because only one handler is supported
webConfiguration.Services.Add(typeof(IExceptionLogger), new MyExceptionLogger()); // webConfiguration is an instance of System.Web.Http.HttpConfiguration
You missed
class GlobalExceptionHandler : ExceptionHandler
{
public override bool ShouldHandle(ExceptionHandlerContext context)
{
return true;
}
//...
}
See WebApi v2 ExceptionHandler not called
I was wondering if it was possible to set some custom header values whenever an internal server error has occurred? I am currently doing:
public class FooExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
// context.Result already contains my custom header values
context.Result = new InternalServerErrorResult(context.Request);
}
}
Here I also want to set some header values but though it appears in the request the response does not contain it.
Is there a way of doing this?
There is a sample code for your reference, my ApiExceptionHandler is your
FooExceptionHandler
public class ApiExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
var response = new Response<string>
{
Code = StatusCode.Exception,
Message = $#"{context.Exception.Message},{context.Exception.StackTrace}"
};
context.Result = new CustomeErrorResult
{
Request = context.ExceptionContext.Request,
Content = JsonConvert.SerializeObject(response),
};
}
}
internal class CustomeErrorResult : IHttpActionResult
{
public HttpRequestMessage Request { get; set; }
public string Content { get; set; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response =
new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(Content),
RequestMessage = Request
};
response.Headers.Add("Access-Control-Allow-Origin", "*");
response.Headers.Add("Access-Control-Allow-Headers", "*");
return Task.FromResult(response);
}
}
It should be possible by creating your own exception filter.
namespace MyApplication.Filters
{
using System;
using System.Net;
using System.Net.Http;
using System.Web.Http.Filters;
public class CustomHeadersFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
context.Response.Content.Headers.Add("X-CustomHeader", "whatever...");
}
}
}
http://www.asp.net/web-api/overview/error-handling/exception-handling
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;
}