REST API HttpClient return response good practice - c#

I have methods that are returning objects, the problem is I don't have good idea how to verify errors and return it correctly in MVC View
public async Task<object> GetUser()
{
//....
if (responseMessage.IsSuccessStatusCode)
{
return await responseMessage.Content.ReadAsAsync<User>();
}
if (responseMessage.StatusCode == HttpStatusCode.BadRequest)
{
return await responseMessage.Content.ReadAsAsync<Error>();
}
}
return null;
}
Now in my controller I'm getting data and trying to return correct type
var httpResult = await GetUser();
if (httpResult.GetType() == typeof (Error))
{
ModelState.AddModelError("", (httpResult as Error).ErrorDescription);
return View(model);
}
if (httpResult.GetType() == typeof (User))
{
var user = httpResult as User;
return View(User);
}
I don't like my ifs and logic, any better solution?

You can try something like this. I used this pattern successfully in the past and present.
[DataContract]
public class OperationResult<T>
{
[DataMember]
public List<Error> Errors { get; set; }
[DataMember]
public T ResultObject { get; set; }
[DataMember]
public bool Success { get; private set; }
public OperationResult(List<Error> errors)
{
Errors = errors;
}
public OperationResult(T resultObject)
{
ResultObject = resultObject;
Success = true;
}
public OperationResult()
{
}
}
You return this type from your methods and you check the Success flag on return, if false you read the Errors property and you can populate the ModelState or return a specialized view model etc.

Related

How to reuse code within the same controller?

I have a controller with 2 actions containing identical code. But i'm having trouble reusing it from a separate function, without resorting to returning null and checking return values. The actions are for logging in and for resending a two factor toke.
2 actions (Login and Resend)
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginModel model)
{
if (!ModelState.IsValid) return View();
var user = await userManager.FindByNameAsync(model.UserName);
if(!(user != null && !await userManager.IsLockedOutAsync(user)))
{
ModelState.AddModelError("", "Invalid UserName or Password");
return View();
}
if(!await userManager.CheckPasswordAsync(user, model.Password))
{
ModelState.AddModelError("", "Invalid UserName or Password");
return View();
}
// can this be improved?
var r = await SendTwoFactor(user);
if(r != null) return r; // error or redirect or no two factor?
return RedirectToAction("Index");
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Resend(ResendModel model)
{
var result = await HttpContext.AuthenticateAsync(IdentityConstants.TwoFactorUserIdScheme);
if (!result.Succeeded)
{
return RedirectToAction(nameof(Login)); // expired
}
var user = await userManager.FindByIdAsync(result.Principal.FindFirstValue("sub"));
if (user == null)
{
return RedirectToAction(nameof(Login)); // invalid
}
await HttpContext.SignOutAsync(IdentityConstants.TwoFactorUserIdScheme);
// can this be improved?
var r = await SendTwoFactor(user);
if(r != null) return r; // error or redirect or no two factor?
...
return RedirectToAction("...");
}
then the re-used code looks something like this:
SendTwoFactor
private async Task<IActionResult> SendTwoFactor(IdentityUser user)
{
if(!await userManager.GetTwoFactorEnabledAsync(user)) return null;
var d = ...;
if(!d)
{
ModelState.AddModelError("", "... d");
return View();
}
// determine preferred two factor method
// send two factor token
return RedirectToAction("...");
}
Does anyone have a good pattern to improve this?
This would be my aproach
First of all, we need to define interface with the 'common' elements inside FooModel and Foo2Model that would be used in common code like this:
public interface iFoo
{
int Id { get; set; }
string Product_name{ get; set; }
}
public class Foo : iFoo
{
public string OtherThings { get; set; }
public int Id { get; set; }
public string Product_name { get; set; }
}
public class Foo2 : iFoo
{
public string OtherThings2 { get; set; }
public int Id { get; set; }
public string Product_name_nv { get; set; }
}
Then we can make a private function like this:
private async Task<IActionResult> identicalFunction<T>(T model) where T : iFoo
{
c = ...;
if(c)
{
d = ...;
if(!d)
{
ModelState.AddModelError("", "... d");
return View();
}
... // a lot more code similar code that can exit early
return RedirectToAction("...");
}
// Note that I included the last return inside this function
return RedirectToAction("...");
}
And then change
// identical code
return RedirectToAction("...");
into:
return await identicalFunction(model);

Underlying class is not converted to json

Question:
Originally command is returning ExecutionResult, which contains Data property of type DomainOperationResult.
Only in one case I need to return some other data, which is defined as CreateOrderCommandResult which is descendant of DomainOperationResult, it contains additional field - Url.
Calling command with swagger, in the debugger, everything is shoing fine, visible id and url with Quick View.
Json(result);
it shows that it is returning Id and Url properties.
but in swagger, curl, postman, etc. only id is returned, tried everything.
there is no Url in response.
controller is working fine, no middlewares, that may change returned response.
Controller method:
{
public async Task<IActionResult> Create([FromBody] CreateOrderCommand command)
{
if (!ModelState.IsValid)
{
return await Task.FromResult(BadRequest(ModelState));
}
var result = await _commandExecutor.ExecuteAsync(command);
if (result.Success)
{
return Json(result);
}
return Json(result);
}
}
CreateOrderCommand:
{
public override async Task<CommandExecutionResult> ExecuteAsync()
{
// .....
return await OkAsync(new CreateOrderCommandResult(orderId, payment.RedirectUrl));
}
}
DomainOperationResult:
{
public class DomainOperationResult
{
public DomainOperationResult()
{
}
public DomainOperationResult(long id)
{
Id = id;
}
public long Id { get; }
}
CreateOrderCommandResult:
{
public class CreateOrderCommandResult : DomainOperationResult
{
public CreateOrderCommandResult()
: base() { }
public CreateOrderCommandResult(long id, string url)
: base(id)
{
Url = url;
}
public string Url { get; set; }
}
}
Execution result:
public class ExecutionResult
{
public ExecutionResult()
{
}
public DomainOperationResult Data { get; set; }
}
returned response:
{
"success": true,
"data": {
"id": 0
},
"error": {
"errors": null
}
}
Still no idea, why this does not works.
After some digging, found workaround.
Response.Headers.Add("Content-type", "application/json"); return Content(JsonConvert.SerializeObject(result, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
returning content like this works ok - at least it generates everything I need.

WEB API post from uri/ Query string in post

i have a model
public partial class TalentVendorShots
{
public int Id { get; set; }
public string Email { get; set; }
public string One { get; set; }
public string Two { get; set; }
public string Three { get; set; }
public string Four { get; set; }
public string Five { get; set; }
public string Six { get; set; }
public string Seven { get; set; }
public string Eight { get; set; }
public string Nine { get; set; }
public string Ten { get; set; }
}
and basic controllers
[Route("api/[controller]")]
[ApiController]
public class TalentVendorShotsController : ControllerBase
{
private readonly champagneDatabase _context;
public TalentVendorShotsController(champagneDatabase context)
{
_context = context;
}
// GET: api/TalentVendorShots
[HttpGet]
public async Task<ActionResult<IEnumerable<TalentVendorShots>>> GetTalentVendorShots()
{
return await _context.TalentVendorShots.ToListAsync();
}
// GET: api/TalentVendorShots/5
[HttpGet("{id}")]
public async Task<ActionResult<TalentVendorShots>> GetTalentVendorShots(int id)
{
var talentVendorShots = await _context.TalentVendorShots.FindAsync(id);
if (talentVendorShots == null)
{
return NotFound();
}
return talentVendorShots;
}
// PUT: api/TalentVendorShots/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTalentVendorShots(int id, TalentVendorShots talentVendorShots)
{
if (id != talentVendorShots.Id)
{
return BadRequest();
}
_context.Entry(talentVendorShots).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TalentVendorShotsExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/TalentVendorShots
[HttpPost]
public async Task<ActionResult<TalentVendorShots>> PostTalentVendorShots(TalentVendorShots talentVendorShots)
{
_context.TalentVendorShots.Add(talentVendorShots);
await _context.SaveChangesAsync();
return CreatedAtAction("GetTalentVendorShots", new { id = talentVendorShots.Id }, talentVendorShots);
}
// DELETE: api/TalentVendorShots/5
[HttpDelete("{id}")]
public async Task<ActionResult<TalentVendorShots>> DeleteTalentVendorShots(int id)
{
var talentVendorShots = await _context.TalentVendorShots.FindAsync(id);
if (talentVendorShots == null)
{
return NotFound();
}
_context.TalentVendorShots.Remove(talentVendorShots);
await _context.SaveChangesAsync();
return talentVendorShots;
}
private bool TalentVendorShotsExists(int id)
{
return _context.TalentVendorShots.Any(e => e.Id == id);
}
}
}
all of this works fine. i get information from the database fine. now i want to make a post to the table via uri. no body.for example
/api/TalentVendorShots/id=1,email=testemail should create a new record with id of 1 and email of testemail. how can i accomplish this?
The basic rule is, You should use POST if the action is not idempotent. Though you can pass the query parameters and no body to POST. But It would not make sense in this scenario. Basically query parameters are used to get/filter information.
Similar way many Web API testing tools like ARC, Swagger, and PostMan (chrome extension does not allow, but standalone application allows) does not allow to send body with the GET request. Though you can send the body in GET requests.

Create Complex Object WebApi

Role Entity Of My Project has Multiple Action With This Modeling:
{
public string Uid { get; set; }
public string Descript { get; set; }
public bool Premitive { get; set; }
public virtual ICollection<ActionDto> Actions { get; set; }
public string Name { get; set; }
public bool IsDeleted { get; set; }
}
I use UnitOfWork Repository Pattern
The create method is:
public async Task<IHttpActionResult> Create([FromBody] RoleFullDto dto)
{
try
{
if (dto.Actions == null || dto.Actions.Count <= 0)
return BadRequest();
//If I Pass Only action uid, return EntityModelException
//When I Pass Complete Entity, Create New Action
//foreach (var action in dto.Actions)
//{
//var act = UnitOfWork.ActionRepository.Get(action.Uid);
//action.ActionName = act.ActionName;
//action.MenuId = act.MenuId;
//action.PersianActionName = act.PersianActionName;
//}
var role = ModelFactory.GetRole(dto);
if (role == null)
return Content(HttpStatusCode.InternalServerError, dto);
var result = await this.AppRoleManager.CreateAsync(role);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return ActionResult<Role>(role, null, "CreateRole", HttpStatusCode.Created);
}
catch (Exception e)
{
Console.WriteLine(e);
return InternalServerError();
}
}
I pass this parameters To create new role:
{
Name: "TestRole1",
Descript: "This is Test",
Premitive: false,
Actions: [{uid:1},{uid:2},{uid:3}]
}
but this method wand to add 3 new Action that Exist in Databese
How Can I Create Role With Exist Actions?
Since you are passing entities that doesn't come from the db, Entity Framework tries to create them. You could use the Attach method of DBSet to link you action objects to entities. But since you're not using a DBSet directly you could also do something like this :
dto.Actions = dto.Actions.Select(a => UnitOfWork.ActionRepository.Get(a.Uid)).ToList();

C# asp.net webapi return default custom error response with custom error model

I am not an expert in asp.net webapi and trying to return some custom errorresponse with a custom error model. Please find below my code. Is this the right approach? basically i have a custom errormodel which i need it to be returned in case of any exception in webapi.
public async Task<HttpResponseMessage> Get(string assetId)
{
_logProvider.Info($"AssetId - {assetId}");
try
{
return Request.CreateResponse(HttpStatusCode.OK, result);
}
catch (Exception exception)
{
return Request.CreateCustomResponse(HttpStatusCode.InternalServerError, exception.Message);
}
}
public static class ApiExtensions
{
public static HttpResponseMessage CreateCustomResponse(this HttpRequestMessage request, HttpStatusCode statusCode, string errorMessage)
{
var errorMessageModel = new ErrorModel(statusCode, errorMessage);
return request.CreateResponse(statusCode, errorMessageModel);
}
}
public class ErrorModel
{
public ErrorModel(HttpStatusCode statusCode, string message)
{
Code = (int)statusCode;
Message = message;
Fields = new Dictionary<string, ModelErrorCollection>();
}
public ErrorModel(HttpStatusCode statusCode)
{
Code = (int)statusCode;
Fields = new Dictionary<string, ModelErrorCollection>();
}
public string Message { get; set; }
public int Code { get; set; }
public Dictionary<string, ModelErrorCollection> Fields { get; set; }
}
Thanks

Categories

Resources