Error handling Web Api .net core and Repository Pattern - httpclient

I have question about web api and Repository may be its a duplicate question.
but i tried to search on it and i did not get any satisfactory answer.
In my Repository i am getting data with the help of httpclient.
My question is that i can get an error inside my response or i can get required json data which i can map to my product class.I am returning IEnumerable.
1) If i get an error how can i bubble it up to controller and display an error to user.
2) Return the MessageResponse instead of IEnumerable and handle it inside the controller.
What is the best way.
enter code here
public interface IProduct{
Task<IEnumerable<Product>> All();
}
public class Product:IProduct
{
public async Task<IEnumerable<Product>> All(){
var ResponseMessage=//some response.
}
}

You could customize a ApiException which is used to get the error message of the response, and call the UseExceptionHandler in your startup.cs ,refer to the following :
ProductRep
public class ProductRep : IProduct
{
private readonly HttpClient _client;
public ProductRep(HttpClient client)
{
_client = client;
}
public async Task<IEnumerable<Product>> All()
{
List<Product> productlist = new List<Product>();
var response = await _client.GetAsync("https://localhost:44357/api/values/GetProducts");
string apiResponse = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode == false)
{
JObject message = JObject.Parse(apiResponse);
var value = message.GetValue("error").ToString();
throw new ApiException(value);
}
productlist = JsonConvert.DeserializeObject<List<Product>>(apiResponse);
return productlist;
}
public class ApiException : Exception
{
public ApiException(string message): base(message)
{ }
}
}
Startup.cs
app.UseExceptionHandler(a => a.Run(async context =>
{
var feature = context.Features.Get<IExceptionHandlerPathFeature>();
var exception = feature.Error;
var result = JsonConvert.SerializeObject(new { error = exception.Message });
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(result);
}));

Related

Asp.Net-Core + MongoDb - How to search database by "code" and return the original url?

I am unsure how to go about searching for the "Code" stored in my Database in order to return the "OriginalUrl".
I know I can search for the ObjectId but I want to be able to search by the "Code" assigned to that ObjectId.
Currently I have a working program that takes a Url as well as a "title" and sends it to the database:
It is assigned an Objectid _id and a randomly generated 12 character "Code":
If it helps this is my Controller class:
namespace ShortenUrls.Controllers
{
[Route("api/codes")]
public class ShortUrlsController : Controller
{
private readonly ShortUrlRepository _repo;
public ShortUrlsController(ShortUrlRepository repo)
{
_repo = repo;
}
[HttpGet("{id}")]
public async Task<IActionResult> Get(string id)
{
var su = await _repo.GetAsync(id);
if (su == null)
return NotFound();
return Ok(su);
}
[HttpPost]
public async Task<IActionResult> Create([FromBody] ShortUrl su)
{
await _repo.CreateAsync(su);
return Ok(su);
}
}
And Repository class:
namespace ShortenUrls.Models.Repository
{
public class ShortUrlRepository
{
private const string alphabet = "23456789bcdfghjkmnpqrstvwxyz-_";
private static readonly Random rand = new Random();
private readonly Database _db;
public ShortUrlRepository(Database db)
{
_db = db;
}
private static string GenerateCode()
{
const int codeLength = 12;
var chars = new char[codeLength];
for (var i = 0; i < codeLength; i++)
{
chars[i] = alphabet[rand.Next(0, alphabet.Length)];
}
return new string(chars);
}
public Task<ShortUrl> GetAsync(string id)
{
var objId = ObjectId.Parse(id);
return _db.Urls.Find(x => x.Id == objId).FirstOrDefaultAsync();
}
public Task CreateAsync(ShortUrl su)
{
su.Code = GenerateCode();
return _db.Urls.InsertOneAsync(su);
}
}
Just use a filter. Doing it this way let's you create a query specifically for the "code".
public async Task<ShortUrl> GetAsync(string code)
{
var filterBuilder = new FilterDefinitionBuilder<ShortUrl>();
var filter = filterBuilder.Eq(s => s.Code, code);
var cursor = await _db.Urls.FindAsync(filter);
return await cursor.FirstOrDefaultAsync();
}
Assuming you already know the code when calling this and that ObjectId is created on InsertOneAsync call. First change your repository to take Code as searchable input.
public Task<ShortUrl> GetAsync(string code)
{
return await _db.Urls.FirstOrDefaultAsync(x => x.Code == code);
}
Then change your controller Get to this:
[HttpGet("{code}")]
public async Task<IActionResult> Get(string code)
{
var su = await _repo.GetAsync(code);
if (su == null)
return NotFound();
return Ok(su);
}
In your controller you can access su.OriginalUrl if you need to only return that after getting the object.
Then in postman you can just call http://localhost:51767/api/codes?code=cmg3fjjr_gtv
Remember only Id works for default url parameters as setup by your default routes in Startup.cs.
app.UseMvc(routes => { /*...*/ })
So this wont work: /api/codes/cmg3fjjr_gtv unless you specifically set up routing or change {code} back to {id}. Readability of your code suffers though.

Best practices to handle Web API status codes

I have a web API project done with .NETCore.
My web API receives a request from another Service A, with the information I have I need to do some conversion on the data and send it to another Service B.
I am expecting that Service B send back some response: like OK or NOK. As the number of codes I can get back from Service B are so much. I would like to know which is the best practices to handle those codes?
As you will see in my code, I get the status code in this way:
var status = (int)response.StatusCode;
And the I have some if to handle this. Looking at my code it looks like a very poor status code Handling but at moment it is the best I can do. I am kindly asking suggestions to improve this.
I am using RestSharp.
Following my code:
[HttpPost]
[Produces("application/json", Type = typeof(MyModel))]
public async Task<IActionResult> Post([FromBody]MyModel myModel)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var response = (RestResponse) await _restHelper.GetResponse("ServiceB:url", myModel);
if (response != null)
{
var status = (int)response.StatusCode;
//2xx status OK
if (status >= 200 && status < 300)
{
return Ok(response.Content);
}
//Catch all status code
return StatusCode(status, response.Content);
}
//If for some reason, I don't get any response from ServiceB
return NotFound("No response from ServiceB");
}
catch (Exception ex)
{
_logger.LogError("POST_ERROR", "ServiceB-relay/Post UNEXPECTED ERROR", ex.Message);
return StatusCode(500, "Server error, not able to process your request");
}
}
and this is my restHelper
public class RestHelper: IRestHelper
{
private readonly IConfigurationRoot _config;
public RestHelper(IConfigurationRoot config)
{
_config = config;
}
public async Task<IRestResponse> GetResponse(string configKey, object dtoObject)
{
//Get the URL from the config.json
var url = _config[configKey];
//Create rest client and rest request
var restClient = new RestClient(url);
var request = new RestRequest {Timeout = 30000, Method = Method.POST};
//Add header
request.AddHeader("Accept", "application/json");
//convert the dto object to json
var jsonObject = JsonConvert.SerializeObject(dtoObject.ToString(), Formatting.Indented);
request.AddParameter("application/json", jsonObject, ParameterType.RequestBody);
var taskCompletion = new TaskCompletionSource<IRestResponse>();
//Execute async
restClient.ExecuteAsync(request, r => taskCompletion.SetResult(r));
//await the task to finish
var response = (RestResponse) await taskCompletion.Task;
return response;
}
Thanks

InvokeApiAsync<HttpResponseMessage> returns null

Can someone explain my why that client (Xamarin.Forms PCL) call returns null?
HttpResponseMessage response = await OfflineSyncStoreManager.Instance.MobileAppClient.InvokeApiAsync<HttpResponseMessage>("ResetTruckAuftragWorkflow");
response is null. When I execute that in a console app it returns the
valid http response.
I use the latest stable ZUMO nugets in client and backend. There is my ZUMO backend code:
[Authorize]
[MobileAppController]
public class ResetTruckAuftragWorkflowController : ApiController
{
private readonly RcsMobileContext _rcsMobileContext;
private readonly TruckFahrerInfo _truckFahrerInfo;
public ResetTruckAuftragWorkflowController()
{
_rcsMobileContext = new RcsMobileContext();
_truckFahrerInfo = new TruckFahrerInfo(this.User as ClaimsPrincipal);
}
// POST api/ResetTruckAuftragWorkflow
[HttpPost]
public async Task<IHttpActionResult> PostAsync()
{
if (ModelState.IsValid)
{
using (var transaction = _rcsMobileContext.Database.BeginTransaction())
{
try
{
var truckAuftragList = _rcsMobileContext.TruckAuftrags.PerUserFilter(_truckFahrerInfo.FahrerId);
var truckAppIds = truckAuftragList?.Select(ta => ta.TruckAppId).ToArray();
if (truckAppIds != null)
{
foreach (var truckAppId in truckAppIds)
{
await _rcsMobileContext.Database.ExecuteSqlCommandAsync(_tawQueryTaskStatus10, truckAppId);
await _rcsMobileContext.Database.ExecuteSqlCommandAsync(_tawQueryTaskStatus5, truckAppId);
await _rcsMobileContext.Database.ExecuteSqlCommandAsync(_talQuery, truckAppId);
await _rcsMobileContext.Database.ExecuteSqlCommandAsync(_taQuery, truckAppId);
}
}
await _rcsMobileContext.Database.ExecuteSqlCommandAsync(_taQuery, _truckFahrerInfo.FahrerId);
transaction.Commit();
}
catch (Exception e)
{
transaction.Rollback();
return BadRequest($"Transaction failed: {e}");
}
}
return Ok();
}
else
{
return BadRequest(ModelState);
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_rcsMobileContext.Dispose();
}
base.Dispose(disposing);
}
}
thanks
Eric
InvokeApiAsync decodes the body that is returned and de-serializes the JSON into type T. You should not use HttpResponseMessage for this purpose as it is not serializable.
If you don't care about the body, use the non-generic form of InvokeApiAsync.

Consuming Web API from MVC4 controller?

I am currently working on a website and I had a good separation of concerns following a repository pattern with repositories and managers. Now, I am attempting to implement a Web API as I would greatly benefit from it in the future being able to use it from various clients. Since I am fairly new to REST services, I am having trouble with the correct procedure to consume my web API from a Service in my MVC4 application to then use that service in my MVC controllers. I do not want to have to use knockout for every call to the API.
My Web APIs look something like this(simplified):
public class UserController : ApiController
{
private readonly IUserManager _manager;
public UserController(IUserManager manager)
{
this._manager = manager;
}
// GET api/user
public IEnumerable<User> Get()
{
return _manager.GetAll();
}
// GET api/user/5
public User Get(int id)
{
return _manager.GetById(id);
}
// POST api/user
public void Post(User user)
{
_manager.Add(user);
}
// PUT api/user/5
public void Put(User user)
{
_manager.Update(user);
}
// DELETE api/user/5
public void Delete(User user)
{
_manager.Delete(user);
}
}
I essentially would like to create a service to consume my web API as such:
public class UserService : IUserService
{
....Implement something to get,post,put,and delete using the api.
}
so then I can use it in my mvc controller:
public class UserController: Controller
{
private readonly IUserService _userService;
public UserController(IUserService userService)
{
this._userService = userService;
}
//And then I will be able to communicate with my WebAPI from my MVC controller
}
I know this is possible because I have seen it done at some workplaces but it is very difficult to find articles about this, I have only found articles explaining how to consume web API through knockout. Any help or tips would be greatly appreciated.
Have a look at the implementation over here: https://github.com/NBusy/NBusy.SDK/blob/master/src/NBusy.Client/Resources/Messages.cs
It basically makes use of HttpClient class to consume Web API. One caveat though, all responses are wrapped in a custom HttpResponse class in that sample. You don't need to do that and can simply use the retrieved DTO object as the return type or a raw HttpResponseMessage class.
You might want to create a static class, I created a separate Class Library to use across solutions that might want to use the API.
NOTE: I use RestSharp for POST and PUT operation since I haven't been able to get them to work using the regular HttpClient over SSL. As you can see documented in this question.
internal static class Container
{
private static bool isInitialized;
internal static HttpClient Client { get; set; }
internal static RestClient RestClient { get; set; }
/// <summary>
/// Verifies the initialized.
/// </summary>
/// <param name="throwException">if set to <c>true</c> [throw exception].</param>
/// <returns>
/// <c>true</c> if it has been initialized; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="System.InvalidOperationException">Service must be initialized first.</exception>
internal static bool VerifyInitialized(bool throwException = true)
{
if (!isInitialized)
{
if (throwException)
{
throw new InvalidOperationException("Service must be initialized first.");
}
}
return true;
}
/// <summary>
/// Initializes the Service communication, all methods throw a System.InvalidOperationException if it hasn't been initialized.
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="connectionUserName">Name of the connection user.</param>
/// <param name="connectionPassword">The connection password.</param>
internal static void Initialize(string url, string connectionUserName, string connectionPassword)
{
RestClient = new RestClient(url);
if (connectionUserName != null && connectionPassword != null)
{
HttpClientHandler handler = new HttpClientHandler
{
Credentials = new NetworkCredential(connectionUserName, connectionPassword)
};
Client = new HttpClient(handler);
RestClient.Authenticator = new HttpBasicAuthenticator(connectionUserName, connectionPassword);
}
else
{
Client = new HttpClient();
}
Client.BaseAddress = new Uri(url);
isInitialized = true;
}
}
public static class UserService
{
public static void Initialize(string url = "https://serverUrl/", string connectionUserName = null, string connectionPassword = null)
{
Container.Initialize(url, connectionUserName, connectionPassword);
}
public static async Task<IEnumerable<User>> GetServiceSites()
{
// RestSharp example
Container.VerifyInitialized();
var request = new RestRequest("api/Users", Method.GET);
request.RequestFormat = DataFormat.Json;
var response = await Task.Factory.StartNew(() => { return Container.RestClient.Execute<List<User>>(request); }).ConfigureAwait(false);
return response.Data;
// HttpClient example
var response = await Container.Client.GetAsync("api/Users/").ConfigureAwait(false);
return await response.Content.ReadAsAsync<IEnumerable<User>>().ConfigureAwait(false);
}
public static async Task<User> Get(int id)
{
Container.VerifyInitialized();
var request = new RestRequest("api/Users/" + id, Method.GET);
var response = await Task.Factory.StartNew(() => { return Container.RestClient.Execute<User>(request); }).ConfigureAwait(false);
return response.Data;
}
public static async Task Put(int id, User user)
{
Container.VerifyInitialized();
var request = new RestRequest("api/Users/" + id, Method.PATCH);
request.RequestFormat = DataFormat.Json;
request.AddBody(user);
var response = await Task.Factory.StartNew(() => { return Container.RestClient.Execute(request); }).ConfigureAwait(false);
}
public static async Task Post(User user)
{
Container.VerifyInitialized();
var request = new RestRequest("api/Users", Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddBody(user);
var response = await Task.Factory.StartNew(() => { return Container.RestClient.Execute(request); }).ConfigureAwait(false);
}
public static async Task Delete(int id)
{
Container.VerifyInitialized();
var request = new RestRequest("api/Users/" + id, Method.DELETE);
var response = await Task.Factory.StartNew(() => { return Container.RestClient.Execute(request); }).ConfigureAwait(false);
}
}

MVC 2.0 - Custom handling of all errors to return json

I have an MVC 2 app that I want all requests to return json. I have overridden a HandleErrorAttribute and an AuthorizeAttribute. My goal is that all errors (even 403 and 404) are returned as json.
Here is my error handler. ExceptionModel is a simple class defining any error returned by my application. The Exception handler is a class that translates the error details into a formatted e-mail and sends it to me.
public class HandleErrorJsonAttribute : System.Web.Mvc.HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
context.ExceptionHandled = true;
RaiseErrorSignal(context.Exception);
context.RequestContext.HttpContext.Response.ContentType = "application/json";
JsonSerializer serializer = new JsonSerializer();
serializer.Serialize(context.HttpContext.Response.Output, new ExceptionModel(context.Exception));
}
private static void RaiseErrorSignal(Exception ex)
{
IExceptionHandler handler = Resolve();
handler.HandleError(ex.GetBaseException());
}
private static IExceptionHandler Resolve()
{
return ServiceLocator.Locate<IExceptionHandler>();
}
}
Here is the Exception model for clarification
public class ExceptionModel
{
public int ErrorCode { get; set; }
public string Message { get; set; }
public ExceptionModel() : this(null)
{
}
public ExceptionModel(Exception exception)
{
ErrorCode = 500;
Message = "An unknown error ocurred";
if (exception != null)
{
if (exception is HttpException)
ErrorCode = ((HttpException)exception).GetHttpCode();
Message = exception.Message;
}
}
public ExceptionModel(int errorCode, string message)
{
ErrorCode = errorCode;
Message = message;
}
}
and finally, my custom authorize attribute. I an using forms auth, but I did not want any of the automatic redirection. I simply want the error to show on the screen and stop any further processing.
public class AuthorizeTokenAttribute : System.Web.Mvc.AuthorizeAttribute
{
public bool SuperAdminOnly { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool authorized = base.AuthorizeCore(httpContext);
if(!SuperAdminOnly)
return authorized;
if(!authorized)
return authorized;
return SessionHelper.UserIsSuperAdmin(httpContext.User.Identity.Name);
}
protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
{
throw new HttpException(403, "Access Denied");
}
}
This all works great for most errors, but it is missing one thing. I have a controller action like this.
[AuthorizeToken]
[HttpPost]
public JsonResult MyAction()
{
return new JsonResult();
}
It works fine when you submit via post, but on a get I receive an unhandled 404 error.
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource
you are looking for (or one of its
dependencies) could have been removed,
had its name changed, or is
temporarily unavailable. Please
review the following URL and make sure
that it is spelled correctly.
Requested URL: /MyController/MyAction
Version Information: Microsoft .NET
Framework Version:4.0.30319; ASP.NET
Version:4.0.30319.1
This happens on a GET, which is to be expected as default behavior. However, how can I handle for this condition so that I could instead return json like this
{"ErrorCode":404,"Message":"Page Not Found"}
To handle errors personally I prefer the Application_Error event in Global.asax:
protected void Application_Error(object sender, EventArgs e)
{
var exception = Server.GetLastError();
Response.Clear();
Server.ClearError();
var httpException = exception as HttpException;
var routeData = new RouteData();
routeData.Values["controller"] = "Errors";
routeData.Values["action"] = "Index";
routeData.Values["error"] = exception;
IController errorController = new ErrorsController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
and then have an ErrorsController:
public class ErrorsController : Controller
{
public ActionResult Index(Exception exception)
{
var errorCode = 500;
var httpException = exception as HttpException;
if (httpException != null)
{
errorCode = httpException.ErrorCode;
}
return Json(new
{
ErrorCode = errorCode,
Message = exception.Message
}, JsonRequestBehavior.AllowGet);
}
}