) ASP.net Web API comprehensive example

Source: Internet
Author: User
Tags md5 encryption visual studio 2010

Address: http://www.cnblogs.com/guogangj/archive/2013/05/30/3107965.html

 

Directory
  1. Overview
  2. Features
  3. Program Structure
  4. Server Introduction
  5. Client Introduction
  6. "Contract"
  7. Web API design rules
  8. Parallel write conflict and timestamp
  9. Authentication details
  10. Web API verification rules
  11. Introduction to client MVVM
  12. Web. Config
  13. Some Problems in this DEMO
  14. Related downloads
Overview

Some blogs I wrote about the ASP.net Web API have received replies from some friends. I have always wanted to sort out the code and paste it for your reference, however, it was not easy to strip a part of the code from the entire project. I forgot it in a twinkling of an eye. Recently I made up my mind to get it done, as a result, although this DEMO is not perfect, it already includes all the web api-related technologies I have mastered. What improvements do I need, I will point out one by one at the end of the article that the Web API server does not have an interface, so it is not easy to demonstrate, so I also provide a client written in WPF, which is a quick start:

Features

The following describes the related technologies or functions to be demonstrated by this program:

Server:

  • Complete and well-structured code (at least I think so) ASP.net Web API Server
  • Use DataAnnotations for Model Verification
  • Custom Model Verification
  • Layered (separated by the UI Layer and business logic layer)
  • Excellent logging methods
  • Authentication with a high security factor (well, I am writing this to use the radical method to lead a master to pick a problem for me)
  • Sensitive Information Encryption
  • Pure Web API code (excluding css/js and unnecessary view engines)

For more information about identity authentication, see how to implement RESTful Web API authentication.

Client:

  • Self-designed simple MVVM framework
  • A lot of WPF Practical Skills
  • Use DataAnnotations for client Model verification
  • Complete HttpClient example

Others:

  • Automatic Object ing (using AutoMapper)
  • Runs independently and has no configuration. You can use Visual Studio 2010 to open it immediately.
  • I have tried my best to reduce repeated code ...... (In fact, it is not enough)
  • Upload and download of binary files
Program Structure

The main purpose of this program is to make a demo with complete documentation, comprehensive functions, and zero configuration, so it does not involve the use of DBMS, although the real use of DBMS is almost necessary, but this time I used XML to replace DBMS.

The program is divided into two parts: one is the server side, the other is the client side, and is split into two different solution, so that it is done to facilitate debugging. But the problem is that there will be repeated things, such as the three libraries: CommLib, WebApiKit, and WebApiContract. They are public libraries, but they exist in different solutions, respectively, in my actual work, I used SVN tools to prevent them from "changing this and forgetting that, this time, I used a tool I wrote a long time ago to "Synchronize" them ":

This tool will also be available for download later in this article.

Server Introduction

File structure on the server

BLL-Business logic layer
UserInfo_BLL.cs-it is the user information class. The suffix "BLL" indicates that it belongs to the business logic layer. I am used to distinguishing models of different layers.
UserManager. cs-Main class in the business logic layer, which provides various methods for adding, deleting, querying, modifying, and deleting
CommLib-Public libraries, including DES encryption, MD5, log, regular expressions, and global constants ......
Server-Main project of ASP.net Web API
AutoMapperConfig. cs-configuration class for Automatic Object ing. For example, you can convert UserInfo_BLL directly to UserInfo_API_Get without assigning values by attribute.
WebApiConfig. cs-ASP.net Web API routing configuration class
AvatarsController. cs-getting, modifying, and deleting an avatar
EntranceController. cs-log on to and obtain your own information
PasswordController. cs-change your password
UsersInfoController. cs-obtain individual user information, obtain user lists, modify user information, add users (not implemented), and delete users (not implemented)
ModelValidationFilter. cs-Global Model authentication filter for all requests
WebApiAuthFilter. cs-authenticator for most controllers (which do not rule out that authentication is not required in some places)
WebApiExceptionFilter. cs-global exception Processor
WebApiRoleFilter. cs-role permission filters for certain actions. For example, some actions can only be performed by administrators.
GuidSet. cs-Guid set help class used to prevent repeated attacks
WebApiPrincipal. cs-Logon user identity class
GlobalServerData. cs-contains a static GuidSet
Managers. cs-contains a static UserManager
WebApiContract-This database is used to "negotiate" with the client.
WebApiKit-Client/Server tools

Client Introduction

Project Structure of the client:

Client-Main project of the Client
PasswordHelper. cs-The help class of the password control, used to bind the Password text of the password control to the View Model. WPF comes from a security requirement and is not supported by default.
UIVisibleConverter. cs-Converters Used on some WPF interfaces, used to control the display and hiding of interface elements based on some properties of the View Model
ChangePassword_VM.cs-View Model used to change the password interface. The suffix "VM" indicates the View Model.
Login_VM.cs-View Model used in the logon interface.
UserInfo_VM.cs-View Model used to display/modify user information on the main interface.
ViewModelBase. cs-the base class of all View models. INotifyPropertyChanged interface, IDataErrorInfo interface, and some help methods are implemented.

"Contract"

The term Contract actually comes from Web Service, but Web Service is a very important technology. I personally don't like it. In fact, simply put, the contract is: how to use Web APIs? The contract should include: What is the call address, what is the method, what is the content, and what is the verification. Take UserInfo_API_Put as an example:

Public class UserInfo_API_Base {[Required (ErrorMessage = Verifier. ERRMSG_CANNOT_BE_NULL)] [RegularExpression (Verifier. REG_EXP_CHINESE_NAME, ErrorMessage = Verifier. ERRMSG_REG_EXP_CHINESE_NAME)] public string RealName {get; set ;}// real name public float Height {get; set ;}// Height public DateTime Birthday {get; set ;} // birthday} // modify user information (normal users can only modify their own information) // PUT api/usersinfo/{username} public class UserInfo_API_Put: UserInfo_API_Base {[EnuValueValidator (RoleType. ADMINISTARTOR, RoleType. NORMAL)] public string Role {get; set;} // Role Administrator, Normal, this field cannot be modified by common users}

To modify the information of the user "guogangj", put such an object to the uri address "api/usersinfo/guogangj". The attribute "RealName" must not be empty, it must also contain 2-10 Chinese characters. Of course, Height and Birthday cannot be empty, because float and DateTime types cannot be empty, the Role attribute performs a custom verification to ensure that the value must be "Administrator" or "Normal ".

Such a contract must be understood by both the server and the client, so it is made into a class library. Both the server and client reference this class library, the biggest problem with this is that when the class library changes, I forget the other side when updating it. Currently, I use some tools to avoid this situation as much as possible, for example, set the Externals parameter of SVN. In this regard, what are the better methods for senior citizens? I hope to share it with you.

Web API design rules

Although in the "Understanding and Design of RESTful Web APIs", I have mentioned the "Rules" of Web APIs. Here I will try again and add a few additional words.

The core content of RESTFul is "R", which is the resource. we add, DELETE, query, and modify the resource into four HTTP actions: POST, DELETE, GET, and PUT. Now there is such a problem: If my user name is guogangj, I want to GET my information, which is "GET/api/myinfo, or "GET/api/usersinfo/guogangj? Technically speaking, this is fine. Now the key is to consider from the perspective of "Resources". If you think "/api/myinfo" is a resource, this means that each user will GET different results for this resource. for resources such as "/api/usersinfo/guogangj", no matter who, the obtained content should be consistent (if you have the permission to obtain it). From this perspective, "/api/usersinfo/guogangj" is more RESTFul, which is my understanding, it is not necessarily correct. Please analyze it with experts.

Parallel write conflict and timestamp

When you perform PUT and DELETE operations on resources, you need to perform parallel write conflict check on them, because the resources may have been moved by others at the time of writing, this check is usually implemented using a "timestamp". I use a DateTime Ticks, which is a long type and is sufficient to reflect the time when the resource has changed. For example, I want to modify the guogangj information:

PUT http: // localhost.: 57955/api/usersinfo/guogangj? UpdateTicks = 635054404507843749
{"Role": "Administrator", "RealName": "Jiang guogang", "Height": 1.67, "Birthday": "1981-11-12T00: 00: 00 "}

You may have noticed that there seems to be an additional "." behind localhost, which is added to enable Fiddler to capture this http packet.

I will include the UpdateTicks parameter in the URI. When the server-side business logic layer executes the Update, it will determine whether the timestamp is consistent with the timestamp in the current database. If the timestamp is inconsistent, the system throws an exception of parallel write conflicts.

The reason why I put UpdateTicks In the URI is: This UpdateTicks can also be part of a resource. For example, for the PUT action above, my intention is to update the resource "/api/usersinfo/guogangj" whose timestamp is "635054404507843749, if the timestamp is not "635054404507843749", it is not the resource I want to update.

This is my method. Another method I can come up with is to put the timestamp in the HTTP header, such:

PUT http: // localhost.: 57955/api/usersinfo/guogangj
UpdateTicks: 635054404507843749
{"Role": "Administrator", "RealName": "Jiang guogang", "Height": 1.67, "Birthday": "1981-11-12T00: 00: 00 "}

In this way, the server can retrieve the timestamp during processing, but the method is slightly different. What is better? Personally, I prefer the former. Please advise me here.

Authentication details

Well, it's finally the most important thing, that is, Web API authentication. In order to give everyone a direct understanding, I use Fiddler to take a packet, let's see what I send each request?

PUT http: // localhost.: 57955/api/usersinfo/guogangj? UpdateTicks = 635054404507843749 HTTP/1.1
Custom-Auth-Name: guogangj
Custom-Auth-Key: Authorization
Accept: application/json
Content-Type: application/json; charset = UTF-8
Host: localhost.: 57955
Content-Length: 94
Secondary CT: 100-continue

{"Role": "Administrator", "RealName": "Jiang guogang", "Height": 1.67, "Birthday": "1981-11-12T00: 00: 00 "}

This is a complete HTTP request. There are two more items in the HTTP header: "M m-Auth-Name" and "Custom-Auth-Key". Needless to say, Custom-Auth-Name, the User ID indicates who the initiator is. But if he says that he or she is a server and thinks that he or she is the initiator, there is no security at all, therefore, we need to verify the Custom-Auth-Key (hereinafter referred to as the Key). This Key is a long string, which is the encrypted and transcoded text, let's talk about how this Key came from.

In the WebApiKit library, there is such a method: WebApiClientHelper. MakePrincipleHeader. There are not many codes in it. I will explain them one by one:

Private static void MakePrincipleHeader (HttpRequestMessage reqMsg, string strUri) {// even for identical request content, I want to generate different keys, so each time a new GUID is generated, this GUID is used to prevent "re-sending". This GUID is used to ensure that each request (regardless of whether the URI and content are the same) is unique. Duplicate Guid = guid cannot be copied. newGuid (); // obtain a valid URI. For example, the URI of this long string of requests obtains the content "/api/usersinfo/guogangj" strUri = InternalHelper. getinclutiveuri (strUri); // valid URI is connected to GUID for MD5 encryption (this method is used to obtain consistent length but completely different content each time) and then connected to GUID, this result is the symmetric encryption plaintext string strToEncrypt = Md5.MD5Encode (strUri + guid) + "" + guid; // The plaintext password is used as the symmetric encryption key after the MD5 is executed twice, encrypt the string "plaintext" generated before encryption. Well, the Key generates string strTheAuthKey = Des. encode (strToEncrypt, Md5.MD5TwiceEncode (Password); // Add the result to the Header of the HTTP request to reqMsg. headers. add (Consts. HTTP_HEADER_AUTH_USER, UserName); reqMsg. headers. add (Consts. HTTP_HEADER_AUTH_KEY, strTheAuthKey );}

Symmetric encryption cannot be restored without a key, and the key is not transmitted over the network. It cannot be tracked by a third party through packet capture or other methods. Therefore, this ciphertext cannot be cracked. After the server obtains the request package, it performs a reverse operation:

Public static bool VerifyAuthKey (string strAuthUser, string strAuthKey, string strRequestUri, string strPwdMd5TwiceSvr, ref Guid guidRequest) {try {// symmetric encryption decryption. The key is the second MD5 of the user's password, string strUrlAndGuid = Des. decode (strAuthKey, strPwdMd5TwiceSvr); // If the decryption is successful, split the space into two segments. The segment is "valid URI connected to GUID for MD5 encryption ", another section is the GUID string [] arrUrlAndGuid = strUrlAndGuid. split (new [] {''}); if (arrUrlAndGuid. count ()! = 2) return false; string strUrl = arrUrlAndGuid [0]; string strGuid = arrUrlAndGuid [1]; // use the decrypted GUID as the return parameter, so that it can be added to a global set to prevent "resend" ("resend" will check in another place) guidRequest = Guid. parse (strGuid); // generate a "valid URI connected to GUID for MD5 encryption" result in the same way as the client, and compare the result with the decrypted result, if they are consistent, the authentication passes strRequestUri = InternalHelper. getinclutiveuri (strRequestUri); if (string. compare (Md5.MD5Encode (strRequestUri + guidRequest), strUrl, true) = 0) {return true ;}} catch (Exception) // ignore any exceptions caused by this, think of it as Verification Failed {// Ignore any exception} return false ;}

This verification method can eliminate "Identity impersonating" and "resending", and completely does not rely on third-party libraries. The method is very simple and developers can easily further strengthen it, I think it is enough for most occasions. Okay, wait for the senior to correct it.

Web API verification rules

Verification is always a key feature of the application. As mentioned above, authentication is actually a kind of authentication. The purpose of verification is to ensure that the right person does the right thing.

Some verification is just a simple rule, such as Chinese name verification: it cannot be blank, it must be 2-10 Chinese characters; some verification requires access to the database, for example: Add a user, it cannot be the same as the existing user ID. There are also some comprehensive verifications, which are also reflected in this example: users can modify their own information, but only Administrators can modify others' information.

Is verification on the UI Layer or the business logic layer? In fact, this is not just a problem with Web APIs. All systems should consider this issue during design. In the past, when I was working on a system, I thought that the layer and layer were mutually untrusted. Therefore, the business logic layer should undergo a complete set of verification, the UI Layer should also carry out a complete set of verification. The consequence of this is the increase in repeated code, which looks a bit messy. Later I thought about it as follows: if the UI Layer of the website has verified the information provided by the user, why does the business logic layer need to be executed again? This should not be necessary because both the UI Layer and the business logic layer are placed on the server side, which can be controlled by ourselves. We only need to verify the data from the client side, as a result, I removed the verification code at the business logic layer, and the program looks neat.

* Note: In this DEMO, the Server site belongs to the UI Layer, while the BLL class library belongs to the business logic layer.

However, some data-related verification is not so easy to do on the UI Layer, for example, "adding a user cannot be the same as the existing user ID ", in this case, you need to check whether the user ID exists in the database.

Therefore, in general, my rules are as follows: identity authentication, input authentication, and permission judgment can be placed on the UI Layer rather than the UI Layer (for example, verification involving specific data ), in the business logic layer. It is best not to repeat the UI Layer verification and business logic layer verification.

Introduction to client MVVM

This article focuses on Web APIs, but also briefly describes the MVVM Model of the client. MVVM is called "Model-View-ViewModel", and ViewModel is bound to the View. Binding here means: when the View changes, the ViewModel should be reflected. Otherwise, the View should also be reflected when the ViewModel changes. This is probably the case. What two-way binding and one-way binding are required.

The View changes and the ViewModel changes. This does not seem difficult. For example, if you enter "zhangsan" in the UserName text box, when your input focus leaves the text box, the program will generate an event, which will process the event and assign the value of the text box to ViewModel. This "Event" may not necessarily lose focus, but may also be typed, it may also be triggered manually.

The ViewModel changes, and the View also changes. How can this be implemented? WPF provides an interface INotifyPropertyChanged, which has only one event called "PropertyChanged". When the ViewModel changes, this event is triggered to notify the View change. I provided a base class named "ViewModelBase" in the client code to implement this interface. All my other viewmodels are derived from this base class, when SetValue is set for their attributes, the event in this interface is triggered to implement the View notification.

There are still many articles on MVVM on the Internet, and there are also some important frameworks, such as Prism. To master these things, it will not be overnight, but I believe that everything will remain unchanged, the principle is as I said.

In addition, I will not mention some of the WPF technologies here. After all, this is not the focus of this article. You can refer to some other materials.

Web. Config

This may be the simplest Web. Config you have ever seen, because I have removed all the unnecessary ones.

<?xml version="1.0" encoding="utf-8"?><!--  For more information on how to configure your ASP.NET application, please visithttp://go.microsoft.com/fwlink/?LinkId=169433  --><configuration>  <system.web>    <compilation debug="true" targetFramework="4.0" />    <authentication mode="None" />  </system.web></configuration>

Yes, the above content is all. the only thing worth mentioning is the mode attribute of authentication. we have to set this to None because we use a custom authentication method, otherwise, the server may use the Windows authentication mechanism. In addition, this program has been verified under IIS, and it is normal to use.

Some Problems in this DEMO

After all, the DEMO is a DEMO. I also found some problems during the writing process. Some of them are due to restrictions on the conditions, and some are true problems. Therefore, you must list them, so that you can avoid this during official development:

  • Is it better to use the UTC time instead of the local time for the timestamp? (I don't need to use it in this DEMO if I use UTC time for a little more conversion)
  • No transactions are used. The transaction function is usually a DBMS function. This DEMO does not use DBMS. In addition, a good system can perform file rollback, not just DBMS, but this far exceeds the scope of this DEMO.
  • The POST and DELETE functions of usersinfo are not implemented (lazy)
  • The client's network communication will block the UI thread, and the user experience is poor. Improvement reference
  • The verification of ViewModel on the client is the same as that of Contract on the API. How can I eliminate this repetition?
  • Authentication requires calling the business logic layer without caching on the UI Layer. In formal large-scale applications, the efficiency will be very low if no caching is available, however, cache updates are also a big problem. I believe that large network applications have a set of rigorous and complex rules in this regard.
  • The password is saved in the second MD5 format on the server, which is not conducive to the improvement of the encryption algorithm in the future.
  • The input verification on the client is not good enough. For example, if you enter "abc" in your age, an error message is prompted (conversion to a number fails ), but you can also submit it (the submitted content is the previous number)
  • Because some global data on the server is static, thread security issues may exist.
Related downloads
  • Server code
  • Client code
  • GornixSync synchronization Tool
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.