Liens
Projet
Clique-droit sur le projet → Properties → Debug → décocher Launch browser
Controller
Controllers/ItemsController.cs
|
[Produces("application/json")] // force le format JSON
[Route("api/[controller]")] // /api/items
[ApiController]
public class ItemsController : ControllerBase
{
private readonly IMyAppRepository _repository;
public ListApiController(IMyAppRepository repository)
{
_repository = repository;
}
|
GET
|
// [HttpGet("[action]")]
[HttpGet]
[ProducesResponseType(200, Type = typeof(IEnumerable<Item>))]
public IActionResult Get()
{
return Ok(_repository.GetAllItems());
}
[HttpGet("{id:int}")]
[ProducesResponseType(200, Type = typeof(Item))]
[ProducesResponseType(404)]
[ProducesResponseType(500)]
public IActionResult Get(int id)
{
try
{
var item = _repository.GetItem(id);
if (item != null)
{
return Ok(item); // status 200 OK
}
else
{
return NotFound(); // status 404 Not Found
}
}
catch (Exception ex)
{
// using Microsoft.AspNetCore.Http;
return StatusCode(StatusCodes.Status500InternalServerError, $"Failed to get Item {id} ({ex})");
}
}
|
POST
|
[HttpPost]
[ProducesResponseType(201, Type = typeof(Item))]
[ProducesResponseType(400)]
[ProducesResponseType(500)]
public IActionResult Post([FromBody] Item item)
{
if (item == null)
return BadRequest();
if (!ModelState.IsValid)
return BadRequest(ModelState);
try
{
var createdItem = _itemRepository.Add(item);
return Created("item", createdItem);
if (_repository.SaveAll())
{
// status 201 Created
return Created($"https://localhost:5001/api/items/{item.Id}", item);
return CreatedAtAction(nameof(Get), new { id = item.Id }, item);
return CreatedAtRoute("api", new {
controller = "items",
action = nameof(Get),
id = $"{item.Id}"
}, item);
}
else
{
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to save.");
}
}
catch (Exception ex)
{
return StatusCode(StatusCodes.Status500InternalServerError, $"Failed to add Item ({ex})");
}
}
|
Startup.cs
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc(routes => {
routes.MapRoute(
name: "api",
template: "api/{controller=Items}/{id?}");
});
|
|
[ProducesResponseType(204)]
[ProducesResponseType(400)]
[ProducesResponseType(404)]
public IActionResult Put([FromBody] Item item)
{
if (item == null)
return BadRequest();
if (!ModelState.IsValid)
return BadRequest(ModelState);
var itemToUpdate = _itemRepository.Get(item.Id);
if (itemToUpdate == null)
return NotFound(); // status 404 Not Found
_itemRepository.Update(itemToUpdate);
return NoContent();
}
|
DELETE
|
[HttpDelete("{id:int}")]
[ProducesResponseType(204)]
[ProducesResponseType(404)]
public IActionResult Delete(int id)
{
var item = _items.SingleOrDefault(i => i.Id == id);
if (item != null)
{
if (_items.Remove(item))
return NoContent();
else
return StatusCode(StatusCodes.Status500InternalServerError);
}
else
return NotFound();
|
Client
Services/ItemDataService.cs
|
public class ItemDataService : IItemDataService
{
private readonly HttpClient _httpClient;
// injection de HttpClient
public ItemDataService(HttpClient httpClient)
{
_httpClient = httpClient;
}
|
GET
|
public async Task<IEnumerable<Employee>> GetAllItemsAsync()
{
return await JsonSerializer.DeserializeAsync<IEnumerable<Item>>(
await _httpClient.GetStreamAsync($"api/item"),
new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
});
}
public async Task<Item> GetItemAsync(int itemId)
{
return await JsonSerializer.DeserializeAsync<Item>(
await _httpClient.GetStreamAsync($"api/item/{itemId}"),
new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
});
}
|
POST
|
public async Task<Item> AddItemAsync(Item item)
{
var itemJson =
new StringContent(JsonSerializer.Serialize(item), Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync("api/item", itemJson);
if (response.IsSuccessStatusCode)
{
return await JsonSerializer.DeserializeAsync<Item>(await response.Content.ReadAsStreamAsync());
}
return null;
}
|
PUT
|
public async Task UpdateItemAsync(Item item)
{
var itemJson =
new StringContent(JsonSerializer.Serialize(item), Encoding.UTF8, "application/json");
await _httpClient.PutAsync("api/item", itemJson);
}
|
DELETE
|
public async Task DeleteItemAsync(int itemId)
{
await _httpClient.DeleteAsync($"api/item/{itemId}");
}
|
Validation
ItemViewModel.cs
|
public class ItemViewModel
{
public int Id { get; set; }
[Required]
[MinLength(6)]
public string Name { get; set; }
}
|
|
[HttpPost]
public IActionResult Post([FromBody]ItemViewModel itemVm)
{
if (ModelState.IsValid)
{
var item = itemVm.ToEfItem();
_repository.Add(item);
if (_repository.SaveAll())
{
var newItemVm = new ItemViewModel(item);
return Created($"/api/ListApi/{newItemVm.Id}", newItemVm);
}
else
{
return BadRequest("Failed to save.");
}
}
else
{
return BadRequest(ModelState);
}
|
Query Strings
Url: http://localhost:80/api/items?option1=true
ItemsController.cs
|
[HttpGet]
public IActionResult Get(bool option1 = false) // par défaut à false
{ }
|
Images
Download
MyController.cs
|
[HttpGet]
public IActionResult Get()
{
var filePath = Path.Combine("~", "folder", "image.jpeg");
// VirtualFileResult
return File(filePath, "image/jpeg");
}
|
MyController.cs
|
[HttpGet]
public IActionResult Get()
{
var filePath = Path.Combine("~", "folder", "image.jpeg");
byte[] bytesArray = System.IO.File.ReadAllBytes(filePath);
return Ok("data:image/jpeg;base64," + Convert.ToBase64String(bytesArray));
}
|
Upload
MyController.cs
|
[HttpPost]
public async Task<IActionResult> Post([FromForm]IFormFile myFile)
{
var filePath = Path.Combine(folderPath, photo.FileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await myFile.CopyToAsync(stream);
}
return Ok(filePath);
}
|
Postman → Body → form-data
- key: myFile
- File
- Value: Choose Files
|
Si myFile est toujours null, enlever [FromForm] |
Renvoyer le message d'erreur des exceptions
ExceptionFilterAttribute
ApiExceptionFilterAttribute.cs
|
public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.HttpContext.Response.ContentType = "text/plain";
context.HttpContext.Response.WriteAsync(context.Exception.Message).ConfigureAwait(false);
}
}
|
MyController.cs
|
[ApiExceptionFilter]
[Route("api/[controller]")]
public class MyController : Controller
|
ExceptionMiddleware.cs
|
public class ExceptionMiddleware
{
public async Task Invoke(HttpContext context)
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
var ex = context.Features.Get<IExceptionHandlerFeature>()?.Error;
if (ex == null) return;
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync(ex.Message);
}
}
|
Startup.cs
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseExceptionHandler(new ExceptionHandlerOptions
{
ExceptionHandler = new ExceptionMiddleware().Invoke
});
app.UseMvc();
|
UseWhen et UseExceptionHandler
Startup.cs
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseWhen(x => x.Request.Path.Value.StartsWith("/api"), builder =>
{
builder.UseExceptionHandler(new ExceptionHandlerOptions
{
ExceptionHandler = new ExceptionMiddleware().Invoke
});
});
app.UseWhen(x => !x.Request.Path.Value.StartsWith("/api"), builder =>
{
builder.UseExceptionHandler("/Home/Error");
});
|
UseExceptionHandler
ExceptionMiddleware.cs
|
public class ExceptionMiddleware
{
public async Task Invoke(HttpContext context)
{
if (context.Request.Path.Value.StartsWith("/api"))
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
var ex = context.Features.Get<IExceptionHandlerFeature>()?.Error;
if (ex == null) return;
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync(ex.Message).ConfigureAwait(false);
}
else
{
// redirect to /Home/Error
}
}
}
|
Startup.cs
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseStatusCodePagesWithReExecute("/error/{0}");
app.UseExceptionHandler("/error/500");
app.UseMvc();
|
|
[HttpGet("/error/{code:int}")]
public string Error(int code)
{
return $"Error: {code}";
}
|
Erreurs
JsonSerializationException: Self referencing loop detected for property 'xxx' ...
Startup.cs
|
services.AddMvc()
// ignorer les références circulaires entre objets dans EF pour JSON
.AddJsonOptions(opt => opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);
|
SqlException: Cannot insert explicit value for identity column in table 'xxx' when IDENTITY_INSERT is set to OFF
Impossible de sauvegarder les changements si une entité a été insérée avec une valeur ≠ default dans une propriété bindée avec une colonne Identity.
Solution: forcer la valeur de cette propriété a default. La bdd mettra la valeur de la propriété a jour après avoir inséré l'entité.