App Zoo-Blog Web
APP ASP NET.CORE MVC Web usando identidade e boostrap EF6 MSSQL
Página de catálogo principal, onde você pode rolar e escolher o Animel para explorar e comentar

Sobre
Este aplicativo Web ASP.NETCORE demonstra o padrão MVC com uma visualização de layout contém barra e corpo de renderização com vistas e controladores diferentes. Incluí um componente de visualização para manter os Animels explorando as divs mais comuns entre as páginas e o estilo usando Boostrap Libary
Modelo (Entidade Framwork Core)
Diagrama MSSQL

Meu modelo contém 3 objetos: categoria, animal e comentário. Eu dei a cada um deles várias propostas e atributos de validação de ajuste, incluindo padrões de regex, erros de mensagens personalizados do tipo de dados etc. Eu criei dois atributos personalizados de vlidação:
- Data de nascimento para validar o animal é inferior a 150 anos e nasceu no dia atual ou anterior
- Validador de arquivo para verificar se o tipo de conteúdo do arquivo incluir a palavra "imagem" e o tamanho do arquivo limitado a 10 MB
| classe pública ImageFileValidationAttribute : ValidationAttribute |
| { |
| const int max_file_size = 10 * 1024 * 1024 ; // 10MB |
| substituição protegida validationResult ? IsValid ( objeto ? Valor , ValidationContext ValidationContext ) |
| { |
| se ( valor é iformFile arquivo ! = padrão ) |
| { |
| if ( arquivo . Comprimento > max_file_size ) |
| Retornar novo ValidationResult ( "O tamanho deste arquivo é maior que a limitação de 10 MB" ) ; |
| if ( file . contentType . contém ( "imagem" )) ) |
| Retorno ValidationResult . Sucesso ; |
| retornar novo validationResult ( "Este não é um arquivo válido" ) ; |
| } |
| Retornar novo ValidationResult ( "Digite um arquivo de imagem válido" ) ; |
| } |
| } |
Para gerar as categorias, fiz um modelo de auxílio enum que não é mapeado para o banco de dados, mas uso para gerar uma tag de seleção apropriada
O projeto do modelo também contém a classe Repsoitório de base genérica da camada de acesso a dados para cada entidade que o ID é do tipo GUID e um serviço de formação de imagem que me ajudam a salvar os arquivos de imagens como matriz de bytes e gerar a imagem de volta no lado do cliente
| Byte estático público [ ] FormFileTobyTearray ( FormFile FormFile ) |
| { |
| if ( formFile ! = nulo ) |
| { |
| MemoryStream MemoryStream = new MemoryStream ( ) ; |
| FormFile . OpenReadstream ( ) . Copyto ( MemoryStream ) ; |
| byte [ ] RAWDATA = MemoryStream . ToArray ( ) ; |
| Retornar RawData ; |
| } |
| retornar padrão ; |
| } |
| public static string formatrawdatatoimage ( byte [ ] imagensfiledata ) |
| { |
| if ( imagensfiledata ! = null ) |
| Retorne "Data: Image; Base64", + converta . Tobase64String ( Imagesfiledata ) ; |
| retornar padrão ; |
| } |
| |
| } |
Visualizar
Eu criei várias visualizações para os controladores, um componente de exibição e 3 vistas parciais úteis para estilos e scripts de layout e barra de navegação, a barra de navegação é usada para navegar entre as vistas e ações diferentes
O bar de navegação do aplicativo

A visualização do gerente de criação e atualização contém uma validação de Vannila JS do tipo de arquivo e tamanho de TI para impedir que o navegador faça um erro
| FileInput . addEventListener ( "alteração" , function ( ) { |
| Deixe fileSize = isso . arquivos [ 0 ] . tamanho ; |
| if ( this . arquivos [ 0 ] === indefinido || fileSize === indefinido ) { |
| esse . setCustomvalidity ( "Por favor, digite o arquivo" ) ; |
| esse . relationVality ( ) ; |
| retornar ; |
| } |
| if ( fileSize > maxfilesize ) { |
| esse . setCustomvalidity ( "Este arquivo é maior que 10 MB" ) ; |
| esse . value = "" ; |
| esse . relationVality ( ) ; |
| retornar ; |
| } |
| if ( ! ValidFileType ( this . Arquivos [ 0 ] ) ) { |
| esse . setCustomvalidity ( "Este não é um arquivo de imagem" ) ; |
| esse . value = "" ; |
| esse . relationVality ( ) ; |
| retornar ; |
| } |
| if ( fileSize < maxfilesize ) { |
| esse . setCustomvalidity ( "" ) ; |
| esse . relationVality ( ) ; |
| } |
| } ) ; |
Controladores
Este projeto contém 4 controladores:
- Home - exibindo os dois animais mais comentados
- Gerente - Lidando com a operação CRUD nos dados dos animais
- Catálogo - Veja os animais no blog e pode classificá -los por categoria
- Dados da Animel - Explore os detalhes dos animais e permita que o usuário deixe um comentário. A postagem de comentários usa a API buscar o Fetch para impedir que a página seja relatada cada vez que o usuário postar um comentário.
| ASYNC FUNCTCOMMENT ( Evento ) { |
| Deixe comentário = { |
| Comentário : indefinido , |
| Conteúdo : ContentTextarea . valor , |
| Animelid : id , |
| } |
| Comentário = JSON . stringify ( comentário ) ; |
| aguarda busca ( ` $ { baseurl } /index` , { |
| Método : 'Post' , |
| corpo : comentar , |
| Cabeçalhos : { |
| "Tipo de Conteúdo" : "Aplicativo/JSON" |
| } |
| } ) . então ( ( ) => { getAllComments ( ) ; } ) ; |
| } |
Olá comentário do mundo

Authentication && Authorization (identidade)
Usei a identidade Nuget e o contexto separado para autenticar e autorizar os usuários em meu aplicativo da web e registrar e fazer login em manuseio por modelo ajudantes denominados LoginModel e SignUpModel no aplicativo Existem 3 tipos de usuários "admin", "usuário" adn anonymous. A função do gerente pode usar o controlador do gerente e possui link de navegação para a criação e atualização. Todo usuário assinado pode comentar os animais no aplicativo (incluindo gerentes). O usuário anônimo só pode percorrer a página do catálogo do Animels ou registrar/fazer login.
Ação de registro:
| [ Httppost ] |
| [ ValidateAntiforgergyToken ] |
| Tarefa ASYNC Public <ictionResult> Registro ( Usuário SignUpmodel ) |
| { |
| if ( ModelState . IsValid ) |
| { |
| IdentityUser idiota = novo IdentityUser |
| { |
| Nome de usuário = usuário . Nome de usuário , |
| PhoneNumber = Usuário . PhoneNumber , |
| Email = usuário . E-mail |
| } ; |
| var Createresult = Aguarda _userManager . Createasync ( idiota , usuário . Senha ) ; |
| var addRole = aguardar _userManager . Addtoroleasync ( idiota , "usuário" ) ; |
| se ( Createresult . conseguiu ) |
| { |
| var signUPRESULT = Aguarda _SigninManager . SenhaSigninSync ( Usuário . Nome de usuário , Usuário . Senha , False , False ) ; |
| se ( signupresult . conseguiu ) |
| { |
| Retornar RedirectToAction ( "Index" , "Home" ) ; |
| } |
| retornar login ( ) ; |
| } |
| } |
| return view ( ) ; |
| } |
Teste de unidade
Esta solução de aplicativo da web inclui um projeto XUnit de teste para a verificação e validação da camada de repositório da classe ReposiroeyBase para os métodos Sync e Async.
Exemplo de teste:
| [ Teste , requesthread ] |
| Public Void FindByidasync ( ) |
| { |
| _categoryRepository ? . Criar ( categorytest ! ) ; |
| _animelRepository ? . Criar ( AnimelTest ! ) ; |
| _CommentRepository ? . Criar ( comentário ! ) ; |
| |
| Tarefa <INALALM> Animelfound = _animelRepository ! . Encontrebyidasync ( AnimelTest !. Id ) ; |
| Animelfound . Continue com ( _ = > { assert . Esse ( resultado . |
| Tarefa <INALALM> AnimelNotfound = _animelrepository . Encontrebyidasync ( do_not_insret_animel !. Id ) ; |
| Animelnotfound . Continue com ( _ => { assert . Que ( resultado . Resultado , é . Nulo ) ; } ) ; |
| Tarefa <EMGENTE> CommentFound = _CommentRepository ! . Encontrebyidasync ( comentário !. CommentId ) ; |
| comentário . Continue com ( _ => { assert . Isso ( CommentFound . Resultado , é . Equalto ( comentário ) ) ; } ) ; |
| Tarefa <Categoria> categoryfound = _categoryRepository ! . FindByidasync ( categoryTest !. CategoryId ) ; |
| categoryfound . Continue em ( _ => { assert . Que ( categoryfound . Resultado , é . Igualto ( categorytest ) ) ; } ) ; |
| } |