public ActionResult <IEnumerable <SongbookResult> > Post([FromBody] IEnumerable <SongbookCreate> songbookCollection) { if (songbookCollection == null) { return(BadRequest()); } _logger.LogDebug("SongbookCollectionController.Post called to add a new collection of songbooks {@songbookCollection}", songbookCollection); var songbooks = _mapper.Map <IEnumerable <SongbookDto> >(songbookCollection).Select(Songbook.From).ToList(); //Perform validation foreach (var songbook in songbooks) { if (!songbook.IsValid) { songbook.Validate().AddToModelState(ModelState, $"({songbook.Title})"); _logger.LogWarning("{method} failed model validation (ModelState: {@modelState}), returning Unprocessable Entity", nameof(Post), ModelState.Values.SelectMany(v => v.Errors)); } } if (!ModelState.IsValid) { return(InvalidModelStateResponseFactory.GenerateResponseForInvalidModelState(ModelState, HttpContext)); } foreach (var songbook in songbooks) { _repository.Save(songbook); } var idsAsString = string.Join(",", songbooks.Select(sb => sb.Id)); return(CreatedAtRoute("GetSongbookCollection", new { ids = idsAsString }, _mapper.Map <IEnumerable <SongbookResult> >(songbooks).Select(CreateLinksForSongbook))); }
public ActionResult <SongResult> Post(Guid songbookId, [FromBody] SongCreate song) { if (song == null) { return(BadRequest()); } _logger.LogDebug("SongController.Post called to create new song for songbook {@songbookId}: {@songbook}", songbookId, song); var songbook = _repository.GetById(songbookId); if (songbook == null) { _logger.LogWarning("Songbook.Post failed to add song {@song} to songbook {@songbookId}. Songbook was not found.", song, songbookId); return(NotFound()); } var dto = _mapper.Map <SongDto>(song); var newSong = Song.From(dto); //Perform validation if (!newSong.IsValid) { newSong.Validate().AddToModelState(ModelState, null); _logger.LogWarning("{method} failed model validation (ModelState: {@modelState}), returning Unprocessable Entity", nameof(Post), ModelState.Values.SelectMany(v => v.Errors)); return(InvalidModelStateResponseFactory.GenerateResponseForInvalidModelState(ModelState, HttpContext)); } songbook.Songs.Add(newSong); _repository.Save(songbook); return(CreatedAtRoute("GetSong", new { songbookId = newSong.SongbookId, id = newSong.Id }, newSong)); }
public ActionResult <SongbookResult> Post([FromBody] SongbookCreate songbook) { if (songbook == null) { return(BadRequest()); } _logger.LogDebug("SongbookController.Post called to create new songbook: {@songbook}", songbook); var dto = _mapper.Map <SongbookDto>(songbook); var newSongbook = Songbook.From(dto); //Perform validation if (!newSongbook.IsValid) { newSongbook.Validate().AddToModelState(ModelState, null); _logger.LogWarning("{method} failed model validation (ModelState: {@modelState}), returning Unprocessable Entity", nameof(Post), ModelState.Values.SelectMany(v => v.Errors)); return(InvalidModelStateResponseFactory.GenerateResponseForInvalidModelState(ModelState, HttpContext)); } _repository.Save(newSongbook); return(CreatedAtRoute("GetSongbook", new { id = newSongbook.Id }, newSongbook)); }
public override BadRequestObjectResult BadRequest(ModelStateDictionary modelState) => InvalidModelStateResponseFactory.CreateFrom("InvalidModel", modelState);
public void ConfigureServices(IServiceCollection services) { services.AddHttpCacheHeaders((expirationModelOptions) => { expirationModelOptions.MaxAge = 600; expirationModelOptions.CacheLocation = Marvin.Cache.Headers.CacheLocation.Private; }, (validationModelOptions) => { validationModelOptions.MustRevalidate = true; }); services.AddControllers() .ConfigureApiBehaviorOptions(setupAction => { setupAction.InvalidModelStateResponseFactory = context => { return(InvalidModelStateResponseFactory.GenerateResponseForInvalidModelState(context.ModelState, context.HttpContext)); }; }); services.AddMvc(setupAction => { //Return 406 'Not Acceptable' for any unsupported media types setupAction.ReturnHttpNotAcceptable = true; //Create default response types for all controllers setupAction.Filters.Add(new ProducesResponseTypeAttribute(StatusCodes.Status400BadRequest)); setupAction.Filters.Add(new ProducesResponseTypeAttribute(StatusCodes.Status406NotAcceptable)); setupAction.Filters.Add(new ProducesResponseTypeAttribute(StatusCodes.Status500InternalServerError)); }).AddFluentValidation(setupAction => { setupAction.RunDefaultMvcValidationAfterFluentValidationExecutes = true; }); services.AddSingleton <ISongDao, SongDao>(); services.AddSingleton <ISongbookDao, SongbookDao>(); services.AddSingleton <ICreatorDao, CreatorDao>(); services.AddSingleton <ISongbookRepository, SongbookRepository>(); services.AddSingleton <IActionContextAccessor, ActionContextAccessor>(); services.AddTransient <IPropertyMappingService, PropertyMappingService>(); services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>(); services.AddSingleton <IRateLimitConfiguration, RateLimitConfiguration>(); services.AddSingleton <IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>(); services.AddSingleton <IIpPolicyStore, MemoryCacheIpPolicyStore>(); var mappingConfig = new MapperConfiguration(mc => { mc.AddProfile(new SongbookProfile()); mc.AddProfile(new CreatorProfile()); mc.AddProfile(new SongProfile()); mc.AddProfile(new ParametersProfile()); }); IMapper mapper = mappingConfig.CreateMapper(); services.AddSingleton(mapper); services.AddSwaggerGen(setupAction => { setupAction.SwaggerDoc("HymnstagramOpenAPISpecification", new Microsoft.OpenApi.Models.OpenApiInfo() { Title = "Library API", Description = "Create, read, and delete songbooks and songs your congregation uses.", Contact = new Microsoft.OpenApi.Models.OpenApiContact() { Email = "*****@*****.**", Name = "Hymnstagram Development", Url = new Uri("https://www.hymnstagram.com") }, License = new Microsoft.OpenApi.Models.OpenApiLicense() { Name = "MIT License", Url = new Uri("https://opensource.org/licenses/MIT") }, //TermsOfService = new Uri(""), Version = "1" }); var xmlCommentsFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlCommentsFullPath = Path.Combine(AppContext.BaseDirectory, xmlCommentsFile); setupAction.IncludeXmlComments(xmlCommentsFullPath); }); services.AddMemoryCache(); services.Configure <IpRateLimitOptions>((options) => { options.GeneralRules = new List <RateLimitRule>() { new RateLimitRule() { Endpoint = "*", Limit = 50, Period = "5m" } }; }); }