WebApi etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
WebApi etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

12 Ekim 2020

Asp Net Core ile ModelState Validation için Custom Yanıt Dönen Middleware

ASP.Net Core Web Api de DataAnnotations attribute ile uymayan durumlarda dönen yanıt örnek User.cs için aşağıdaki gibidir. 

 { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", 

 "title": "One or more validation errors occurred.",

 "status": 400, 

 "traceId": "|c591e8b8-46918ec46443b7d8.", 

"errors": { 

"Username": [ "The Username field is required.", "En az sekiz karakterli bir kullanıcı adı giriniz." ] 

    } 

 }

 Bizim tüm uygulama boyunca generic api responseda formatı aşağıda ki gibi istediğimizi varsayalım 

{ "Status": 400,

 "IsSuccess": false,

 "Result": null,

 "Errors":

 "The Username field is required. \r\n En az sekiz karakterli bir kullanıcı adı giriniz."

  }

 Bunun için yapılan adımlar aşağıdaki gibidir.

#
// ApiResponse.cs //

using System;
using Microsoft.Extensions.Logging;

namespace CustomMiddlewareForModelState.Models
{
public class ApiResponse<TResult>
{
public int Status { get; set; }
public bool IsSuccess { get; set; }
public TResult Result { get; set; }
public string Errors { get; set; }

public ApiResponse<TResult> Invoke(Func<TResult> func, ILogger _logger)
{
try
{
Result = func.Invoke();
Status = 200;
IsSuccess = true;
Errors = string.Empty;
}
catch (Exception ex)
{
Guid? exceptionId = Guid.NewGuid();
Result = default(TResult);
IsSuccess = false;
Errors = $"Genel bir hata meydana geldi. Lütfen {exceptionId} takip numarası ile geri dönüşte bulunun. ";

_logger?.LogError("ExceptionId : " + exceptionId.ToString()
+ " , Exception: " + ex.ToString());
}
return this;
}
}
}

// ModelStateFeature.cs //

using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace CustomMiddlewareForModelState.Models
{
public class ModelStateFeature
{
public ModelStateDictionary ModelState { get; set; }

public ModelStateFeature(ModelStateDictionary state)
{
ModelState = state;
}
}

}

// ModelStateFeatureFilter.cs //


using Microsoft.AspNetCore.Mvc.Filters;
using System.Threading.Tasks;
using CustomMiddlewareForModelState.Models;

namespace CustomMiddlewareForModelState.Filters
{
public class ModelStateFeatureFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var state = context.ModelState;
context.HttpContext.Features.Set(new ModelStateFeature(state));
await next();
}
}
}

// ModelStateResponseMiddleware.cs //

using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using CustomMiddlewareForModelState.Models;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;

namespace CustomMiddlewareForModelState.Middlewares{
public class ModelStateResponseMiddleware{
private readonly RequestDelegate next;
public ModelStateResponseMiddleware(RequestDelegate next)
{
this.next = next;
}

public async Task Invoke(HttpContext context)
{
Stream originalBody = context.Response.Body;
try
{
using (var ms = new MemoryStream())
{
context.Response.Body = ms;
await next(context);
ms.Position = 0;
string responseBody = new StreamReader(ms).ReadToEnd();

if (context.Response.StatusCode == 400)
{
var modelState = context.Features.Get<ModelStateFeature>()?.ModelState;
if (modelState != null
&& !modelState.IsValid
&& modelState.Count > 0)
{
var errors = modelState.Values.Where(v => v.Errors.Count > 0)
.SelectMany(v => v.Errors)
.Select(v => v.ErrorMessage)
.ToList();

var apiResponse = new ApiResponse<object>(){
IsSuccess = false,
Result = null,
Status = 400,
Errors = string.Join(" \r\n ", errors)
};

context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
context.Response.ContentType = "application/json";
string strApiResponse = JsonConvert.SerializeObject(apiResponse);
var data = System.Text.Encoding.UTF8.GetBytes(strApiResponse);
ms.Position = 0;
await ms.WriteAsync(data);
ms.SetLength(data.Length);
}
}
ms.Position = 0;
await ms.CopyToAsync(originalBody);
}
}
finally
{
context.Response.Body = originalBody;
}
}
}
}

// Startup.cs //
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CustomMiddlewareForModelState.Filters;
using CustomMiddlewareForModelState.Middlewares;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace CustomMiddlewareForModelState
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(opts => {
opts.Filters.Add(typeof(ModelStateFeatureFilter),int.MinValue);
});
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthorization();

app.UseMiddleware<ModelStateResponseMiddleware>();

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

// User.cs //

using System.ComponentModel.DataAnnotations;

namespace CustomMiddlewareForModelState.Models
{
public class User
{
[Required]
[StringLength(int.MaxValue, MinimumLength = 1, ErrorMessage = "En az sekiz karakterli bir kullanıcı adı giriniz.")]
public string Username { get; set; }

[Required]
[StringLength(int.MaxValue, MinimumLength = 1, ErrorMessage = "En az sekiz karakterli bir şifre giriniz.")]
public string Password { get; set; }
}
}

// TestController.cs //


using System;
using Microsoft.AspNetCore.Mvc;
using CustomMiddlewareForModelState.Models;

namespace CustomMiddlewareForModelState.Controllers{
[Route("api/[controller]")]
[ApiController]
public class TestController : Controller
{
[HttpPost]
public IActionResult Post(User user)
{
var resp = new ApiResponse<Guid>().Invoke(() =>
{
if (ModelState.IsValid)
{
return Guid.NewGuid();
}
throw new Exception("Validasyon hatası!");
}, null);

if (resp.Status == 200)
return Ok(resp);
else return BadRequest(resp);
}
}
}

ModelState Başarısızsa dönen yanıt

{
"Status": 400,
"IsSuccess": false,
"Result": null,
"Errors": "The Username field is required. \r\n En az sekiz karakterli bir kullanıcı adı giriniz."
}

Başarılıysa Dönen Yanıt

{
"status": 200,
"isSuccess": true,
"result": "fdc56d1b-1ee8-43ce-b7d8-46ba36bee668",
"errors": ""
}
 #